#include "CppGen.h"
#include "Gen/Generated.h"
#include "Logging.h"
#include "MiniDef.h"

#include "TypeDef.h"
#include "bx/hash.h"
#include "bx/string.h"
#include <bx/filepath.h>
#include <ctype.h>

bool Parser::CmpAdvance(bx::StringView expect, Result& Res, bool RequireGap)
{
    uint32_t size = expect.getLength();
    const char* ptr = expect.getPtr();

    // abort if expect longer than remaining text
    if (size > GetRemaining())
    {
        return false;
    }

    // start matching
    int32_t i = 0;
    for (; i < size; ++i)
    {
        // if expect has 0 byte, go back one idx, we succeded
        if (expect.getPtr()[i] == 0)
        {
            i--;
            break;
        }

        // check match
        if (expect.getPtr()[i] != ReadPtr[i]) return false;
    }

    // check for following whitespace or symbols
    if (RequireGap && GetRemaining() > size)
    {
        char next = ReadPtr[i];
        if (isgraph(next) && next != '{' && next != '}' && next != '(' && next != ')')
        {
            return false;
        }
    }
    Res = Advance(i);
    return true;
}

Parser::Result Parser::ExpectChar(bx::StringView expect)
{
    Result Res = OK;
    if (!CmpAdvance(expect, Res))
    {
        LOG_ERROR(Line, "Expected: %s", expect.getPtr());
        ErrorLine();
        return Error;
    }
    return Res;
}

Parser::Result Parser::SkipWhitespace(bool* SkippedNewLine)
{
    int32_t Remaining = GetRemaining();
    for (int32_t i = 0; i < Remaining; ++i)
    {
        if (*ReadPtr == 0) return EndOfFile;
        if (SkippedNewLine != nullptr && (*ReadPtr == '\r' || *ReadPtr == '\n'))
        {
            if (*ReadPtr == '\n') ++Line;
            *SkippedNewLine = true;
        }
        if (isgraph(*ReadPtr)) return OK;
        ReadPtr++;
    }
    return EndOfFile;
}

Parser::Result Parser::SkipLine()
{
    int32_t Remaining = GetRemaining();
    for (int32_t i = 0; i < Remaining; ++i)
    {
        if (*ReadPtr == 0) return EndOfFile;
        if (*ReadPtr == '\n' && i < Remaining - 1)
        {
            ReadPtr++;
            if (*ReadPtr == 0) return EndOfFile;
            return OK;
        }
        ReadPtr++;
    }
    return EndOfFile;
}

Parser::Result Parser::Advance(int32_t Amount)
{
    int32_t Remaining = GetRemaining();
    if (Amount > Remaining)
    {
        ReadPtr += Remaining;
        return EndOfFile;
    }
    ReadPtr += Amount;
    return OK;
}

#define CHECK(x)                                                                                                       \
    Res = x;                                                                                                           \
    if (Res != OK) return Res;

Parser::Result Parser::Parse()
{
    ReadPtr = &Buffer[0];
    Parser::Result Res = Parser::OK;
    while (Res == Parser::OK)
    {
        Res = SkipWhitespace();
        if (Res == Parser::OK)
        {
            // Start of Line
            if (*ReadPtr == '#')
            {
                Res = SkipLine();
                continue;
            }

            Res = HandleFileStart();
        }
    }

    return Res;
}

Parser::Result Parser::HandleFileStart()
{
    Result Res = OK;
    if (CmpAdvance("type", Res, true) && Res == OK)
    {
        return HandleType();
    }
    if (Res != OK) return Res;

    if (CmpAdvance("enum", Res, true) && Res == OK)
    {
        return HandleEnum();
    }
    if (Res != OK) return Res;

    LOG(Line, "Only valid here: type or enum!");
    return Error;
}

