from .blender_dialogue import openDialogue
from .__meta__ import getVersion
import bpy
import os
import shutil
import subprocess
import sys

from .utils_debug import debug_err, debug_log
from .settings_scene import tryGetMainSceneSettings

def isBlenderVersionSupported():
    return bpy.app.version >= (4, 0, 0)

def getMinSupportedBlenderVersionString():
    return "4.0.x"

def getChangeDirectoryCommand(path):
    if sys.platform == "win32":
        partitionKey = path[0] + ":" + " && "
    else:
        partitionKey = ""
    return partitionKey + " cd \"" + path + "\""

def getOpenDirectoryCommand(path):
    import sys
    if sys.platform == "win32":
        return "start \"\" \"" + path + "\""
    elif sys.platform == "darwin":
        return "open \"" + path + "\""
    else:
        return "xdg-open \"" + path + "\""

def deleteDirectoryRecursive(path:str):
    print("DELETE DIR", path)
    if os.path.exists(path):
        if isWindows():
            subprocess.call("rmdir /S /Q \"" + path + "\"", shell=True)
        else:
            subprocess.call("rm -rf \"" + path + "\"", shell=True)

def isTempProject():
    path = getWebProjectPath()
    return path.startswith(getTempWebProjectPath())

def webProjectExists():
    path = getWebProjectPath()
    if path is None: 
        return False
    if not os.path.exists(path):
        return False
    # the project directory must either contain a package.json or a index.html file
    packageJsonPath = os.path.join(path, "package.json")
    if not os.path.exists(packageJsonPath):
        return False
    return True

def getIsVanillaJsProject():
    path = getWebProjectPath()
    if path is None: 
        return False
    if not os.path.exists(path):
        return False
    # the project directory must either contain a package.json or a index.html file
    indexHtmlPath = os.path.join(path, "index.html")
    if not os.path.exists(indexHtmlPath):
        return False
    return True


def getTempWebProjectPath():
    import tempfile
    tempDir = tempfile.gettempdir()
    tempWebProjectPath = os.path.join(tempDir, "Needle", "Blender", "WebProject")
    # make sure the directory exists
    if not os.path.exists(tempWebProjectPath):
        os.makedirs(tempWebProjectPath)
    return tempWebProjectPath

def validateTempWebProject():
    tempProjectPath = getTempWebProjectPath()
    if not os.path.exists(tempProjectPath):
        return False
    indexPath = os.path.join(tempProjectPath, "index.html")
    if not os.path.exists(indexPath):
        return False
    return True

def clearTempWebProject(force:bool = False):
    try:
        debug_log("Clearing temp web project")
        tempProjectPath = getTempWebProjectPath()
        if os.path.exists(tempProjectPath):
            if force == False:
                # check if the directory was created less than 48 hours ago
                import time
                creationTime = os.path.getctime(tempProjectPath)
                currentTime = time.time()
                if currentTime - creationTime < 60 * 60 * 48:
                    return

            import shutil
            debug_log("Clearing temp web project at " + tempProjectPath)
            # first try to delete node_modules because it may fail if files are locked
            # and if node_modules are incomplete it's not as bad as e.g. index.html is missing
            node_modules_path = os.path.join(tempProjectPath, "node_modules")
            if os.path.exists(node_modules_path):
                shutil.rmtree(node_modules_path)
            shutil.rmtree(tempProjectPath)
            os.makedirs(tempProjectPath)
            debug_log("Cleared temp web project at " + tempProjectPath)
    except Exception as e:
        debug_err("Failed to clear temp web project at " + tempProjectPath, e)
    pass


def getWebProjectPath(debug:bool = False):
    settings = tryGetMainSceneSettings()
    if settings and settings.projectPath:
        if debug: print("> WebProjectPath from scene settings:", settings.projectPath)
        # remove leading slashes
        path = settings.projectPath
        # we need to make it absolute (e.g. the filepicker saves the file as //mydirectory\ on windows if we have a blend file and the directory is next to it)
        path = bpy.path.abspath(path)
        path = os.path.abspath(path)
        path = path.replace("\\", "/")
        if debug: print("> WebProjectPath from scene settings (absolute):", path)

        # # make an absolute path if the path is relative
        # if path.startswith("/") or path.startswith("\\"):
        #     currentPath = bpy.path.abspath("//")
        #     if debug: print("> WebProjectPath - current path:", currentPath)
        #     path = os.path.join(currentPath, path[1:])
        #     if debug: print("> WebProjectPath from scene settings (absolute):", path)
        # while path.endswith("/") or path.endswith("\\"):
        #     if debug: print("> WebProjectPath - removing last char:", path)
        #     path = path[:-1]
        return path
    return getTempWebProjectPath()

