import os
import subprocess
import sys
import threading

from .utils_debug import getLogFilePath
from .blender_windows import setStatus
import bpy

startedExternalProcesses = []

def stopExternalProcesses():
    print("Kill started processes", len(startedExternalProcesses))
    for p in startedExternalProcesses:
        try:
            p.send_signal(subprocess.signal.CTRL_C_EVENT)
        finally:
            p.kill()


# write to vscode console
# sys.stdout.write("error")

stdout_result = 1
stderr_result = 1


def hideCommandSecrets(command):
    try:
        # all parts wrapped in quotes
        rgx = r"\"(.+?)\""
        import re
        matches = re.findall(rgx, command)
        for match in matches:
            # replace with ***
            command = command.replace(match, "***")
        return command
    except Exception as e:
        print("Error in hideCommandSecrets", e)
        return command

def stdout_thread(pipe, debug:bool=False, callback = None):
    global stdout_result
    lineBuffer = ""
    while True:
        try:
            out : bytes = pipe.stdout.read(1)
            stdout_result = pipe.poll()
            # bytes to string
            out = out.decode("utf-8", "ignore")

            if out == '' and stdout_result is not None:
                break
            
            if out != '':
                if(len(out) > 0):
                    if out == '\r' or out == '\n':
                        if debug:
                            print(lineBuffer)
                        if callback is not None:
                            callback(lineBuffer)
                        lineBuffer = ""
                    else:
                        lineBuffer += out
                sys.stdout.flush()
        except Exception as e:
            print("Error in stdout_thread", e)
            break


def stderr_thread(pipe, debug:bool):
    global stderr_result
    lineBuffer = ""
    while True:
        err = pipe.stderr.read(1)
        stderr_result = pipe.poll()
        err = err.decode("utf-8", "ignore")
        
        if err == '' and stderr_result is not None:
            break

        if err != '':
            if(len(err) > 0):
                if err == '\r' or err == '\n':
                    if debug: print("ERR: " + lineBuffer)
                    lineBuffer = ""
                else:
                    lineBuffer += err 
            sys.stdout.flush()


def exec_command(command, cwd=None, debug=False, onMessage=None, onClose=None):

    if onMessage == True:
        onMessage = printMessage
    else:
        onMessage = None

    onCloseCallback = None
    if callable(onClose):
        onCloseCallback = onClose

    if onClose == True:
        onClose = printMessage
    elif isinstance(onClose, str):
        onClose = lambda: printMessage("Finished: " + onClose)

    if debug: printMessage("START: " + hideCommandSecrets(command))
    p = subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, shell=True
    )
    startedExternalProcesses.append(p)

    out_thread = threading.Thread(name='stdout_thread', target=stdout_thread, args=(p,debug, onMessage))
    err_thread = threading.Thread(name='stderr_thread', target=stderr_thread, args=(p,debug))

    err_thread.start()
    out_thread.start()

    bpy.app.timers.register(lambda: checkProcess(p, onCloseCallback, command, debug), first_interval=0.1)

    # out_thread.join()
    # err_thread.join()

    # return stdout_result + stderr_result
    return p

def checkProcess(p, onClose, cmd, debug = False):
    secretCommand = hideCommandSecrets(cmd)
    try:
        if p.poll() is not None:
            if debug: printMessage("FINISHED: " + secretCommand)
            if onClose is not None:
                onClose()
            return None
    except Exception as e:
        print("Error in checkProcess", e)
        return None
    return 0.1


def printMessage(msg):
    isEmpty = len(msg) == 0
    msg = "> " + msg
    print(msg)
    if not isEmpty:
        setStatus(msg)


def exec_command_standalone(command, cwd=None, log_file_path=None):
    if (cwd is None):
        cwd = os.getcwd()
    
    cmd = ""
    if sys.platform.startswith('win'):
        cmd = "start \"Needle Engine\" cmd /c \""
        cmd += command
        cmd += "\""
    elif sys.platform.startswith('darwin'):
        command = command.replace("\"", "\\\"")
        cmd = 'osascript -e \'tell application "Terminal" to do script "' + "cd \\\"" + cwd + "\\\" && " + command + '"\''
    elif sys.platform.startswith('linux'):
        # TODO test on Linux
        cmd = "gnome-terminal -- bash -c \"" + command + "\""
    
    print("EXECUTE: " + cmd)
    # if cwd is not None:
    #     os.chdir(cwd)
    # cl = os.popen(cmd)
    cl = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd);

    # out, err = cl.communicate() 

    return cl

def getLogOutputToFileCommand():
    log_path = getLogFilePath()
    if sys.platform.startswith('win'):
        return ">> \"" + log_path + "\" 2>&1"
    elif sys.platform.startswith('darwin'):
        return ">> \"" + log_path + "\" 2>&1"