Parser::Result Parser::HandleType()
{
    Result Res = OK;
    if (!Stack.Push(TokenType::TypeKeyword)) return Error;
    CHECK(SkipWhitespace());

    if (Definitions.TypeCount >= Def::DefinitionFile::MaxTypes)
    {
        LOG_ERROR(Line, "Too many types!");
        ErrorLine();
        return Error;
    }
    Def::Type& t = Definitions.Types[Definitions.TypeCount];
    for (int32_t i = 0; i < Def::MaxFields; ++i)
    {
        bx::strCopy(t.FieldValues[i], sizeof(t.FieldValues[i]), "{}");
    }

    CHECK(ReadName(t.Name));
    CHECK(SkipWhitespace());

    CHECK(ExpectChar("{"));
    CHECK(SkipWhitespace());

    while (!CmpAdvance("}", Res))
    {
        if (t.FieldCount >= Def::MaxFields)
        {
            LOG_ERROR(Line, "Too many fields in type!");
            ErrorLine();
            return Error;
        }
        CHECK(ReadTypeToken());
        CHECK(SkipWhitespace());
        CHECK(ReadName(t.FieldNames[t.FieldCount]));

        bool NewLine = false;
        CHECK(SkipWhitespace(&NewLine));

        while (!NewLine)
        {
            Result Res;
            if (CmpAdvance("Arr", Res, true) && Res == Result::OK)
            {
                CHECK(ExpectChar("("));
                CHECK(ReadUint(t.FieldArraySizes[t.FieldCount]));
                CHECK(ExpectChar(")"));
            }
            else if (CmpAdvance("Default", Res, true) && Res == OK)
            {
                CHECK(ExpectChar("("));
                CHECK(ExpectChar("\""));

                int32_t Remaining = bx::min(GetRemaining(), (int32_t)BX_COUNTOF(Def::Type::FieldValues[0]));
                for (int32_t i = 0; i < Remaining; ++i)
                {
                    if (*ReadPtr != '\\' && CmpAdvance("\"", Res)) break;
                    t.FieldValues[t.FieldCount][i] = *ReadPtr;
                    ReadPtr++;
                }
                if (Res != OK) return Res;
                CHECK(ExpectChar(")"));
            }
            else
            {
                LOG_ERROR(Line, "Unknown token!");
                ErrorLine();
                return Error;
            }
            CHECK(SkipWhitespace(&NewLine));
        }

        t.FieldCount++;
    }
    Definitions.TypeCount++;
    return Res;
}

Parser::Result Parser::HandleEnum()
{
    Result Res = OK;
    if (!Stack.Push(TokenType::EnumKeyword)) return Error;
    CHECK(SkipWhitespace());

    if (Definitions.EnumCount >= Def::DefinitionFile::MaxTypes)
    {
        LOG_ERROR(Line, "Too many enums!");
        ErrorLine();
        return Error;
    }
    Def::Enum& e = Definitions.Enums[Definitions.EnumCount];
    CHECK(ReadName(e.Name));
    CHECK(SkipWhitespace());

    if (CmpAdvance("(", Res) && Res == OK)
    {
        CHECK(SkipWhitespace());
        Def::FieldType field;
        CHECK(ReadNativeFieldType(field));
        CHECK(SkipWhitespace());
        CHECK(ExpectChar(")"));
        CHECK(SkipWhitespace());
    }

    CHECK(ExpectChar("{"));
    CHECK(SkipWhitespace());
    while (!CmpAdvance("}", Res))
    {
        if (e.EntryCount >= Def::MaxFields)
        {
            LOG_ERROR(Line, "Too many fields in enum!");
            return Error;
        }
        CHECK(ReadName(e.EntryNames[e.EntryCount]));
        CHECK(ReadOptionalEnumValues(e, e.EntryCount));
        CHECK(SkipWhitespace());
        e.EntryCount++;
    }
    Definitions.EnumCount++;
    return Res;
}

