import re
import subprocess
import os
from enum import Enum


class CfgVar:
    def __init__(self, key, value=None, value_type='STRING', comment=""):
        self.key = key
        self.value = value
        self.value_type = value_type
        self.comment = comment

    def __repr__(self):
        return f"CfgVar(key='{self.key}', value={self.value}, value_type='{self.value_type}', comment='{self.comment}')"


TokenType = Enum('TokenType', ['BRACE_OPEN', 'BRACE_CLOSE', 'VARIABLE', 'NAMESPACE'])


class Token:
    def __init__(self, type):
        self.type = type

    def __repr__(self):
        return str(self.type)


class Variable(Token):
    def __init__(self, name, comment=""):
        super().__init__(TokenType.VARIABLE)
        self.name = name
        self.comment = comment

    def __repr__(self):
        return f"VAR {self.name} [{self.comment}]"


class Namespace(Token):
    def __init__(self, name):
        super().__init__(TokenType.NAMESPACE)
        self.name = name

    def __repr__(self):
        return f"NAMESPACE {self.name}"


def tokenize(file_path):
    text = ""
    if os.path.exists(file_path):
        text = open(file_path, "r", encoding="utf-8").read()

    tokens = []
    it = text.__iter__()

    try:
        char = it.__next__()
        while True:
            if char == "{":
                tokens.append(Token(TokenType.BRACE_OPEN))
                char = it.__next__()
            elif char == "}":
                tokens.append(Token(TokenType.BRACE_CLOSE))
                char = it.__next__()
            elif char.isalpha():
                name = char

                char = it.__next__()
                while char.isalnum() or char == "_" or char == ".":
                    name += char 
                    char = it.__next__()

                char = it.__next__()
                while char in (" ", "\t"):
                    char = it.__next__()

                if char == "=":
                    tokens.append(Variable(name))

                    char = it.__next__()
                    while char != ";":
                        char = it.__next__()

                    char = it.__next__()
                    while char in (" ", "\t"):
                        char = it.__next__()

                    if char == "#":
                        
                        char = it.__next__()

                        while char in (" ", "\t"):
                            char = it.__next__()

                        comment = ""

                        while char != "\n":
                            comment += char
                            char = it.__next__()

                        if tokens[-1].type == TokenType.VARIABLE:
                            tokens[-1].comment = comment    

                        char = it.__next__()
                else:
                    tokens.append(Namespace(name))
            elif char == "#":
                while char != "\n":
                    char = it.__next__()
                char = it.__next__()
            else:
                char = it.__next__()
    except StopIteration as e:
        return tokens


def parse(file_path):
    stack = []

    data = dict()

    tokens = tokenize(file_path)
    it = tokens.__iter__()

    try:
        while True:
            token = it.__next__()
            if token.type == TokenType.NAMESPACE:
                stack.append(token.name)

            if token.type == TokenType.BRACE_CLOSE:
                stack.pop()

            if token.type == TokenType.VARIABLE:
                if stack:
                    path = '.'.join(stack) + f'.{token.name}'
                else:
                    path = token.name
                data[path] = CfgVar(token.name, comment=token.comment)
    except StopIteration as e:
        return data


def load_config(file_path):
    config = parse(file_path)

    output = subprocess.run(["config-parser/parser-cli", file_path], capture_output=True, text=True)

    for line in output.stdout.split("\n"):
        match = re.match(r" > (\w+) '(.*)' = *(.*)", line)
        if match:
            value_type, path, value = match.groups()
            config[path].value = get_value_from_str(value, value_type)
            config[path].value_type = value_type

    return config

def construct_nested_dict(config):
    config_nested = dict()
    for path in config:
        nss = path.split(".")

        cur_ns = config_nested

        for ns in nss[:-1]:
            if ns not in cur_ns:
                cur_ns[ns] = {}

            cur_ns = cur_ns[ns]

        cur_ns[nss[-1]] = config[path]
    return config_nested

def get_str_from_value(value, value_type):
    if not value:
        if value_type == 'STRING':
            return '""'
        elif value_type == 'INT':
            return '0'
        elif value_type == 'DOUBLE':
            return '0.0'
        elif value_type == 'BOOLEAN':
            return 'false'
    else:
        if value_type == 'STRING':
            return f'"{value}"'
        elif value_type == 'BOOLEAN':
            return str(value).lower()
        elif value_type == 'DOUBLE':
            return "{:.7f}".format(float(value))
    return str(value)

def get_value_from_str(string, value_type):
    if not string:
        if value_type == 'STRING':
            return ""
        elif value_type == 'INT':
            return 0
        elif value_type == 'DOUBLE':
            return 0.0
        elif value_type == 'BOOLEAN':
            return False
    else:
        if value_type == 'STRING':
            return string
        elif value_type == 'BOOLEAN':
            return string == 'true'
        elif value_type == 'DOUBLE':
            return float(string)
        elif value_type == 'INT':
            return int(string)

def write_namespace(file, ns, ns_name, indent):
    file.write("\n")
    file.write(" " * indent + ns_name + "\n")
    file.write(" " * indent + "{\n")
    for key in ns:
        if isinstance(ns[key], dict):
            write_namespace(file, ns[key], key, indent + 4)
        else:
            file.write(" " * (indent + 4) + f"{key} = {get_str_from_value(ns[key].value, ns[key].value_type)};\n")
    file.write(" " * indent + "}\n")
    file.write("\n")

def dump_config(config, file_path):
    config_nested = construct_nested_dict(config)

    indent = 0
    with open(file_path, "w", encoding="utf-8") as file:
        for key in config_nested:
            if isinstance(config_nested[key], dict):
                write_namespace(file, config_nested[key], key, indent)
            else:
                file.write(f"{key} = {get_str_from_value(config_nested[key].value, config_nested[key].value_type)};\n")


if __name__ == '__main__':
    print(load_config('config.txt'))