import os.path
import json
import configparser
from pathlib import Path
import re

from .s_expression_parse import readFile2var, parse_sexp, convert_list_to_dicts


class filehandler:
    def __init__(self, path):
        self.path = ""
        self.filelist = []
        self.change_path(path)

    def change_path(self, newpath):
        if not os.path.isdir(newpath):
            newpath = "."
        if newpath != self.path:
            self.filelist = []
        self.path = newpath

    def GetNewFiles(self, path):
        if path != self.path:
            self.change_path(path)

        filelist = os.listdir(self.path)
        filelist.sort()
        newFiles = []
        for i in filelist:
            if i not in self.filelist and i.endswith(".zip"):
                pathtemp = os.path.join(self.path, i)
                # the file is less than 50 MB and larger 1kB
                if (os.path.getsize(pathtemp) < 1000 * 1000 * 50) and (
                    os.path.getsize(pathtemp) > 1000
                ):
                    newFiles.append(pathtemp)
                    self.filelist.append(i)
        return newFiles


class config_handler:
    def __init__(self, config_path):
        self.config = configparser.ConfigParser()
        self.config_path = config_path
        self.config_is_set = False
        try:
            self.config.read(self.config_path)
            self.config["config"]["SRC_PATH"]  # only for check
            self.config["config"]["DEST_PATH"]  # only for check
            self.config_is_set = True
        except:
            self.print("An exception occurred during import " + self.config_path)
            self.config = configparser.ConfigParser()
            self.config.add_section("config")
            self.config.set("config", "SRC_PATH", "")
            self.config.set("config", "DEST_PATH", "")

        if self.config["config"]["SRC_PATH"] == "":
            self.config["config"]["SRC_PATH"] = str(Path.home() / "Downloads")
        if self.config["config"]["DEST_PATH"] == "":
            self.config["config"]["DEST_PATH"] = str(Path.home() / "KiCad")
            self.config_is_set = False

    def get_SRC_PATH(self):
        return self.config["config"]["SRC_PATH"]

    def set_SRC_PATH(self, var):
        self.config["config"]["SRC_PATH"] = var
        self.save_config()

    def get_DEST_PATH(self):
        return self.config["config"]["DEST_PATH"]

    def set_DEST_PATH(self, var):
        self.config["config"]["DEST_PATH"] = var
        self.save_config()

    def save_config(self):
        with open(self.config_path, "w") as configfile:
            self.config.write(configfile)

    def print(self, text):
        print(text)