Parser::Result Parser::ReadName(char* Target)
{
    int32_t Remaining = GetRemaining();
    for (int32_t i = 0; i < Remaining; ++i)
    {
        if (!IsValidNameCharacter(*ReadPtr))
        {
            if (i == 0)
            {
                LOG_ERROR(Line, "Empty names are not allowed!");
                ErrorLine();
                return Error;
            }
            return OK;
        }
        if (i < Def::MaxNameLength)
        {
            Target[i] = *ReadPtr;
        }
        else
        {
            LOG_ERROR(Line, "Name too long!");
            ErrorLine();
            return Error;
        }
        ReadPtr++;
    }
    return EndOfFile;
}

Parser::Result Parser::ReadUint(uint32_t& Out)
{
    char DigitBuf[32]{0};
    int32_t Remaining = GetRemaining();
    if (Remaining == 0) return Result::EndOfFile;
    int32_t Written = 0;

    for (int32_t i = 0; i < Remaining; ++i)
    {
        if (*ReadPtr < '0' || *ReadPtr > '9') break;

        DigitBuf[i] = *ReadPtr;
        ++ReadPtr;
        ++Written;
    }
    if (Written == 0)
    {
        LOG_ERROR(Line, "No digits for uint!");
        ErrorLine();
        return Result::Error;
    }
    if (!bx::fromString(&Out, DigitBuf))
    {
        LOG_ERROR(Line, "Failed to parse uint!");
        ErrorLine();
        return Result::Error;
    }
    return Result::OK;
}

Parser::Result Parser::ReadTypeToken()
{
    Result Res = OK;
    Def::Type& t = Definitions.Types[Definitions.TypeCount];
    if (ReadNativeFieldType(t.FieldTypes[t.FieldCount]) == OK) return OK;
    if (ReadDefinedFieldType(t.FieldTypes[t.FieldCount]) == OK) return OK;
    LOG_ERROR(Line, "Unknown type token!");
    ErrorLine();
    return Error;
}

Parser::Result Parser::ReadNativeFieldType(Def::FieldType& FieldT)
{
    Result Res = OK;
    for (int32_t i = 0; i < Generated::KnownType::EntryCount; ++i)
    {
        if (CmpAdvance(Generated::KnownType::EntryNames[i], Res, true) && Res == OK)
        {
            FieldT.FieldKind = Def::EFieldType::Native;
            FieldT.Native = Generated::KnownType::Enum(i);
            return OK;
        }
    }
    return Error;
}

Parser::Result Parser::ReadDefinedFieldType(Def::FieldType& FieldT)
{
    Result Res = OK;
    for (uint16_t i = 0; i < Definitions.TypeCount; ++i)
    {
        if (CmpAdvance(Definitions.Types[i].Name, Res, true) && Res == OK)
        {
            Def::Type& t = Definitions.Types[Definitions.TypeCount];
            FieldT.FieldKind = Def::EFieldType::DefinedClass;
            FieldT.TypeIdx = i;
            return OK;
        }
    }
    for (uint16_t i = 0; i < Definitions.EnumCount; ++i)
    {
        if (CmpAdvance(Definitions.Enums[i].Name, Res, true) && Res == OK)
        {
            Def::Type& t = Definitions.Types[Definitions.TypeCount];
            FieldT.FieldKind = Def::EFieldType::DefinedEnum;
            FieldT.TypeIdx = i;
            return OK;
        }
    }
    return Error;
}

