/**
 * Copyright (c) 2015 QFish <im@qfi.sh>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include "katana.h"
#include "selector.h"
#include "katana.tab.h"
#include "parser.h"
#include "tokenizer.h"

#include "foundation.h"

//#undef	assert
//#define assert(x)

#define breakpoint
#define KATANA_PARSER_STRING(literal) { literal, sizeof(literal) - 1 }


typedef void (*KatanaArrayDeallocator)(KatanaParser* parser, void* e);

#undef  katana_destroy_array
#define katana_destroy_array(parser, callback, e) \
        katana_destroy_array_using_deallocator((parser), \
                    (KatanaArrayDeallocator)(&(callback)), (e))


void katana_destroy_stylesheet(KatanaParser* parser, KatanaStylesheet* e);
void katana_destroy_rule(KatanaParser* parser, KatanaRule* e);
void katana_destroy_font_face_rule(KatanaParser* parser, KatanaFontFaceRule* e);
void katana_destroy_import_rule(KatanaParser* parser, KatanaImportRule* e);
void katana_destroy_media_rule(KatanaParser* parser, KatanaMediaRule* e);
void katana_destroy_media_query(KatanaParser* parser, KatanaMediaQuery* e);
void katana_destroy_media_query_exp(KatanaParser* parser, KatanaMediaQueryExp* e);
void katana_destroy_keyframes_rule(KatanaParser* parser, KatanaKeyframesRule* e);
void katana_destroy_keyframe(KatanaParser* parser, KatanaKeyframe* e);
void katana_destroy_media_list(KatanaParser* parser, KatanaArray* e);
void katana_destroy_rule_list(KatanaParser* parser, KatanaArray* e);
void katana_destroy_style_rule(KatanaParser* parser, KatanaStyleRule* e);
void katana_destroy_qualified_name(KatanaParser* parser, KatanaQualifiedName* e);
void katana_destroy_selector(KatanaParser* parser, KatanaSelector* e);
void katana_destroy_rare_data(KatanaParser* parser, KatanaSelectorRareData* e);
void katana_destroy_declaration(KatanaParser* parser, KatanaDeclaration* e);
void katana_destroy_value(KatanaParser* parser, KatanaValue* e);
void katana_destroy_function(KatanaParser* parser, KatanaValueFunction* e);

void katana_destroy_array_using_deallocator(KatanaParser* parser,
                          KatanaArrayDeallocator deallocator, KatanaArray* array);



extern int katanaparse(void* scanner, struct KatanaInternalParser * parser);

static KatanaOutput* katana_parse_with_options(const KatanaOptions* options,
                                               yyconst char* bytes,
                                               size_t len,
                                               KatanaParserMode mode);

static KatanaOutput* katana_parse_fragment(const char* prefix,
                                           size_t pre_len,
                                           const char* string,
                                           size_t str_len,
                                           KatanaParserMode mode);

static const char* katana_stringify_value_list(KatanaParser* parser, KatanaArray* value_list);
static const char* katana_stringify_value(KatanaParser* parser, KatanaValue* value);

static void* malloc_wrapper(void* unused, size_t size) {
    return malloc(size);
}

static void free_wrapper(void* unused, void* ptr) {
    free(ptr);
}

const KatanaOptions kKatanaDefaultOptions = {
    &malloc_wrapper,
    &free_wrapper,
    NULL
};

static void output_init(KatanaParser* parser, KatanaParserMode mode)
{
    KatanaOutput* output = katana_parser_allocate(parser, sizeof(KatanaOutput));
    output->stylesheet = katana_new_stylesheet(parser);
    output->mode = mode;
    katana_array_init(parser, 0, &output->errors);
    parser->output = output;
}

void katana_destroy_output(KatanaOutput* output)
{
    if ( NULL == output )
        return;

    KatanaParser parser;
    parser.options = &kKatanaDefaultOptions;
    switch (output->mode) {
        case KatanaParserModeStylesheet:
            break;
        case KatanaParserModeRule:
            if ( NULL != output->rule ) {
                katana_destroy_rule(&parser, output->rule);
            }
            break;
        case KatanaParserModeKeyframeRule:
            if ( NULL != output->keyframe ) {
                katana_destroy_keyframe(&parser, output->keyframe);
            }
            break;
        case KatanaParserModeKeyframeKeyList:
            if ( NULL != output->keyframe_keys ) {
                katana_destroy_array(&parser, katana_destroy_value, output->keyframe_keys);
                katana_parser_deallocate(&parser, (void*) output->keyframe_keys);
                output->keyframe_keys = NULL;
            }
            break;
        case KatanaParserModeMediaList:
            if ( NULL != output->medias ) {
                katana_destroy_media_list(&parser, output->medias);
            }
            break;
        case KatanaParserModeValue:
            if ( NULL != output->values ) {
                katana_destroy_array(&parser, katana_destroy_value, output->values);
                katana_parser_deallocate(&parser, (void*) output->values);
                output->values = NULL;
            }
            break;
        case KatanaParserModeSelector:
            if ( NULL != output->selectors ) {
                katana_destroy_array(&parser, katana_destroy_selector, output->selectors);
                katana_parser_deallocate(&parser, (void*) output->selectors);
                output->selectors = NULL;
            }
            break;
        case KatanaParserModeDeclarationList:
            if ( NULL != output->declarations ) {
                katana_destroy_array(&parser, katana_destroy_declaration, output->declarations);
                katana_parser_deallocate(&parser, (void*) output->declarations);
                output->declarations = NULL;
            }
            break;
    }
    katana_destroy_stylesheet(&parser, output->stylesheet);
    katana_array_destroy(&parser, &output->errors);
    katana_parser_deallocate(&parser, output);
}


static const KatanaParserString kKatanaParserModePrefixs[] = {
    KATANA_PARSER_STRING(""),
    KATANA_PARSER_STRING("@-internal-rule "), // 16
    KATANA_PARSER_STRING("@-internal-keyframe-rule "), // 25
    KATANA_PARSER_STRING("@-internal-keyframe-key-list "), // 29
    KATANA_PARSER_STRING("@-internal-media-list "), // 22
    KATANA_PARSER_STRING("@-internal-value "), // 17
    KATANA_PARSER_STRING("@-internal-selector "), // 20
    KATANA_PARSER_STRING("@-internal-decls "), // 17
};

KatanaOutput* katana_parse(const char* str, size_t len, KatanaParserMode mode)
{
    switch (mode) {
        case KatanaParserModeStylesheet:
            return katana_parse_with_options(&kKatanaDefaultOptions, (yyconst char*)str, len, mode);
        case KatanaParserModeRule:
        case KatanaParserModeKeyframeRule:
        case KatanaParserModeKeyframeKeyList:
        case KatanaParserModeMediaList:
        case KatanaParserModeValue:
        case KatanaParserModeSelector:
        case KatanaParserModeDeclarationList: {
            KatanaParserString prefix = kKatanaParserModePrefixs[mode];
            return katana_parse_fragment(prefix.data, prefix.length, str, len, mode);
        }
        default:
            katana_print("Whoops, not support yet!");
            return NULL;
    }
}

KatanaOutput* katana_parse_in(FILE* fp)
{
    assert(NULL != fp);
    if ( NULL == fp )
        return NULL;
    
    yyscan_t scanner;
    if (katanalex_init(&scanner)) {
        katana_print("no scanning today!");
        return NULL;
    }
    
    katanaset_in(fp, scanner);
    
    KatanaParser parser;
    parser.options = &kKatanaDefaultOptions;
    parser.scanner = &scanner;
    parser.default_namespace = kKatanaAsteriskString;
    parser.parsed_declarations = katana_new_array(&parser);
#if KATANA_PARSER_DEBUG
    parser.parsed_selectors = katana_new_array(&parser);
#endif // #if KATANA_PARSER_DEBUG
    parser.position = katana_parser_allocate(&parser, sizeof(KatanaSourcePosition));
    output_init(&parser, KatanaParserModeStylesheet);
    katanaparse(scanner, &parser);
    katanalex_destroy(scanner);
    katana_parser_clear_declarations(&parser);
    katana_parser_deallocate(&parser, parser.position);
#if KATANA_PARSER_DEBUG
    katana_destroy_array(&parser, katana_destroy_selector, parser.parsed_selectors);
    katana_parser_deallocate(&parser, parser.parsed_selectors);
#endif // #if KATANA_PARSER_DEBUG
    parser.scanner = NULL;
    KatanaOutput* output = parser.output;
    return output;
}


static KatanaOutput* katana_parse_fragment(const char* prefix,
                                           size_t pre_len,
                                           const char* str,
                                           size_t str_len,
                                           KatanaParserMode mode) {
    size_t len = pre_len + str_len + 1;
    char * source = malloc_wrapper(NULL, len);
    if ( source == NULL )
        return NULL;
    memcpy(source, prefix, pre_len);
    memcpy(source+pre_len, str, str_len);
    source[pre_len + str_len] = '\0';
    KatanaOutput * output = katana_parse_with_options(&kKatanaDefaultOptions, (void*)source, len, mode);
    free_wrapper(NULL, source);
    return output;
}

static KatanaOutput* katana_parse_with_options(const KatanaOptions* options,
                                               yyconst char* bytes,
                                               size_t len,
                                               KatanaParserMode mode) {
    assert(NULL != bytes);
    if ( NULL == bytes )
        return NULL;
    
    yyscan_t scanner;
    if (katanalex_init(&scanner)) {
        katana_print("no scanning today!");
        return NULL;
    }
        
    katana_scan_bytes(bytes, len, scanner);
    
    KatanaParser parser;
    parser.options = options;
    parser.scanner = &scanner;
    parser.default_namespace = kKatanaAsteriskString;
    parser.parsed_declarations = katana_new_array(&parser);
#if KATANA_PARSER_DEBUG
    parser.parsed_selectors = katana_new_array(&parser);
#endif // #if KATANA_PARSER_DEBUG
    parser.position = katana_parser_allocate(&parser, sizeof(KatanaSourcePosition));
    output_init(&parser, mode);
    katanaparse(scanner, &parser);
    katanalex_destroy(scanner);
    if ( KatanaParserModeDeclarationList != mode ) {
        katana_parser_clear_declarations(&parser);
    }
    katana_parser_deallocate(&parser, parser.position);
#if KATANA_PARSER_DEBUG
    katana_destroy_array(&parser, katana_destroy_selector, parser.parsed_selectors);
    katana_parser_deallocate(&parser, parser.parsed_selectors);
#endif // #if KATANA_PARSER_DEBUG
    parser.scanner = NULL;
    KatanaOutput* output = parser.output;
    return output;
}


void katana_parse_internal_rule(KatanaParser* parser, KatanaRule* e)
{
    parser->output->rule = e;
}

void katana_parse_internal_keyframe_rule(KatanaParser* parser, KatanaKeyframe* e)
{
    parser->output->keyframe = e;
}

void katana_parse_internal_keyframe_key_list(KatanaParser* parser, KatanaArray* e)
{
    parser->output->keyframe_keys = e;
}

void katana_parse_internal_value(KatanaParser* parser, KatanaArray* e)
{
    parser->output->values = e;
}

void katana_parse_internal_media_list(KatanaParser* parser, KatanaArray* e)
{
    parser->output->medias = e;
}

void katana_parse_internal_declaration_list(KatanaParser* parser, bool e)
{
    parser->output->declarations = parser->parsed_declarations;
}

void katana_parse_internal_selector(KatanaParser* parser, KatanaArray* e)
{
    parser->output->selectors = e;
}


KatanaArray* katana_new_array(KatanaParser* parser) {
    KatanaArray* array = katana_parser_allocate(parser, sizeof(KatanaArray));
    katana_array_init(parser, 0, array);
    return array;
}

void katana_destroy_array_using_deallocator(KatanaParser* parser,
                          KatanaArrayDeallocator callback, KatanaArray* array) {
    //assert(NULL != array);
    if ( NULL == array )
        return;
    for (size_t i = 0; i < array->length; ++i) {
        callback(parser, array->data[i]);
    }
    katana_array_destroy(parser, array);
}


KatanaStylesheet* katana_new_stylesheet(KatanaParser* parser) {
    KatanaStylesheet* stylesheet =
        katana_parser_allocate(parser, sizeof(KatanaStylesheet));
    stylesheet->encoding = NULL;
    katana_array_init(parser, 0, &stylesheet->rules);
    katana_array_init(parser, 0, &stylesheet->imports);
    return stylesheet;
}

void katana_destroy_stylesheet(KatanaParser* parser, KatanaStylesheet* e)
{
    assert(NULL != e);
    if ( NULL == e )
        return;
    
    // free encoding
    if ( e->encoding )
        katana_parser_deallocate(parser, (void*) e->encoding);

    // free imports
    for (size_t i = 0; i < e->imports.length; ++i) {
        katana_destroy_import_rule(parser, e->imports.data[i]);
    }
    katana_parser_deallocate(parser, (void*) e->imports.data);

    // free rules
    for (size_t i = 0; i < e->rules.length; ++i) {
        katana_destroy_rule(parser, e->rules.data[i]);
    }
    katana_parser_deallocate(parser, (void*) e->rules.data);

    // free e
    katana_parser_deallocate(parser, (void*) e);
}

void katana_destroy_rule(KatanaParser* parser, KatanaRule* rule)
{
    switch (rule->type) {
        case KatanaRuleStyle:
            katana_destroy_style_rule(parser, (KatanaStyleRule*)rule);
            break;
        case KatanaRuleImport:
            katana_destroy_import_rule(parser, (KatanaImportRule*)rule);
            break;
        case KatanaRuleFontFace:
            katana_destroy_font_face_rule(parser, (KatanaFontFaceRule*)rule);
            break;
        case KatanaRuleKeyframes:
            katana_destroy_keyframes_rule(parser, (KatanaKeyframesRule*)rule);
            break;
        case KatanaRuleMedia:
            katana_destroy_media_rule(parser, (KatanaMediaRule*)rule);
            break;
            
        default:
            break;
    }
}

void katana_destroy_rule_list(KatanaParser* parser, KatanaArray* rules)
{
    katana_destroy_array(parser, katana_destroy_rule, rules);
    katana_parser_deallocate(parser, (void*) rules);
}


KatanaRule* katana_new_style_rule(KatanaParser* parser, KatanaArray* selectors)
{
    assert(NULL != selectors);
    if ( NULL == selectors )
        return NULL;
    
    KatanaStyleRule* rule = katana_parser_allocate(parser, sizeof(KatanaStyleRule));
    rule->base.name = "style";
    rule->base.type = KatanaRuleStyle;
    rule->selectors = selectors;
    // Do not check parser->parsed_declarations, when we encounter something like `selectors {}`, treat it as valid.
    rule->declarations = parser->parsed_declarations;
    katana_parser_reset_declarations(parser);
    
    return (KatanaRule*)rule;
}

void katana_destroy_style_rule(KatanaParser* parser, KatanaStyleRule* e)
{
    assert(e->selectors->length);

    katana_destroy_array(parser, katana_destroy_selector, e->selectors);
    katana_parser_deallocate(parser, (void*) e->selectors);

    katana_destroy_array(parser, katana_destroy_declaration, e->declarations);
    katana_parser_deallocate(parser, (void*) e->declarations);
    
    // katana_parser_deallocate(parser, (void*) e->base.name);
    katana_parser_deallocate(parser, (void*) e);
}


void katana_add_namespace(KatanaParser* parser, KatanaParserString* prefix, KatanaParserString* uri)
{
    // TODO: No need for right now
}


KatanaRule* katana_new_font_face(KatanaParser* parser)
{
    KatanaFontFaceRule* rule = katana_parser_allocate(parser, sizeof(KatanaFontFaceRule));
    rule->base.name = "font-face";
    rule->base.type = KatanaRuleFontFace;
    rule->declarations = parser->parsed_declarations;

    katana_parser_reset_declarations(parser);
    
    return (KatanaRule*)rule;
}

void katana_destroy_font_face_rule(KatanaParser* parser, KatanaFontFaceRule* e)
{
    katana_destroy_array(parser, katana_destroy_declaration, e->declarations);
    katana_parser_deallocate(parser, (void*) e->declarations);
    // katana_parser_deallocate(parser, (void*) e->base.name);
    katana_parser_deallocate(parser, (void*) e);
}


KatanaRule* katana_new_keyframes_rule(KatanaParser* parser, KatanaParserString* name, KatanaArray* keyframes, bool isPrefixed)
{
    KatanaKeyframesRule * rule = katana_parser_allocate(parser, sizeof(KatanaKeyframesRule));
    rule->base.name = "keyframes";
    rule->base.type = KatanaRuleKeyframes;
    rule->name = katana_string_to_characters(parser, name);
    rule->keyframes = keyframes;
    return (KatanaRule*)rule;
}

void katana_destroy_keyframes_rule(KatanaParser* parser, KatanaKeyframesRule * e)
{
    katana_parser_clear_keyframes(parser, e->keyframes);
    katana_parser_deallocate(parser, (void*) e->name);
    katana_parser_deallocate(parser, (void*) e);
}

KatanaKeyframe* katana_new_keyframe(KatanaParser* parser, KatanaArray* selectors)
{
    KatanaKeyframe* keyframe = katana_parser_allocate(parser, sizeof(KatanaKeyframe));
    keyframe->selectors = selectors;
    keyframe->declarations = parser->parsed_declarations;
    katana_parser_reset_declarations(parser);
    return keyframe;
}

void katana_destroy_keyframe(KatanaParser* parser, KatanaKeyframe* e)
{
    katana_destroy_array(parser, katana_destroy_value, e->selectors);
    katana_parser_deallocate(parser, (void*) e->selectors);
    
    katana_destroy_array(parser, katana_destroy_declaration, e->declarations);
    katana_parser_deallocate(parser, (void*) e->declarations);

    katana_parser_deallocate(parser, (void*) e);
}

KatanaArray* katana_new_Keyframe_list(KatanaParser* parser)
{
    return katana_new_array(parser);
}

void katana_keyframe_rule_list_add(KatanaParser* parser, KatanaKeyframe* keyframe, KatanaArray* list)
{
    assert(keyframe);
    katana_array_add(parser, keyframe, list);
}

void katana_parser_clear_keyframes(KatanaParser* parser, KatanaArray* keyframes)
{
    katana_destroy_array(parser, katana_destroy_keyframe, keyframes);
    katana_parser_deallocate(parser, (void*) keyframes);
}


void katana_set_charset(KatanaParser* parser, KatanaParserString* charset)
{
//    parser->output->stylesheet->encoding = katana_string_to_characters(parser, charset);
}


KatanaRule* katana_new_import_rule(KatanaParser* parser, KatanaParserString* href, KatanaArray* media)
{
    KatanaImportRule* rule = katana_parser_allocate(parser, sizeof(KatanaImportRule));
    rule->base.name = "import";
    rule->base.type = KatanaRuleImport;
    rule->href = katana_string_to_characters(parser, href);
    rule->medias = media;
    return (KatanaRule*)rule;
}

void katana_destroy_import_rule(KatanaParser* parser, KatanaImportRule* e)
{
    katana_destroy_array(parser, katana_destroy_media_query, e->medias);
    katana_parser_deallocate(parser, (void*) e->medias);
    // katana_parser_deallocate(parser, (void*) e->base.name);
    katana_parser_deallocate(parser, (void*) e->href);
    katana_parser_deallocate(parser, (void*) e);
}


KatanaValue* katana_new_value(KatanaParser* parser)
{
    return katana_parser_allocate(parser, sizeof(KatanaValue));
}

void katana_destroy_value(KatanaParser* parser, KatanaValue* e)
{
    switch (e->unit) {
        case KATANA_VALUE_URI:
        case KATANA_VALUE_IDENT:
        case KATANA_VALUE_STRING:
        case KATANA_VALUE_DIMENSION:
        case KATANA_VALUE_UNICODE_RANGE:
        case KATANA_VALUE_PARSER_HEXCOLOR:
        {
            katana_parser_deallocate(parser, (void*) e->string);
        }
            break;
        case KATANA_VALUE_PARSER_LIST:
        {
            katana_destroy_array(parser, katana_destroy_value, e->list);
            katana_parser_deallocate(parser, (void*) e->list);
        }
            break;
        case KATANA_VALUE_PARSER_FUNCTION:
        {
            katana_destroy_function(parser, e->function);
        }
            break;
        case KATANA_VALUE_NUMBER:
        case KATANA_VALUE_PERCENTAGE:
        case KATANA_VALUE_PX:
        case KATANA_VALUE_CM:
        case KATANA_VALUE_MM:
        case KATANA_VALUE_IN:
        case KATANA_VALUE_PT:
        case KATANA_VALUE_PC:
        case KATANA_VALUE_DEG:
        case KATANA_VALUE_RAD:
        case KATANA_VALUE_GRAD:
        case KATANA_VALUE_TURN:
        case KATANA_VALUE_MS:
        case KATANA_VALUE_S:
        case KATANA_VALUE_HZ:
        case KATANA_VALUE_KHZ:
        case KATANA_VALUE_EMS:
        case KATANA_VALUE_PARSER_Q_EMS:
        case KATANA_VALUE_EXS:
        case KATANA_VALUE_REMS:
        case KATANA_VALUE_CHS:
        case KATANA_VALUE_VW:
        case KATANA_VALUE_VH:
        case KATANA_VALUE_VMIN:
        case KATANA_VALUE_VMAX:
        case KATANA_VALUE_DPPX:
        case KATANA_VALUE_DPI:
        case KATANA_VALUE_DPCM:
        case KATANA_VALUE_FR:
            katana_parser_deallocate(parser, (void*) e->raw);
            break;
        default:
            break;
    }
    
    katana_parser_deallocate(parser, (void*) e);
}

KatanaValueFunction* katana_new_function(KatanaParser* parser, KatanaParserString* name, KatanaArray* args)
{
    KatanaValueFunction* func = katana_parser_allocate(parser, sizeof(KatanaValueFunction));
    func->name = katana_string_to_characters(parser, name);
    func->args = args;
    return func;
}

void katana_destroy_function(KatanaParser* parser, KatanaValueFunction* e)
{
    katana_destroy_array(parser, katana_destroy_value, e->args);
    katana_parser_deallocate(parser, (void*) e->args);
    katana_parser_deallocate(parser, (void*) e->name);
    katana_parser_deallocate(parser, (void*) e);
}

KatanaValue* katana_new_number_value(KatanaParser* parser, int sign, KatanaParserNumber* value, KatanaValueUnit unit)
{
    KatanaValue* v = katana_new_value(parser);
    v->id = KatanaValueInvalid;
    v->isInt = false;
    v->fValue = sign * value->val;
    v->unit = unit;
    if ( 1 == sign ) {
        v->raw = katana_string_to_characters(parser, &value->raw);
    } else {
        v->raw = katana_string_to_characters_with_prefix_char(parser, &value->raw, '-');
    }
    return v;
}

KatanaValue* katana_new_dimension_value(KatanaParser* parser, KatanaParserNumber* value, KatanaValueUnit unit)
{
    KatanaValue* v = katana_new_value(parser);
    v->id = KatanaValueInvalid;
    v->isInt = false;
    v->fValue = value->val;
    v->raw = katana_string_to_characters(parser, &value->raw);
    v->unit = unit;
    return v;
}

KatanaValue* katana_new_operator_value(KatanaParser* parser, int value)
{
    KatanaValue* v = katana_new_value(parser);
    v->id = KatanaValueInvalid;
    v->isInt = false;
    v->unit = KATANA_VALUE_PARSER_OPERATOR;
    v->iValue = value;
    return v;
}

KatanaValue* katana_new_ident_value(KatanaParser* parser, KatanaParserString* value)
{
    KatanaValue* v = katana_new_value(parser);
    // is it necessary to do this ?
    // v.id = cssValueKeywordID(string);
    v->id = KatanaValueCustom;
    v->isInt = false;
    v->unit = KATANA_VALUE_IDENT;
    v->string = katana_string_to_characters(parser, value);
    return v;
}

KatanaValue* katana_new_function_value(KatanaParser* parser, KatanaParserString* name, KatanaArray* args)
{
    KatanaValueFunction* func = katana_new_function(parser, name, args);
    KatanaValue* value = katana_new_value(parser);
    value->unit = KATANA_VALUE_PARSER_FUNCTION;
    value->function = func;
    return value;
}

KatanaValue* katana_new_list_value(KatanaParser* parser, KatanaArray* list)
{
    KatanaValue* value = katana_new_value(parser);
    value->unit = KATANA_VALUE_PARSER_LIST;
    value->list = list;
    return value;
}

void katana_value_set_string(KatanaParser* parser, KatanaValue* value, KatanaParserString* string)
{
    value->string = katana_string_to_characters(parser, string);
}

void katana_value_set_sign(KatanaParser* parser, KatanaValue* value, int sign)
{
    value->fValue *= sign;
    
    if ( sign < 0 ) {
        const char* raw = value->raw;
        size_t len = strlen(raw);
        char* new_str = katana_parser_allocate(parser, sizeof(char) * (len + 2));
        strcpy(new_str + 1, raw);
        new_str[0] = '-';
        new_str[len + 1] = '\0';
        value->raw = new_str;
        katana_parser_deallocate(parser, (void*) raw);
    }
}


KatanaArray* katana_new_value_list(KatanaParser* parser)
{
    return katana_new_array(parser);
}

void katana_value_list_insert(KatanaParser* parser, KatanaValue* value, int index, KatanaArray* list)
{
    assert(NULL != value);
    if ( value == NULL)
        return;
    katana_array_insert_at(parser, value, index, list);
}

void katana_value_list_add(KatanaParser* parser, KatanaValue* value, KatanaArray* list)
{
    assert(NULL != value);
    if ( value == NULL)
        return;
    katana_array_add(parser, value, list);
}

void katana_value_list_steal_values(KatanaParser* parser, KatanaArray* values, KatanaArray* list)
{
    assert(NULL != values && values->length);
    if ( values == NULL || 0 == values->length )
        return;
    for (size_t i = 0; i < values->length; ++i)
        katana_value_list_add(parser, values->data[i], list);
    katana_parser_deallocate(parser, (void*) values);
}


bool katana_new_declaration(KatanaParser* parser, KatanaParserString* name, bool important, KatanaArray* values)
{
    KatanaDeclaration * decl = katana_parser_allocate(parser, sizeof(KatanaDeclaration));
    decl->property = katana_string_to_characters(parser, name);
    decl->important = important;
    decl->values = values;
    decl->raw = katana_stringify_value_list(parser, values);
    katana_array_add(parser, decl, parser->parsed_declarations);
    
    return true;
}

void katana_destroy_declaration(KatanaParser* parser, KatanaDeclaration* e)
{
    katana_destroy_array(parser, katana_destroy_value, e->values);
    katana_parser_deallocate(parser, (void*) e->values);
    katana_parser_deallocate(parser, (void*) e->raw);
    katana_parser_deallocate(parser, (void*) e->property);
    katana_parser_deallocate(parser, (void*) e);
}

void katana_parser_clear_declarations(KatanaParser* parser)
{
    katana_destroy_array(parser, katana_destroy_declaration, parser->parsed_declarations);
    katana_parser_deallocate(parser, (void*) parser->parsed_declarations);
    parser->parsed_declarations = NULL;
}

void katana_parser_reset_declarations(KatanaParser* parser)
{
    parser->parsed_declarations = katana_new_array(parser);
}


KatanaRule* katana_new_media_rule(KatanaParser* parser, KatanaArray* medias, KatanaArray* rules)
{
//	assert(NULL != medias && NULL != rules);
    
    if ( medias == NULL || rules == NULL )
        return NULL;
    
    KatanaMediaRule* rule = katana_parser_allocate(parser, sizeof(KatanaMediaRule));
    rule->base.name = "media";
    rule->base.type = KatanaRuleMedia;
    rule->medias = medias;
    rule->rules = rules;
    return (KatanaRule*)rule;
}

void katana_destroy_media_rule(KatanaParser* parser, KatanaMediaRule* e)
{
    katana_destroy_media_list(parser, (void*) e->medias);
    katana_destroy_rule_list(parser,  (void*) e->rules),
    // katana_parser_deallocate(parser,  (void*) e->base.name);
    katana_parser_deallocate(parser,  (void*) e);
}


KatanaArray* katana_new_media_list(KatanaParser* parser)
{
    return katana_new_array(parser);
}

void katana_media_list_add(KatanaParser* parser, KatanaMediaQuery* media_query, KatanaArray* medias)
{
    // debug here
//    katana_print_media_query(parser, media_query);
    if ( NULL != media_query ) {
        katana_array_add(parser, media_query, medias);
    }
}

void katana_destroy_media_list(KatanaParser* parser, KatanaArray* medias)
{
    katana_destroy_array(parser, katana_destroy_media_query, medias);
    katana_parser_deallocate(parser, (void*) medias);
}


KatanaMediaQuery* katana_new_media_query(KatanaParser* parser, KatanaMediaQueryRestrictor r, KatanaParserString *type, KatanaArray* exps)
{
    KatanaMediaQuery* media_query = katana_parser_allocate(parser, sizeof(KatanaMediaQuery));
    media_query->restrictor = r;
    media_query->type = type == NULL ? NULL : katana_string_to_characters(parser, type);
    media_query->expressions = exps;
    return media_query;
}

void katana_destroy_media_query(KatanaParser* parser, KatanaMediaQuery* e)
{
    katana_destroy_array(parser, katana_destroy_media_query_exp, e->expressions);
    katana_parser_deallocate(parser, (void*) e->expressions);
    if ( NULL != e->type ) {
        katana_parser_deallocate(parser, (void*) e->type);
    }
    katana_parser_deallocate(parser, (void*) e);
}


KatanaMediaQueryExp * katana_new_media_query_exp(KatanaParser* parser, KatanaParserString* feature, KatanaArray* values)
{
    assert( NULL != feature );
    if ( NULL == feature )
        return NULL;
    
    KatanaMediaQueryExp* exp = katana_parser_allocate(parser, sizeof(KatanaMediaQueryExp));
    exp->feature = katana_string_to_characters(parser, feature);
    exp->values = values;
    exp->raw = katana_stringify_value_list(parser, values);
    return exp;
}

void katana_destroy_media_query_exp(KatanaParser* parser, KatanaMediaQueryExp* e)
{
    if ( NULL != e->values ) {
        katana_destroy_array(parser, katana_destroy_value, e->values);
        katana_parser_deallocate(parser, e->values);
    }
    katana_parser_deallocate(parser, (void*) e->raw);
    katana_parser_deallocate(parser, (void*) e->feature);
    katana_parser_deallocate(parser, (void*) e);
}


void katana_media_query_exp_list_add(KatanaParser* parser, KatanaMediaQueryExp* exp, KatanaArray* list)
{
    assert(NULL != exp);
    if ( NULL == exp )
        return;
    katana_array_add(parser, exp, list);
}

KatanaArray* katana_new_media_query_exp_list(KatanaParser* parser)
{
    return katana_new_array(parser);
}


KatanaArray* katana_new_rule_list(KatanaParser* parser)
{
    return katana_new_array(parser);
}

KatanaArray* katana_rule_list_add(KatanaParser* parser, KatanaRule* rule, KatanaArray* rule_list)
{
    if ( NULL != rule ) {
        if ( NULL == rule_list )
            rule_list = katana_new_rule_list(parser);
        katana_array_add(parser, rule, rule_list);
    }
    
    return rule_list;
}


void katana_start_declaration(KatanaParser* parser)
{
    katana_parser_log(parser, "katana_start_declaration");   
}

void katana_end_declaration(KatanaParser* parser, bool flag, bool ended)
{
    katana_parser_log(parser, "katana_end_declaration");
}

void katana_set_current_declaration(KatanaParser* parser, KatanaParserString* tag)
{
    katana_parser_log(parser, "katana_set_current_declaration");
}


void katana_start_selector(KatanaParser* parser)
{
    katana_parser_log(parser, "katana_start_selector");
}

void katana_end_selector(KatanaParser* parser)
{
    katana_parser_log(parser, "katana_end_selector");
}

KatanaQualifiedName * katana_new_qualified_name(KatanaParser* parser, KatanaParserString* prefix, KatanaParserString* local, KatanaParserString* uri)
{
    KatanaQualifiedName* name = katana_parser_allocate(parser, sizeof(KatanaQualifiedName));
    name->prefix = prefix == NULL ? NULL : katana_string_to_characters(parser, prefix);
    name->local = local == NULL ? NULL : katana_string_to_characters(parser, local);
    name->uri = uri == NULL ? NULL : katana_string_to_characters(parser, uri);
    return name;
}

void katana_destroy_qualified_name(KatanaParser* parser,  KatanaQualifiedName* e)
{
    katana_parser_deallocate(parser, (void*) e->local);
    katana_parser_deallocate(parser, (void*) e->prefix);
    katana_parser_deallocate(parser, (void*) e->uri);
    katana_parser_deallocate(parser, (void*) e);
}

KatanaSelectorRareData* katana_new_rare_data(KatanaParser* parser)
{
    KatanaSelectorRareData* data = katana_parser_allocate(parser, sizeof(KatanaSelectorRareData));
    data->value = NULL;
    data->attribute = NULL;
    data->argument = NULL;
    data->selectors = NULL;
    return data;
}

void katana_destroy_rare_data(KatanaParser* parser, KatanaSelectorRareData* e)
{
    if ( NULL != e->value )
        katana_parser_deallocate(parser, (void*) e->value);
    
    if ( NULL != e->argument )
        katana_parser_deallocate(parser, (void*) e->argument);
    
    if ( NULL != e->attribute )
        katana_destroy_qualified_name(parser, e->attribute);

    if ( NULL != e->selectors ) {
        katana_destroy_array(parser, katana_destroy_selector, e->selectors);
        katana_parser_deallocate(parser, (void*) e->selectors);
    }
    
    katana_parser_deallocate(parser, e);
}

KatanaSelector* katana_new_selector(KatanaParser* parser)
{
    KatanaSelector* selector = katana_parser_allocate(parser, sizeof(KatanaSelector));
    selector->data = katana_new_rare_data(parser);
    selector->tag = NULL;
    selector->match = 0;
    selector->pseudo = KatanaPseudoNotParsed;
    selector->relation = 0;
    selector->specificity = 0;
    selector->tag = NULL;
    selector->tagHistory = NULL;
#if KATANA_PARSER_DEBUG
    katana_array_add(parser, selector, parser->parsed_selectors);
#endif // #if KATANA_PARSER_DEBUG
    return selector;
}

KatanaSelector* katana_sink_floating_selector(KatanaParser* parser, KatanaSelector* selector)
{
#if KATANA_PARSER_DEBUG
    katana_array_remove(parser, selector, parser->parsed_selectors);
#endif // #if KATANA_PARSER_DEBUG
    return selector;
}

void katana_destroy_one_selector(KatanaParser* parser, KatanaSelector* e)
{
    katana_destroy_rare_data(parser, e->data);
    
    if ( e->tag  != NULL )
        katana_destroy_qualified_name(parser, e->tag);
    
    katana_parser_deallocate(parser, e);
}

void katana_destroy_selector(KatanaParser* parser, KatanaSelector* e)
{
    KatanaSelector *p = e, *q;
    while ( p ) {
        q = p->tagHistory;
        katana_destroy_one_selector(parser, p);
        p = q;
    }
}

KatanaSelector* katana_rewrite_specifier_with_element_name(KatanaParser* parser, KatanaParserString* tag, KatanaSelector* specifier)
{
    // TODO: (@QFish) check if css3 support
    bool supported = true;
    
    if ( supported ) {
        KatanaSelector* prepend = katana_new_selector(parser);
        prepend->tag = katana_new_qualified_name(parser, NULL, tag, &parser->default_namespace);
        prepend->match = KatanaSelectorMatchTag;
        prepend->tagHistory = katana_sink_floating_selector(parser, specifier);
        prepend->relation = KatanaSelectorRelationSubSelector;
        return prepend;
    }
    
    return specifier;
}

KatanaSelector* katana_rewrite_specifier_with_namespace_if_needed(KatanaParser* parser, KatanaSelector* specifiers)
{
    // TODO: @(QFish) add logic
    return specifiers;
}

KatanaSelector* katana_rewrite_specifiers(KatanaParser* parser, KatanaSelector* specifiers, KatanaSelector* newSpecifier)
{
    if (katana_selector_crosses_tree_scopes(newSpecifier)) {
        // Unknown pseudo element always goes at the top of selector chain.
        katana_selector_append(parser, newSpecifier, katana_sink_floating_selector(parser, specifiers), KatanaSelectorRelationShadowPseudo);
        return newSpecifier;
    }
    if (katana_selector_is_content_pseudo_element(newSpecifier)) {
        katana_selector_append(parser, newSpecifier, katana_sink_floating_selector(parser, specifiers), KatanaSelectorRelationSubSelector);
        return newSpecifier;
    }
    if (katana_selector_crosses_tree_scopes(specifiers)) {
        // Specifiers for unknown pseudo element go right behind it in the chain.
        katana_selector_insert(parser, specifiers, katana_sink_floating_selector(parser, newSpecifier), KatanaSelectorRelationSubSelector, KatanaSelectorRelationShadowPseudo);
        return specifiers;
    }
    if (katana_selector_is_content_pseudo_element(specifiers)) {
        katana_selector_insert(parser, specifiers, katana_sink_floating_selector(parser, newSpecifier), KatanaSelectorRelationSubSelector, KatanaSelectorRelationSubSelector);
        return specifiers;
    }

    katana_selector_append(parser, specifiers, katana_sink_floating_selector(parser, newSpecifier), KatanaSelectorRelationSubSelector);
    return specifiers;
}

void katana_adopt_selector_list(KatanaParser* parser, KatanaArray* selectors, KatanaSelector* selector)
{
    katana_parser_log(parser, "katana_adopt_selector_list");
    selector->data->selectors = selectors;
}

void katana_selector_append(KatanaParser* parser, KatanaSelector* selector, KatanaSelector* new_selector, KatanaSelectorRelation relation)
{
    katana_parser_log(parser, "katana_selector_append");
    KatanaSelector* end = selector;
    while (NULL != end->tagHistory)
        end = end->tagHistory;
    end->relation = relation;
    end->tagHistory = new_selector;
}

void katana_selector_insert(KatanaParser* parser, KatanaSelector* selector, KatanaSelector* new_selector, KatanaSelectorRelation before, KatanaSelectorRelation after)
{
    katana_parser_log(parser, "katana_selector_insert");

    if (selector->tagHistory)
        new_selector->tagHistory = selector;
    selector->relation = before;
    new_selector->relation = after;
    selector->tagHistory = selector;
}

void katana_selector_prepend_with_element_name(KatanaParser* parser, KatanaSelector* selector, KatanaParserString* tag)
{
    katana_parser_log(parser, "katana_selector_prepend_with_element_name");

    KatanaSelector* prev = katana_new_selector(parser);
    prev->tag = katana_new_qualified_name(parser, NULL, tag, &parser->default_namespace);
    selector->tagHistory = prev;
    selector->relation = KatanaSelectorRelationSubSelector;
}

KatanaArray* katana_new_selector_list(KatanaParser* parser)
{
    return katana_new_array(parser);
}

KatanaArray* katana_reusable_selector_list(KatanaParser* parser)
{
    return katana_new_array(parser);
}

void katana_selector_list_shink(KatanaParser* parser, int capacity, KatanaArray* list)
{

}

void katana_selector_list_add(KatanaParser* parser, KatanaSelector* selector, KatanaArray* list)
{
    assert(NULL != selector);
    if ( NULL == selector )
        return;
        
    katana_array_add(parser, selector, list);
}

void katana_selector_set_value(KatanaParser* parser, KatanaSelector* selector, KatanaParserString* value)
{
    selector->data->value = katana_string_to_characters(parser, value);
}

void katana_selector_set_argument_with_number(KatanaParser* parser, KatanaSelector* selector, int sign, KatanaParserNumber* value)
{
    if ( 1 == sign ) {
        selector->data->argument = katana_string_to_characters(parser, &value->raw);
    } else {
        selector->data->argument = katana_string_to_characters_with_prefix_char(parser, &value->raw, '-');
    }
}

void katana_selector_set_argument(KatanaParser* parser, KatanaSelector* selector, KatanaParserString* argument)
{
    selector->data->argument = katana_string_to_characters(parser, argument);
}

bool katana_parse_attribute_match_type(KatanaParser* parser, KatanaAttributeMatchType type, KatanaParserString* attr)
{
    return true;
}

bool katana_selector_is_simple(KatanaParser* parser, KatanaSelector* selector)
{
    if (NULL != selector->data->selectors)
        return false;
    
    if (NULL == selector->tagHistory)
        return true;
    // TODO: @(QFish) check more.
    return false;
}


void katana_add_rule(KatanaParser* parser, KatanaRule* rule)
{
    assert( NULL != rule );
    if ( NULL == rule )
        return;
    
    switch ( rule->type ) {
        case KatanaRuleImport:
            katana_array_add(parser, rule, &parser->output->stylesheet->imports);
            break;
        default:
            katana_array_add(parser, rule, &parser->output->stylesheet->rules);
            break;
    }
}

void katana_start_rule(KatanaParser* parser)
{
    katana_parser_log(parser, "katana_start_rule");
}

void katana_end_rule(KatanaParser* parser, bool ended)
{
    katana_parser_log(parser, "katana_end_rule");
}

void katana_start_rule_header(KatanaParser* parser, KatanaRuleType type)
{
    katana_parser_log(parser, "katana_start_rule_header");
}

void katana_end_rule_header(KatanaParser* parser)
{
    katana_parser_log(parser, "katana_end_rule_header");
}

void katana_end_invalid_rule_header(KatanaParser* parser)
{
    katana_parser_log(parser, "katana_end_invalid_rule_header");
}

void katana_start_rule_body(KatanaParser* parser)
{
    katana_parser_log(parser, "katana_start_rule_body");
}


bool katana_string_is_function(KatanaParserString* string)
{
    return string && (string->length > 0) && (string->data[string->length - 1] == '(');
}

void katana_string_clear(KatanaParser* parser, KatanaParserString* string)
{
	printf("==%s==\n", string->data);
    katana_parser_deallocate(parser, (void*) string->data);
    katana_parser_deallocate(parser, (void*) string);
}


void katanaerror(YYLTYPE* yyloc, void* scanner, struct KatanaInternalParser * parser, char* error)
{
#ifdef KATANA_PARSER_DEBUG
#if KATANA_PARSER_DEBUG
    katana_print("[Error] %d.%d - %d.%d: %s at %s\n",
           yyloc->first_line,
           yyloc->first_column,
           yyloc->last_line,
           yyloc->last_column,
           error,
           katanaget_text(parser->scanner));

    YYSTYPE * s = katanaget_lval(parser->scanner);

//	struct yy_buffer_state state = katana_get_previous_state(parser->scanner);
//    s, (*yy_buffer_stack[0]).yy_ch_buf);
//    
//    katana_print("%s", s->);
#endif // #if KATANA_PARSER_DEBUG
#endif // #ifdef KATANA_PARSER_DEBUG

    KatanaError *e = (KatanaError *)malloc(sizeof(KatanaError));
    e->type = KatanaParseError;
    e->first_line = yyloc->first_line;
    e->first_column = yyloc->first_column;
    e->last_line = yyloc->last_line;
    e->last_column = yyloc->last_column;
    snprintf(e->message, KATANA_ERROR_MESSAGE_SIZE, "%s at %s", error,
             katanaget_text(parser->scanner));
    katana_array_add(parser, e, &(parser->output->errors));
}

void katana_parser_log(KatanaParser* parser, const char * format, ...)
{
#ifdef KATANA_PARSER_LOG_ENABLE
#if KATANA_PARSER_LOG_ENABLE
    va_list args;
    va_start(args, format);
    printf(" -> ");
    vprintf(format, args);
    printf("\n");
    va_end(args);
    fflush(stdout);
#endif // #if KATANA_PARSER_LOG_ENABLE
#endif // #ifdef KATANA_PARSER_LOG_ENABLE
}

void katana_parser_resume_error_logging()
{
    
}

void katana_parser_report_error(KatanaParser* parser, KatanaSourcePosition* pos, const char* format, ...)
{
#ifdef KATANA_PARSER_DEBUG
#if KATANA_PARSER_DEBUG
    printf("[ERROR] %d.%d - %d.%d : ", pos->line, pos->column, katanaget_lineno(*parser->scanner), katanaget_column(*parser->scanner) );
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
    printf("\n");
    fflush(stdout);
#endif // #if KATANA_PARSER_DEBUG
#endif // #ifdef KATANA_PARSER_DEBUG
}


void katana_print_position(YYLTYPE* yyloc)
{
    katana_print(NULL,
                 "Loaction %d.%d - %d.%d",
                 yyloc->first_line,
                 yyloc->first_column,
                 yyloc->last_line,
                 yyloc->last_column
                 );
}

KatanaSourcePosition* katana_parser_current_location(KatanaParser* parser, YYLTYPE* yylloc)
{
    parser->position->line = katanaget_lineno(*parser->scanner);
    parser->position->column = katanaget_column(*parser->scanner);
    //    katana_print_position(yylloc);
    return parser->position;
}


void katana_print(const char * format, ...)
{
    va_list args;
    va_start(args, format);
    vprintf(format, args);
	printf("\n");
    va_end(args);
    fflush(stdout);
}

void katana_print_stylesheet(KatanaParser* parser, KatanaStylesheet* sheet)
{
    katana_print("stylesheet with ");
    katana_print("%d rules.\n", sheet->rules.length);
    for (size_t i = 0; i < sheet->imports.length; ++i) {
        katana_print_rule(parser, sheet->imports.data[i]);
    }
    for (size_t i = 0; i < sheet->rules.length; ++i) {
        katana_print_rule(parser, sheet->rules.data[i]);
    }
    katana_print("\n");
}

void katana_print_rule(KatanaParser* parser, KatanaRule* rule)
{
    if ( NULL == rule ) {
        breakpoint;
        return;
    }
    
    switch (rule->type) {
        case KatanaRuleStyle:
            katana_print_style_rule(parser, (KatanaStyleRule*)rule);
            break;
        case KatanaRuleImport:
            katana_print_import_rule(parser, (KatanaImportRule*)rule);
            break;
        case KatanaRuleFontFace:
            katana_print_font_face_rule(parser, (KatanaFontFaceRule*)rule);
            break;
        case KatanaRuleKeyframes:
            katana_print_keyframes_rule(parser, (KatanaKeyframesRule*)rule);
            break;
        case KatanaRuleMedia:
            katana_print_media_rule(parser, (KatanaMediaRule*)rule);
            break;
        case KatanaRuleSupports:
            break;
        case KatanaRuleUnknown:
            break;
            
        default:
            break;
    }
}

void katana_print_import_rule(KatanaParser* parser, KatanaImportRule* rule)
{
    katana_print("@%s ", rule->base.name);
    katana_print("url(%s)", rule->href);
    katana_print(";\n");
}

void katana_print_keyframes_rule(KatanaParser* parser, KatanaKeyframesRule* rule)
{
    katana_print("@%s ", rule->base.name);
    katana_print("%s {\n", rule->name);
    for (size_t i = 0; i < rule->keyframes->length; ++i) {
        katana_print_keyframe(parser, rule->keyframes->data[i]);
    }
    katana_print("}\n");
}

void katana_print_keyframe(KatanaParser* parser, KatanaKeyframe* keyframe)
{
    assert( NULL != keyframe );
    if ( NULL == keyframe )
        return;
    
    for (size_t i = 0; i < keyframe->selectors->length; ++i) {
        KatanaValue* value = keyframe->selectors->data[i];
        if ( value->unit == KATANA_VALUE_NUMBER ) {
            katana_print("%s", value->raw);
        }
        if ( i != keyframe->selectors->length -1 ) {
            katana_print(", ");
        }
    }
    katana_print(" {\n");
    katana_print_declaration_list(parser, keyframe->declarations);
    katana_print("}\n");
}

void katana_print_media_query_exp(KatanaParser* parser, KatanaMediaQueryExp* exp)
{
    katana_print("(");
    if (NULL != exp->feature) {
        katana_print("%s", exp->feature);
    }
    if ( exp->values && exp->values->length ) {
        const char* str = katana_stringify_value_list(parser, exp->values);
        katana_print(": %s", str);
        katana_parser_deallocate(parser, (void*) str);
    }
    katana_print(")");
}

void katana_print_media_query(KatanaParser* parser, KatanaMediaQuery* query)
{
    // For now ignored is always false
//    if ( !query->ignored ) {
        // print restrictor
        switch ( query->restrictor ) {
            case KatanaMediaQueryRestrictorOnly:
                katana_print("only ");
                break;
            case KatanaMediaQueryRestrictorNot:
                katana_print("not ");
                break;
            case KatanaMediaQueryRestrictorNone:
                break;
        }
        
        if ( NULL == query->expressions || 0 == query->expressions->length ) {
            if ( NULL != query->type ) {
                katana_print("%s", query->type);
            }
            return;
        }
        
        if ( (NULL != query->type && stricmp(query->type, "all")) || query->restrictor != KatanaMediaQueryRestrictorNone) {
            if ( NULL != query->type ) {
                katana_print("%s", query->type);
            }
            katana_print(" and ");
        }
        
        katana_print_media_query_exp(parser, query->expressions->data[0]);
        
        for (size_t i = 1; i < query->expressions->length; ++i) {
            katana_print(" and ");
            katana_print_media_query_exp(parser, query->expressions->data[i]);
        }
//    } else {
//        // If query is invalid, serialized text should turn into "not all".
//        katana_print("not all");
//    }
}

void katana_print_media_list(KatanaParser* parser, KatanaArray* medias)
{
    bool first = true;
    for (size_t i = 0; i < medias->length; ++i) {
        if (!first)
            katana_print(", ");
        else
            first = false;
        katana_print_media_query(parser, (KatanaMediaQuery*)medias->data[i]);
    }
}

void katana_print_media_rule(KatanaParser* parser, KatanaMediaRule* rule)
{
    katana_print("@%s ", rule->base.name);
    
    if ( rule->medias->length ) {
        katana_print_media_list(parser, rule->medias);
    }
    
    if ( rule->medias->length ) {
        katana_print(" {\n");
        for (size_t i = 0; i < rule->rules->length; ++i) {
            katana_print_style_rule(parser, rule->rules->data[i]);
        }
        katana_print("}\n");
    } else {
        katana_print(";\n");
    }
}

void katana_print_selector(KatanaParser* parser, KatanaSelector* selector)
{
    KatanaParserString * string = katana_selector_to_string(parser, selector, NULL);
    const char* text = katana_string_to_characters(parser, string);
    katana_parser_deallocate(parser, (void*) string->data);
    katana_parser_deallocate(parser, (void*) string);
    katana_print("%s", text);
    katana_parser_deallocate(parser, (void*) text);
}

void katana_print_selector_list(KatanaParser* parser, KatanaArray* selectors)
{
    for (size_t i = 0; i < selectors->length; ++i) {
        katana_print_selector(parser, selectors->data[i]);
        if ( i != selectors->length -1 ) {
            katana_print(",\n");
        }
    }
}

void katana_print_style_rule(KatanaParser* parser, KatanaStyleRule* rule)
{
    katana_print_selector_list(parser, rule->selectors);
    katana_print(" {\n");
    
    if ( rule->declarations->length ) {
        katana_print_declaration_list(parser, rule->declarations);
    } else {
        katana_print("  /*no rule*/\n");
    }
    
    katana_print("}\n");
}

