#include "TomlReader.h"

#include <iostream>
#include <locale>
#include <algorithm>

TomlTable::TomlTable(const std::string& header)
    : mHeader(header)
{

}

void TomlTable::addComment(const Comment& comment)
{
    mComments.push_back(comment);
}

void TomlTable::addTable(std::unique_ptr<TomlTable> table)
{
    mTables[table->getHeader()] = std::move(table);
}

void TomlTable::addKeyValuePair(const std::string& key, const std::string& value)
{
    mMap[key] = value;
}

std::string TomlTable::getHeader() const
{
    return mHeader;
}

TomlTable* TomlTable::getTable(const std::string& path)
{
    return mTables[path].get();
}

TomlTable::KeyValuePairs TomlTable::getKeyValuePairs() const
{
    return mMap;
}


TomlContent::TomlContent()
    : mRootTable(std::make_unique<TomlTable>("root"))
{

}

TomlTable* TomlContent::getRootTable() const
{
    return mRootTable.get();
}

TomlTable* TomlContent::getTable(const std::string& path) const
{
    return mRootTable->getTable(path);
}

TomlReader::TomlReader()
    : mContent(std::make_unique<TomlContent>())
{

}

TomlContent* TomlReader::getContent() const
{
    return mContent.get();
}

void TomlReader::read(const Path& input_path)
{
    const auto lines = File(input_path).readLines();
    mLastSectionOffset = 0;
    mWorkingTable = mContent->getRootTable();

    for (const auto& line : lines)
    {
        processLine(line);
        mLastSectionOffset++;
    }

    mWorkingTable = nullptr;
}

void TomlReader::processLine(const std::string& line)
{
    bool in_comment{ false };
    bool in_header{ false };
    bool found_key{ false };
    std::string working_string;
    std::string key_string;
    for (auto c : line)
    {
        if (c == '#' && !in_comment)
        {
            in_comment = true;
        }
        else if (c == '[' && !in_comment && !in_header)
        {
            in_header = true;
        }
        else if (c == '=' && !in_comment && !in_header)
        {
            found_key = true;
            key_string = working_string;
            working_string = "";
        }
        else if (c == ']' && in_header)
        {
            break;
        }
        else
        {
            working_string += c;
        }
    }

    if (in_comment)
    {
        mWorkingTable->addComment({ mLastSectionOffset, working_string });
                }
    else if (in_header)
    {
        onHeader(working_string);
    }
    else if (found_key)
    {
        std::locale locale;
        key_string.erase(std::remove_if(key_string.begin(), key_string.end(), [locale](unsigned char c) {return std::isspace(c, locale); }), key_string.end());
        working_string.erase(std::remove_if(working_string.begin(), working_string.end(), [locale](unsigned char c) {return std::isspace(c, locale); }), working_string.end());

        if (working_string.size()>2 && working_string[0] == '"' && working_string[working_string.size() - 1] == '"')
        {
            working_string = working_string.substr(1, working_string.size() - 2);
        }

        onKeyValuePair(key_string, working_string);
    }
}

void TomlReader::onHeader(const std::string& header)
{
    auto new_table = std::make_unique<TomlTable>(header);
    auto table_temp = new_table.get();
    mWorkingTable->addTable(std::move(new_table));
    mWorkingTable = table_temp;
}

void TomlReader::onKeyValuePair(const std::string key, const std::string value)
{
    mWorkingTable->addKeyValuePair(key, value);
}
