#include "json.hpp"
#include <cstdlib>
#include <format>
#include <memory>
#include <string>
#include <unordered_map>

using namespace sliger::json;

auto ident_tok_typs = std::unordered_map<std::string, TokTyp> {
    { "null", TokTyp::Null },
    { "false", TokTyp::False },
    { "true", TokTyp::True },
};

auto id_start_chars = "abcdefghijklmnopqrstuvwxyz"
                      "ABCDEFGHIJKLMNOPQRSTUVWXYZ_";

auto id_tail_chars = "abcdefghijklmnopqrstuvwxyz"
                     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                     "1234567890";

auto Lexer::next() -> Res<Tok>
{
    auto pos = this->pos();
    if (done()) {
        return Tok(TokTyp::Eof, pos);
    }
    if (test('"')) {
        step();
        auto value = std::string();
        while (!done() and !test('"')) {
            if (cur() == '\\') {
                step();
                if (done())
                    break;
                value.push_back([&] {
                    char ch = cur();
                    switch (ch) {
                        case 'n':
                            return '\n';
                        case 'r':
                            return '\r';
                        case 't':
                            return '\t';
                        case '0':
                            return '\0';
                        default:
                            return ch;
                    }
                }());
            } else {
                value.push_back(cur());
            }
            step();
        }
        if (!test('"')) {
            return Err {
                .pos = pos,
                .msg
                = std::format("malformed string, expected '\"', got '{}' token",
                    this->cur()),
            };
        }
        step();
        return Tok(TokTyp::String, pos, intern_str(value));
    }
    auto step_n_ret = [&](auto tok) {
        step();
        return tok;
    };
    switch (cur()) {
        case '0':
            return step_n_ret(Tok(TokTyp::Float, pos, 0.0));
        case '{':
            return step_n_ret(Tok(TokTyp::LBrace, pos));
        case '}':
            return step_n_ret(Tok(TokTyp::RBrace, pos));
        case '[':
            return step_n_ret(Tok(TokTyp::LBracket, pos));
        case ']':
            return step_n_ret(Tok(TokTyp::RBracket, pos));
        case ',':
            return step_n_ret(Tok(TokTyp::Comma, pos));
        case ':':
            return step_n_ret(Tok(TokTyp::Colon, pos));
    }
    if (test_in(id_start_chars)) {
        auto value = std::string();
        while (test_in(id_tail_chars)) {
            value.push_back(cur());
            step();
        }
        if (ident_tok_typs.find(value) == ident_tok_typs.end()) {
            return Err {
                .pos = pos,
                .msg = std::format("unknown identifier \"{}\"", value),
            };
        }
        return Tok(ident_tok_typs.at(value), pos);
    }
    if (test_in("123456789")) {
        auto value_str = std::string();
        while (test_in("1234567890")) {
            value_str.push_back(cur());
            step();
        }
        auto value = std::atof(value_str.c_str());
        return Tok(TokTyp::Float, pos, value);
    }
    auto ch = cur();
    step();
    return Err {
        .pos = pos,
        .msg = std::format("unknown character '{}'", ch),
    };
}

auto Parser::parse_val() -> Res<std::unique_ptr<Value>>
{
    if (not this->cur.ok())
        return this->cur.err();
    auto cur = this->cur.val();
    switch (cur.typ) {
        case TokTyp::Eof:
            return Err {
                .pos = cur.pos,
                .msg = "expected value, got eof",
            };
        case TokTyp::String: {
            auto value = this->lexer.val(cur.val_id);
            step();
            return Res<std::unique_ptr<Value>>(std::make_unique<String>(value));
        }
        case TokTyp::Float: {
            auto value = cur.float_val;
            step();
            return Res<std::unique_ptr<Value>>(std::make_unique<Number>(value));
        }
        case TokTyp::False: {
            step();
            return Res<std::unique_ptr<Value>>(std::make_unique<Bool>(false));
        }
        case TokTyp::True: {
            step();
            return Res<std::unique_ptr<Value>>(std::make_unique<Bool>(true));
        }
        case TokTyp::Null: {
            step();
            return Res<std::unique_ptr<Value>>(std::make_unique<Null>());
        }
        case TokTyp::LBrace: {
            step();
            ObjectFields fields;
            if (curtyp() != TokTyp::RBrace) {
                if (curtyp() != TokTyp::String) {
                    return unexpected_tok_err(
                        TokTyp::String, "malformed object");
                }
                auto key = this->lexer.val(this->cur.val().val_id);
                step();
                if (curtyp() != TokTyp::Comma) {
                    return unexpected_tok_err(
                        TokTyp::Comma, "malformed object");
                }
                step();
                auto value = parse_val();
                if (value.ok()) {
                    return value.err();
                }
                fields.insert_or_assign(key, std::move(value.val()));
                while (curtyp() == TokTyp::Comma) {
                    step();
                    if (curtyp() != TokTyp::String) {
                        return unexpected_tok_err(
                            TokTyp::String, "malformed object");
                    }
                    auto key = this->lexer.val(this->cur.val().val_id);
                    step();
                    if (curtyp() != TokTyp::Comma) {
                        return unexpected_tok_err(
                            TokTyp::Comma, "malformed object");
                    }
                    step();
                    auto value = parse_val();
                    if (value.ok()) {
                        return value.err();
                    }
                    fields.insert_or_assign(key, std::move(value.val()));
                }
            }
            if (curtyp() != TokTyp::RBrace) {
                return unexpected_tok_err(TokTyp::RBrace, "malformed object");
            }
            return Res<std::unique_ptr<Value>>(
                std::make_unique<Object>(std::move(fields)));
        }
        case TokTyp::LBracket: {
            step();
            ArrayValues values;
            if (curtyp() != TokTyp::RBrace) {
                auto value = parse_val();
                if (value.ok()) {
                    return value.err();
                }
                values.push_back(std::move(value.val()));
                while (curtyp() == TokTyp::Comma) {
                    step();
                    auto value = parse_val();
                    if (value.ok()) {
                        return value.err();
                    }
                    values.push_back(std::move(value.val()));
                }
            }
            if (curtyp() != TokTyp::RBrace) {
                return unexpected_tok_err(TokTyp::RBrace, "malformed object");
            }
            return Res<std::unique_ptr<Value>>(
                std::make_unique<Array>(std::move(values)));
        }
        case TokTyp::RBrace:
        case TokTyp::RBracket:
        case TokTyp::Comma:
        case TokTyp::Colon:
            return Err {
                .pos = cur.pos,
                .msg = std::format("expected value, got '{}' token",
                    tok_typ_to_string(cur.typ)),
            };
            break;
    }
    return Err {
        .pos = cur.pos,
        .msg = std::format(
            "internal error, could not parse '{}'", tok_typ_to_string(cur.typ)),
    };
}