import os
import json
import socket
import tempfile
import zipfile
import shutil
import VIStk
from VIStk.Structures._Version import Version


def send_to_host(project_title: str, screen_name: str) -> bool:
    """Send a screen-open request to a running Host via IPC.

    Reads the port file written by ``Host._start_ipc()`` and connects to
    the local TCP server.  Returns ``True`` if the message was delivered,
    ``False`` if no Host is running or the connection fails.

    Args:
        project_title: The VIS project title (used to locate the port file).
        screen_name:   Name of the screen to open.
    """
    safe = project_title.replace(" ", "_")
    port_file = os.path.join(tempfile.gettempdir(), f"{safe}_vis_host.port")
    if not os.path.exists(port_file):
        return False
    try:
        with open(port_file) as f:
            port = int(f.read().strip())
        with socket.create_connection(("127.0.0.1", port), timeout=1) as s:
            s.sendall(screen_name.encode("utf-8"))
        return True
    except Exception:
        return False

VISROOT = VIStk.__file__.replace("__init__.pyc","").replace("__init__.py","")

#Copied from source
#https://stackoverflow.com/a/75246706
def unzip_without_overwrite(src_path, dst_dir):
    with zipfile.ZipFile(src_path, "r") as zf:
        for member in zf.infolist():
            file_path = os.path.join(dst_dir, member.filename)
            if not os.path.exists(file_path):
                zf.extract(member, dst_dir)

def getPath()->str:
    """Searches for .VIS folder
    """
    wd = os.getcwd().replace("\\","/").split("/")
    for i in range(len(wd),0,-1):
        if os.path.exists("/".join(wd[:i])+"/.VIS/"):
            return "/".join(wd[:i])
    else:
        return None

_RESERVED_VIS_COMMANDS = {
    "-v", "-V", "-Version", "-version",
    "new", "New", "N", "n",
    "add", "Add", "a", "A",
    "stop", "Stop",
    "stitch", "Stitch", "s", "S",
    "release", "Release", "r", "R",
}

def validName(name:str):
    """Checks if provided path is a valid filename
    """
    if " " in name:
        print("Cannot have spaces in file name.")
        return False
    if "/" in name or "\\" in name:
        print("Cannot have filepath deliminator in file name.")
        return False
    if "<" in name or ">" in name or ":" in name or '"' in name or "|" in name or "?" in name or "*" in name:
        print('Invlaid ASCII characters for windows file creation, please remove all <>:"|?* from file name.')
        return False
    if name.split(".")[0] in ["CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"]:
        print(f"Filename {name} reserved by OS.")
        return False
    if "" == name:
        print("Must provide a name for file.")
        return False
    else:
        return True
    