void katana_print_declaration(KatanaParser* parser, KatanaDeclaration* decl)
{
    const char* str = katana_stringify_value_list(parser, decl->values);
    katana_print("%s: %s", decl->property, str);
    katana_parser_deallocate(parser, (void*) str);
    if ( decl->important ) {
        katana_print(" !important");
    }
}

void katana_print_declaration_list(KatanaParser* parser, KatanaArray* declarations)
{
    for (size_t i = 0; i < declarations->length; ++i) {
        katana_print("  ");
        katana_print_declaration(parser, declarations->data[i]);
        katana_print(";\n");
    }
}

void katana_print_value_list(KatanaParser* parser, KatanaArray* values)
{
    const char* str = katana_stringify_value_list(parser, values);
    katana_print("%s", str);
    katana_parser_deallocate(parser, (void*) str);
}

void katana_print_font_face_rule(KatanaParser* parser, KatanaFontFaceRule* rule)
{
    katana_print("@%s {", rule->base.name);
    katana_print_declaration_list(parser, rule->declarations);
    katana_print("}\n");
}

KatanaOutput* katana_dump_output(KatanaOutput* output)
{
    if ( NULL == output )
        return output;
    
    KatanaParser parser;
    parser.options = &kKatanaDefaultOptions;

    switch (output->mode) {
        case KatanaParserModeStylesheet:
            katana_print_stylesheet(&parser, output->stylesheet);
            break;
        case KatanaParserModeRule:
            katana_print_rule(&parser, output->rule);
            break;
        case KatanaParserModeKeyframeRule:
            katana_print_keyframe(&parser, output->keyframe);
            break;
        case KatanaParserModeKeyframeKeyList:
            katana_print_value_list(&parser, output->keyframe_keys);
            break;
        case KatanaParserModeMediaList:
            katana_print_media_list(&parser, output->medias);
            break;
        case KatanaParserModeValue:
            katana_print_value_list(&parser, output->values);
            break;
        case KatanaParserModeSelector:
            katana_print_selector_list(&parser, output->selectors);
            break;
        case KatanaParserModeDeclarationList:
            katana_print_declaration_list(&parser, output->declarations);
            break;
    }
    katana_print("\n");
    return output;
}