class KiCad_Settings:
    def __init__(self, SettingPath):
        self.SettingPath = SettingPath

    def get_sym_table(self):
        path = os.path.join(self.SettingPath, "sym-lib-table")
        return self.__parse_table__(path)

    def set_sym_table(self, libname: str, libpath: str):
        path = os.path.join(self.SettingPath, "sym-lib-table")
        self.__add_entry_sexp__(path, name=libname, uri=libpath)

    def sym_table_change_entry(self, old_uri, new_uri):
        path = os.path.join(self.SettingPath, "sym-lib-table")
        self.__update_uri_in_sexp__(path, old_uri=old_uri, new_uri=new_uri)

    def get_lib_table(self):
        path = os.path.join(self.SettingPath, "fp-lib-table")
        return self.__parse_table__(path)

    def set_lib_table_entry(self, libname: str):
        path = os.path.join(self.SettingPath, "fp-lib-table")
        uri_lib = "${KICAD_3RD_PARTY}/" + libname + ".pretty"
        self.__add_entry_sexp__(path, name=libname, uri=uri_lib)

    def __parse_table__(self, path):
        sexp = readFile2var(path)
        parsed = parse_sexp(sexp)
        return convert_list_to_dicts(parsed)

    def __update_uri_in_sexp__(
        self,
        path,
        old_uri,
        new_uri,
    ):
        with open(path, "r") as file:
            data = file.readlines()

        entry_pattern = re.compile(
            r'\s*\(lib \(name "(.*?)"\)\(type "(.*?)"\)\(uri "(.*?)"\)\(options "(.*?)"\)\(descr "(.*?)"\)\)\s*'
        )
        uri_found = False

        for index, line in enumerate(data):
            match = entry_pattern.match(line)
            if match:
                name, type_, uri, options, descr = match.groups()
                if uri == old_uri:
                    # Create a new entry with the new URI
                    new_entry = f'  (lib (name "{name}")(type "KiCad")(uri "{new_uri}")(options "{options}")(descr "{descr}"))\n'
                    print("old entry:", data[index], end="")
                    data[index] = new_entry
                    print("new entry:", data[index], end="")
                    uri_found = True
                    break

        if not uri_found:
            raise ValueError(f"URI '{old_uri}' not found in the file.")

        with open(path, "w") as file:
            file.writelines(data)

    def __add_entry_sexp__(
        self,
        path,
        name="Snapeda",
        uri="${KICAD_3RD_PARTY}/Snapeda.pretty",
        type="KiCad",
        options="",
        descr="",
    ):
        table_entry = self.__parse_table__(path)
        entries = {lib["name"]: lib for lib in table_entry}
        if name in entries:
            raise ValueError(f"Entry with the name '{name}' already exists.")

        new_entry = f'  (lib (name "{name}")(type "{type}")(uri "{uri}")(options "{options}")(descr "{descr}"))\n'

        with open(path, "r") as file:
            data = file.readlines()

        # Insert the new entry before the last bracket character
        insert_index = len(data) - 1
        data.insert(insert_index, new_entry)

        with open(path, "w") as file:
            file.writelines(data)

    def get_kicad_json(self):
        path = os.path.join(self.SettingPath, "kicad.json")

        with open(path) as json_data:
            data = json.load(json_data)

        return data

    def set_kicad_json(self, kicad_json):
        path = os.path.join(self.SettingPath, "kicad.json")

        with open(path, "w") as file:
            json.dump(kicad_json, file, indent=2)

    def get_kicad_common(self):
        path = os.path.join(self.SettingPath, "kicad_common.json")

        with open(path) as json_data:
            data = json.load(json_data)

        return data

    def set_kicad_common(self, kicad_common):
        path = os.path.join(self.SettingPath, "kicad_common.json")

        with open(path, "w") as file:
            json.dump(kicad_common, file, indent=2)

    def get_kicad_GlobalVars(self):
        KiCadjson = self.get_kicad_common()
        return KiCadjson["environment"]["vars"]

    def check_footprintlib(self, SearchLib, add_if_possible=True):
        msg = ""
        FootprintTable = self.get_lib_table()
        FootprintLibs = {lib["name"]: lib for lib in FootprintTable}

        temp_path = "${KICAD_3RD_PARTY}/" + SearchLib + ".pretty"
        if SearchLib in FootprintLibs:
            if not FootprintLibs[SearchLib]["uri"] == temp_path:
                msg += "\n" + SearchLib
                msg += " in the Footprint Libraries is not imported correctly."
                msg += "\nYou have to import the library " + SearchLib
                msg += "' with the path '" + temp_path + "' in Footprint Libraries."
                if add_if_possible:
                    msg += "\nThe entry must either be corrected manually or deleted."
                    # self.set_lib_table_entry(SearchLib) # TODO
        else:
            msg += "\n" + SearchLib + " is not in the Footprint Libraries."
            if add_if_possible:
                self.set_lib_table_entry(SearchLib)
                msg += "\nThe library " + SearchLib
                msg += " has been successfully added."
                msg += "\n##### A restart of KiCad is necessary. #####"
            else:
                msg += "\nYou have to import the library " + SearchLib
                msg += "' with the path '" + temp_path
                msg += "' in the Footprint Libraries or select the automatic option."

        return msg

    def check_symbollib(self, SearchLib: str, add_if_possible: bool = True):
        msg = ""
        SearchLib_name = SearchLib.split(".")[0]
        SearchLib_name_short = SearchLib_name.split("_")[0]

        SymbolTable = self.get_sym_table()
        SymbolLibs = {lib["name"]: lib for lib in SymbolTable}
        SymbolLibsUri = {lib["uri"]: lib for lib in SymbolTable}

        temp_path = "${KICAD_3RD_PARTY}/" + SearchLib

        if not temp_path in SymbolLibsUri:
            msg += "\n'" + temp_path + "' is not imported into the Symbol Libraries."
            if add_if_possible:
                if SearchLib_name_short not in SymbolLibs:
                    self.set_sym_table(SearchLib_name_short, temp_path)
                    msg += "\nThe library " + SearchLib
                    msg += " has been successfully added."
                    msg += "\n##### A restart of KiCad is necessary. #####"
                elif SearchLib_name not in SymbolLibs:
                    self.set_sym_table(SearchLib_name, temp_path)
                    msg += "\nThe library " + SearchLib
                    msg += " has been successfully added."
                    msg += "\n##### A restart of KiCad is necessary. #####"
                else:
                    msg += "\nThe entry must either be corrected manually or deleted."
                    # self.set_sym_table(SearchLib_name, temp_path) # TODO
            else:
                msg += "\nYou must add them manually or select the automatic option."

        return msg

    def check_GlobalVar(self, LocalLibFolder, add_if_possible=True):
        msg = ""
        GlobalVars = self.get_kicad_GlobalVars()

        def setup_kicad_common():
            kicad_common = self.get_kicad_common()
            kicad_common["environment"]["vars"]["KICAD_3RD_PARTY"] = LocalLibFolder
            self.set_kicad_common(kicad_common)

        if GlobalVars and "KICAD_3RD_PARTY" in GlobalVars:
            if not GlobalVars["KICAD_3RD_PARTY"] == LocalLibFolder:
                msg += "\nKICAD_3RD_PARTY is defined as '"
                msg += GlobalVars["KICAD_3RD_PARTY"]
                msg += "' and not '" + LocalLibFolder + "'."
                if add_if_possible:
                    setup_kicad_common()
                    msg += "\nThe entry was changed automatically."
                    msg += "\n##### A restart of KiCad is necessary. #####"
                else:
                    msg += "\nChange the entry or select the automatic option."
        else:
            msg += "\nKICAD_3RD_PARTY" + " is not defined in Environment Variables."
            if add_if_possible:
                setup_kicad_common()
                msg += "\nThe entry has been added successfully."
            else:
                msg += "\nYou must add them manually or select the automatic option."

        return msg


if __name__ == "__main__":
    import pcbnew

    Manager = pcbnew.SETTINGS_MANAGER()
    Setting = KiCad_Settings(Manager.GetUserSettingsPath())

    SearchLib = "Samacsys"
    LocalLibFolder = "~/KiCad"

    Setting.check_footprintlib(SearchLib)
    Setting.check_symbollib(SearchLib)
    Setting.check_GlobalVar(SearchLib)