Parser::Result Parser::ReadOptionalEnumValues(Def::Enum& Enum, int32_t EntryIdx)
{
    Result Res = OK;

    bool SkippedNewLine = false;
    CHECK(SkipWhitespace(&SkippedNewLine));
    if (SkippedNewLine) return Res;

    for (int32_t extraIdx = 0; extraIdx < Def::MaxExtraEnumFields; ++extraIdx)
    {
        CHECK(ReadName(Enum.ExtraStringFieldNames[extraIdx]));
        CHECK(SkipWhitespace());
        CHECK(ExpectChar("("));
        CHECK(SkipWhitespace());
        CHECK(ExpectChar("\""));

        int32_t Remaining = GetRemaining();
        for (int32_t i = 0; i < Remaining; ++i)
        {
            if (CmpAdvance("\"", Res)) break;
            Enum.ExtraStringFields[EntryIdx][extraIdx][i] = *ReadPtr;
            ReadPtr++;
        }
        if (Res != OK) return Res;

        CHECK(SkipWhitespace());
        CHECK(ExpectChar(")"));
        CHECK(SkipWhitespace(&SkippedNewLine));
        Enum.ExtraStringFieldCount = bx::max(Enum.ExtraStringFieldCount, extraIdx + 1);

        if (SkippedNewLine) return Res;
    }
    LOG_ERROR(Line, "Too many extra enum fields!");
    ErrorLine();
    return Error;
}

int32_t Parser::GetRemaining()
{
    return &Buffer[BufferSize] - ReadPtr;
}
uint32_t Parser::CalculateTypeHash(const Def::Type& t)
{
    bx::HashMurmur2A hash;
    hash.begin();
    for (int32_t i = 0; i < t.FieldCount; ++i)
    {
        hash.add(t.FieldNames[i]);
        hash.add(t.FieldArraySizes[i]);

        Def::EFieldType fieldType = t.FieldTypes[i].FieldKind;
        if (fieldType == Def::EFieldType::Native)
        {
            hash.add(t.FieldTypes[i].Native);
        }
        else if (fieldType == Def::EFieldType::DefinedClass)
        {
            Def::Type& dependType = Definitions.Types[t.FieldTypes[i].TypeIdx];
            if (dependType.Hash == 0)
            {
                CalculateTypeHash(dependType);
            }
            hash.add(dependType.Hash);
        }
        else if (fieldType == Def::EFieldType::DefinedEnum)
        {
            hash.add(Definitions.Enums[t.FieldTypes[i].TypeIdx].Hash);
        }
        else
        {
            LOG_ERROR(0, "TODO!");
        }
    }
    return hash.end();
}
void Parser::CalculateHashes()
{
    for (int32_t i = 0; i < Definitions.EnumCount; ++i)
    {
        Def::Enum& e = Definitions.Enums[i];
        bx::HashMurmur2A hash;
        hash.begin();
        hash.add(e.EnumType.Native);
        e.Hash = hash.end();
    }
    for (int32_t i = 0; i < Definitions.TypeCount; ++i)
    {
        Def::Type& t = Definitions.Types[i];
        if (t.Hash == 0)
        {
            t.Hash = CalculateTypeHash(t);
        }
    }
}

Parser FileParser;
CppFileWriter Writer;
int main(int argc, const char** argv)
{
    if (argc != 3)
    {
        LOG(0, "invalid number of arguments!");
        return 1;
    }
    bx::FilePath defPath(argv[1]);
    bx::FilePath outPath(argv[2]);

    bx::FileReader reader;
    bx::Error error;
    if (!reader.open(defPath, &error))
    {
        LOG(0, "failed to find def file at %s", error.getMessage().getCPtr());
        return 1;
    }

    LOG(0, "Reading from %s", defPath.getCPtr());
    FileParser.BufferSize = bx::read(&reader, FileParser.Buffer, Parser::MaxBufferSize, &error);
    Parser::Result Res = FileParser.Parse();

    if (Res == Parser::OK)
    {
        LOG_WARN(0, "Ending not at end of file, this is strange...");
    }
    else if (Res == Parser::Error)
    {
        LOG_ERROR(0, "Aborting with error, not generating file.");
        return 1;
    }
    LOG(0, "Finished parsing!");

    FileParser.CalculateHashes();
    Writer.GenerateCpp(outPath, FileParser.Definitions);

    LOG(0, "Writing type data file");
    Writer.WriteTypeDataFile(outPath, FileParser.Definitions);
    LOG(0, "Finished writing type data file!");

    return 0;
}