static const char* katana_stringify_value_list(KatanaParser* parser, KatanaArray* values)
{
    if (NULL == values)
        return NULL;
    KatanaParserString * buffer = katana_parser_allocate(parser, sizeof(KatanaParserString));
    katana_string_init(parser, buffer);
    for (size_t i = 0; i < values->length; ++i) {
        KatanaValue* value = values->data[i];
        const char* value_str = katana_stringify_value(parser, value);
        katana_string_append_characters(parser, value_str, buffer);
        katana_parser_deallocate(parser, (void*) value_str);
        value_str = NULL;
        if ( i < values->length - 1 ) {
            if ( value->unit != KATANA_VALUE_PARSER_OPERATOR ) {
                if ( i < values->length - 2 ) {
                    value = values->data[i+1];
                    if ( value->unit != KATANA_VALUE_PARSER_OPERATOR ) {
                        katana_string_append_characters(parser, " ", buffer);
                    }
                } else {
                    katana_string_append_characters(parser, " ", buffer);
                }
            }
        }
    }
    const char * str = katana_string_to_characters(parser, (KatanaParserString*)buffer);
    katana_parser_deallocate(parser, buffer->data);
    katana_parser_deallocate(parser, (void*) buffer);
    return str;
}

static const char* katana_stringify_value(KatanaParser* parser, KatanaValue* value)
{
    // TODO: @(QFish) Handle this more gracefully X).
    char str[256];
    
    switch (value->unit) {
        case KATANA_VALUE_NUMBER:
        case KATANA_VALUE_PERCENTAGE:
        case KATANA_VALUE_EMS:
        case KATANA_VALUE_EXS:
        case KATANA_VALUE_REMS:
        case KATANA_VALUE_CHS:
        case KATANA_VALUE_PX:
        case KATANA_VALUE_CM:
        case KATANA_VALUE_DPPX:
        case KATANA_VALUE_DPI:
        case KATANA_VALUE_DPCM:
        case KATANA_VALUE_MM:
        case KATANA_VALUE_IN:
        case KATANA_VALUE_PT:
        case KATANA_VALUE_PC:
        case KATANA_VALUE_DEG:
        case KATANA_VALUE_RAD:
        case KATANA_VALUE_GRAD:
        case KATANA_VALUE_MS:
        case KATANA_VALUE_S:
        case KATANA_VALUE_HZ:
        case KATANA_VALUE_KHZ:
        case KATANA_VALUE_TURN:
            snprintf(str, sizeof(str), "%s", value->raw);
            break;
        case KATANA_VALUE_IDENT:
            snprintf(str, sizeof(str), "%s", value->string);
            break;
        case KATANA_VALUE_STRING:
            // FIXME: @(QFish) Do we need double quote or not ?
//            snprintf(str, sizeof(str), "\"%s\"", value->string);
            snprintf(str, sizeof(str), "%s", value->string);
            break;
        case KATANA_VALUE_PARSER_FUNCTION:
        {
            const char* args_str = katana_stringify_value_list(parser, value->function->args);
            snprintf(str, sizeof(str), "%s%s)", value->function->name, args_str);
            katana_parser_deallocate(parser, (void*) args_str);
            break;
        }
        case KATANA_VALUE_PARSER_OPERATOR:
            if (value->iValue != '=') {
                snprintf(str, sizeof(str), " %c ", value->iValue);
            } else {
                snprintf(str, sizeof(str), " %c", value->iValue);
            }
            break;
        case KATANA_VALUE_PARSER_LIST:
            return katana_stringify_value_list(parser, value->list);
            break;
        case KATANA_VALUE_PARSER_HEXCOLOR:
            snprintf(str, sizeof(str), "#%s", value->string);
            break;
        case KATANA_VALUE_URI:
            snprintf(str, sizeof(str), "url(%s)", value->string);
            break;
        default:
            katana_print("KATANA: Unknown Value unit.");
            break;
    }

    size_t len = strlen(str);
    char* dest = katana_parser_allocate(parser, len+1);
    strcpy(dest, str);
    dest[len] = '\0';
    return dest;
}
