#include "parser.h"
#include <algorithm>
#include <cassert>
#include <codecvt>
#include <deque>
#include <locale>
#include <sstream>

#include <iostream>

namespace {
    double readNumber(std::string in) {
        std::istringstream iss(in);
        double d;
        iss >> d;
        return d;
    }

    std::string readString(std::string in) {
        std::string result;
        for (std::size_t i = 1; i < in.length() - 1; i++) {
            if (in[i] == '\\' && in[i + 1] != 'u') {
                char c = 0;
                switch (in[i + 1]) {
                    case '"':
                    case '\\':
                    case '/':
                        c = in[i + 1];
                        break;
                    case 'b':
                        c = '\b';
                        break;
                    case 'f':
                        c = '\f';
                        break;
                    case 'n':
                        c = '\n';
                        break;
                    case 'r':
                        c = '\r';
                        break;
                    case 't':
                        c = '\t';
                        break;
                    default:
                        break;
                }
                result.push_back(c);
                i++;
            } else if (in[i] == '\\'){
                char16_t unicode_value;
                std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t > utf8converter;
                unicode_value = 0;
                for (int j = 0; j < 4; j++, i++) {
                    char hex = in[i + 2];
                    unicode_value *= 16;
                    if (hex >= '0' && hex <= '9')
                        unicode_value += hex - '0';
                    else if (hex >= 'A' && hex <= '9')
                        unicode_value += hex - 'A' + 10;
                    else
                        unicode_value += hex - 'a' + 10;
                }
                result.append(utf8converter.to_bytes(unicode_value));
                i++;
            } else {
                if (iscntrl(in[i])) {
                    throw SyntaxError("Control character inside string");
                }
                result.push_back(in[i]);
            }
        }
        return result;
    }
}

namespace json {

Parser::Parser(JSONLexer lex) : JSONParser<JSON>(), m_lex(lex)
{}

Parser::Token Parser::lex() {
    try {
        JSONLexer::Token orig = m_lex.nextToken();
        JSONParser_Symbol s;
        switch (orig.type) {
            case JSONLexer::COLON:
                s = JSONParser_Symbol::T_COLON;
                break;
            case JSONLexer::COMMA:
                s = JSONParser_Symbol::T_COMMA;
                break;
            case JSONLexer::LBRACE:
                s = JSONParser_Symbol::T_LBRACE;
                break;
            case JSONLexer::RBRACE:
                s = JSONParser_Symbol::T_RBRACE;
                break;
            case JSONLexer::LBRACKET:
                s = JSONParser_Symbol::T_LBRACKET;
                break;
            case JSONLexer::RBRACKET:
                s = JSONParser_Symbol::T_RBRACKET;
                break;
            case JSONLexer::STRING:
                return Token{JSONParser_Symbol::T_STRING, JSON::string(readString(orig.content))};
            case JSONLexer::NUMBER:
                return Token{JSONParser_Symbol::T_NUMBER, JSON::num(readNumber(orig.content))};
            case JSONLexer::TTRUE:
                return Token{JSONParser_Symbol::T_TTRUE, JSON::boolean(true)};
            case JSONLexer::TFALSE:
                return Token{JSONParser_Symbol::T_TFALSE, JSON::boolean(false)};
            case JSONLexer::TNULL:
                return Token{JSONParser_Symbol::T_TNULL, JSON::null()};
            default:
                //impossible
                break;
        }
        return Token{s, JSON()};
    }
    catch (JSONLexer::NoMoreTokens) {
        return Token{JSONParser_Symbol::T_EOF, JSON()};
    }
}

JSON Parser::reduce_0(std::deque<Token> subparts) {
    return std::move(subparts[0].value);
}
JSON Parser::reduce_1(std::deque<Token> subparts) {
    return std::move(subparts[0].value);
}
JSON Parser::reduce_2(std::deque<Token> subparts) {
    return std::move(subparts[0].value);
}
JSON Parser::reduce_3(std::deque<Token> subparts) {
    return std::move(subparts[0].value);
}
JSON Parser::reduce_4(std::deque<Token> subparts) {
    return std::move(subparts[0].value);
}
JSON Parser::reduce_5(std::deque<Token> subparts) {
    return std::move(subparts[0].value);
}
JSON Parser::reduce_6(std::deque<Token> subparts) {
    return std::move(subparts[0].value);
}
JSON Parser::reduce_7(std::deque<Token> subparts) {
    return std::move(subparts[1].value);
}
JSON Parser::reduce_8(std::deque<Token>) {
    return JSON::object();
}
JSON Parser::reduce_9(std::deque<Token> subparts) {
    JSON obj = JSON::object();
    obj[std::move(subparts[0].value)] = std::move(subparts[2].value);
    return obj;
}
JSON Parser::reduce_10(std::deque<Token> subparts) {
    JSON obj = std::move(subparts[4].value);
    obj[std::move(subparts[0].value)] = std::move(subparts[2].value);
    return obj;
}
JSON Parser::reduce_11(std::deque<Token> subparts) {
    return std::move(subparts[1].value);
}
JSON Parser::reduce_12(std::deque<Token>) {
    return JSON::array();
}
JSON Parser::reduce_13(std::deque<Token> subparts) {
    JSON arr = JSON::array();
    arr.push_front(std::move(subparts[0].value));
    return arr;
}
JSON Parser::reduce_14(std::deque<Token> subparts) {
    JSON arr = std::move(subparts[2].value);
    arr.push_front(std::move(subparts[0].value));
    return arr;
}

}