def makeRelativeToBlenderFile(path):
    currrentPath = bpy.path.abspath("//")
    if not currrentPath:
        return path
    return os.path.relpath(path, currrentPath)

def makeAbsolute(path):
    currrentPath = bpy.path.abspath("//")
    if not currrentPath:
        return path
    return os.path.join(currrentPath, path)

def getSchemesDirectory(webDirectory:str):
    return os.path.join(webDirectory, "blender.codegen")

def isSchemeFile(path):
    return path.endswith(".component.json")

def projectIsSaved():
    dir = bpy.path.abspath("//")
    if len(dir) <= 0:
        return False
    return True


def getTemplatePath(name):
    return os.path.join(os.path.dirname(__file__), 'templates', name)


def copy_and_overwrite(from_path, to_path):
    try:
        if os.path.exists(to_path):
            shutil.rmtree(to_path)
        # skip .meta files
        shutil.copytree(from_path, to_path,
                        ignore=shutil.ignore_patterns('*.meta', 'node_modules'))
    except Exception as e:
        debug_err(e)
        raise e


def openUrl(url):
    # open with default app
    if isWindows():
        os.startfile(url)
    elif isMac():
        subprocess.call(('open', url))
    else:
        import webbrowser
        webbrowser.open(url)


def clearConsole():
    if isWindows():
        os.system("cls") # on windows
    else:
        os.system("clear") # on linux / os x

def generateIdFromString(str) -> int:
    return int.from_bytes(str.encode(), 'little') % 999999


def generateRandomStringId(length):
    import random
    import string
    letters = string.ascii_lowercase
    result = ''
    for i in range(length):
        result += random.choice(letters)
    return result

def openChangelog():
    addonPath = os.path.dirname(__file__)
    changelogPath = os.path.join(addonPath, "CHANGELOG.md")
    openUrl("file://" + changelogPath)


import requests
import time

lastRequestTime = 0
lastVersionResult = None

def getLatestVersion():
    global lastRequestTime
    global lastVersionResult
    try:
        if lastRequestTime > 0:
            if time.time() - lastRequestTime < 600:
                return lastVersionResult
        print("Checking for updates...")
        x = requests.get('https://engine.needle.tools/downloads/blender/latest-version')
        lastRequestTime = time.time()
        lastVersionResult = x.text
        return x.text
    except:
        lastRequestTime = time.time()
        return None


def tryParseInt(value:str):
    try:
        return int(value), True
    except ValueError:
        return value, False

def getAttrInsensitive(obj, attr):
    if hasattr(obj, attr):
        return getattr(obj, attr)
    for key in obj.keys():
        if key.lower() == attr.lower():
            return getattr(obj, key)
    return None



def copyToClipboard(txt):
    bpy.context.window_manager.clipboard = txt
    # cmd = None
    # if  isWindows():
    #     cmd='echo '+txt.strip()+'|clip'
    # elif isMac():
    #     cmd='echo '+txt.strip()+'|pbcopy'
    # elif isLinux():
    #     cmd='echo '+txt.strip()+'|xclip'
    # if cmd is not None:
    #     return subprocess.check_call(cmd, shell=True)

def isWindows():
    return os.name == 'nt'

def isMac():
    return os.name == 'mac' or sys.platform == 'darwin'

def isLinux():
    return os.name == 'posix'



def showMessage(message:str or list = "", title = "Message Box", icon = 'INFO'):
    def draw(self, context):
        # split "\n" into separate lines
        # if not isinstance(message, list):
        #     message = message.split("\n")

        if isinstance(message, list):
            for line in message:
                if not isinstance(line, str):
                    line = str(line)
                self.layout.label(text=line)
        else:
            msg = message
            if not isinstance(msg, str):
                msg = str(msg)
            self.layout.label(text=msg)
    bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)


def openFile(path:str):
    if isWindows():
        os.startfile(path)
    else:
        opener ="open" if sys.platform == "darwin" else "xdg-open"
        subprocess.call([opener, path])

def openFolder(path:str):
    openFile(path)


def getCommand(cmd:str):
    if isWindows():
        return cmd
    return f"`which {cmd}`"



def decode_text(bytes:bytes) -> str:
    """
    Try to decode a bytes object to a string using different encodings
    """
    encodings = ['utf-8', 'cp1252', 'iso-8859-1', 'latin1']
    for encoding in encodings:
        try:
            str = bytes.decode(encoding)
            return str
        
        except UnicodeDecodeError:
            continue
    return ""