class VINFO():
    """Overarching control structure within the /.VIS/ project folder"""
    def __init__(self):
        """Creates an overarching control stricture within the /.VIS/ project folder
        """
        #Begin Project Creation If Project Is Not Found
        if getPath() == None:
            wd = os.getcwd()
            os.mkdir(wd+"\\.VIS")

            unzip_without_overwrite(VISROOT+"Form.zip",wd)
            print(f"Copied structure to {wd}")

            shutil.copytree(VISROOT+"Templates",wd+"/.VIS/Templates",dirs_exist_ok=True)
            print(f"Loaded default templates into {wd}/.VIS/Templates/")

           
            #DO NOT MESS WITH THE TEMPLATE HEADERS

            folder_name = os.path.basename(wd)
            while True:
                title = input(f"Enter a name for the VIS project (Enter for '{folder_name}'): ").strip()
                if title == "":
                    title = folder_name
                if title in _RESERVED_VIS_COMMANDS:
                    print(f"'{title}' is a reserved VIS command. Please choose a different name.")
                elif not validName(title):
                    pass  # validName already printed the error
                else:
                    break
            self.title:str = title
            """Name of the Project"""
            info = {}
            info[self.title] = {}
            info[self.title]["Screens"]={}
            info[self.title]["defaults"]={}
            info[self.title]["defaults"]["icon"]="VIS"#default icon
            info[self.title]["defaults"]["default_screen"] = None
            self.d_icon = "VIS"
            info[self.title]["metadata"]={}
            comp = input("What company is this for(or none)? ")
            if not comp in ["none","None"]:
                info[self.title]["metadata"]["company"] = comp
                self.company = comp
            else:
                info[self.title]["metadata"]["company"] = None
                self.company = None

            copyright_in = input("Enter a copyright string (or none to use company name): ")
            if copyright_in in ["none", "None", ""]:
                info[self.title]["metadata"]["copyright"] = comp if comp not in ["none","None"] else None
                self.copyright = self.company
            else:
                info[self.title]["metadata"]["copyright"] = copyright_in
                self.copyright = copyright_in

            version = input("What is the initial version for the project (0.0.1 default): ")
            vers = version.split(".")
            if len(vers)==3:
                if vers[0].isnumeric() and vers[1].isnumeric() and vers[2].isnumeric():
                    self.Version = Version(version)
                else:
                    self.Version = Version("0.0.1")
            else:
                self.Version = Version("0.0.1")
            info[self.title]["metadata"]["version"] = str(self.Version)
            info[self.title]["release_info"] = {}
            info[self.title]["release_info"]["location"] = "./dist/"
            info[self.title]["release_info"]["hidden_imports"] = ["PIL._tkinter_finder"]

            info[self.title]["host"] = {"script": ".VIS/Host.py"}

            with open(wd+"/.VIS/project.json","w") as f:
                json.dump(info,f,indent=4)
            print(f"Setup project.json for project {self.title} in {wd}/.VIS/")

            # Generate Host.py inside .VIS/ (not user-editable)
            host_template = VISROOT + "Templates/host.txt"
            host_script = wd + "/.VIS/Host.py"
            shutil.copyfile(host_template, host_script)
            with open(host_script, "r") as f:
                host_text = f.read()
            host_text = host_text.replace("<icon>", "VIS")
            with open(host_script, "w") as f:
                f.write(host_text)
            print(f"Created .VIS/Host.py in {wd}")

        #Get VIS Root location
        self.p_vis = VIStk.__file__.replace("__init__.pyc","").replace("__init__.py","")
        """The Installed Location of VIStk"""
        
        #Project root location
        self.p_project = getPath() 
        """The Location of the Project"""
        self.p_vinfo = self.p_project + "/.VIS"
        """The Location of the Project Info Folder `/.VIS`"""
        self.p_sinfo = self.p_vinfo + "/project.json"
        """The Path of the `project.json` file"""
        with open(self.p_sinfo,"r") as f: 
            info = json.load(f)
            self.title = list(info.keys())[0]
            """Name of the Project"""
            self.Version = Version(info[self.title]["metadata"]["version"])
            """Project Version Number"""
            self.company = info[self.title]["metadata"]["company"]
            """Project Copyright Owner [Company]"""
            self.copyright = info[self.title]["metadata"].get("copyright", self.company)
            """Project Copyright String"""
            self.default_screen: str | None = info[self.title]["defaults"].get(
                "default_screen",
                info[self.title].get("default_screen"),  # backwards-compat
            )
            """Name of the screen opened when the Host restores from the tray."""
            
        self.p_screens = self.p_project +"/Screens"
        """The Path to the `/Screens` Folder"""
        self.p_modules = self.p_project +"/modules"
        """The Path to the `/modules` Folder"""
        self.p_templates = self.p_vinfo + "/Templates"
        """The Location of the Project `/Templates` Folder"""
        self.p_icons = self.p_project + "/Icons"
        """The Location of the Project `/Icons` Folder"""
        self.p_images = self.p_project + "/Images"
        """The Location of the Project  `/Images` Folder"""

    def restoreAll(self):
        """Undoes screen isolation"""
        with open(self.p_sinfo,"r") as f:
            info = json.load(f)

        for i in info[self.title]["Screens"]:
            if info[self.title]["Screens"][i]["release"] is None:
                info[self.title]["Screens"][i]["release"] = True

        with open(self.p_sinfo,"w") as f:
            json.dump(info,f,indent=4)