import os, sys, subprocess, shutil, time, re, random, json

def runQnxCommand(cmd):
    return subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

def runQnxCommandWait(cmd):
    p = runQnxCommand(cmd)
    out, err = p.communicate()
    return out, err

def sendPPSMessage(ppsPath, message, user):
    message = message.serialize().decode('UTF-8').replace('"', '\\"')
    command = "echo 'exec 5<> %s\nprint \"%s\" >&5\nwhile :; do\nreply=$(cat <&5)\nif [ -n \"${reply}\" ]; then\necho \"${reply}\"\nbreak\nfi\nsleep 1\ndone\nexec 5<&-' | %s | grep dat:json:" % (ppsPath, message, user)
    result, _ = runQnxCommandWait(command)
    return result

def getImpersonation(type, user):
    usercommand = "";
    if type == "user":
        usercommand = usercommand + "__";
    elif type == "group":
        usercommand = usercommand + "g_";
    return "/q/" + usercommand + user

def ledControl(ledData):
    if ledData["blue"]:
        runQnxCommandWait("echo 'echo \"blue::on\" >> /pps/services/led/control' | /q/g_led_control")
    else:
        runQnxCommandWait("echo 'echo \"blue::off\" >> /pps/services/led/control' | /q/g_led_control")
    
    if ledData["red"]:
        runQnxCommandWait("echo 'echo \"red::on\" >> /pps/services/led/control' | /q/g_led_control")
    else:
        runQnxCommandWait("echo 'echo \"red::off\" >> /pps/services/led/control' | /q/g_led_control")

    if ledData["green"]:
        runQnxCommandWait("echo 'echo \"green::on\" >> /pps/services/led/control' | /q/g_led_control")
    else:
        runQnxCommandWait("echo 'echo \"green::off\" >> /pps/services/led/control' | /q/g_led_control")

def getBatteryInfo():
    result, _ = runQnxCommandWait("echo 'cat /pps/system/BattMgr/status' | /q/g_nto | grep 'BatteryInfo:json:'")
    if not result:
        return {}
    result = result.decode().strip().replace("BatteryInfo:json:", "")
    result = json.loads(result)
    return result;

def readFileAs(path, user="/q/g_nto"):
    randomString = ''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for i in range(8))
    out, _ = runQnxCommandWait('echo "echo "==__START%s==__";cat %s;echo "==__END%s=="" | %s' % (randomString, path, randomString, user))
    if not out:
        return ""
    out = out.decode().strip().split("==__START%s==__\n" % randomString)[1].split("\n==__END%s==" % randomString)[0].strip()
    return out

def writeFileAs(path, content, user="/q/g_nto"):
    content = content.replace("'", "'\\''")
    content = content.replace("\n", "\\n")
    content = content.replace('"', '\\"')
    runQnxCommandWait('echo "echo \'%s\' > %s" | %s' % (content, path, user))

def addToFileAs(path, content, user="/q/g_nto"):
    content = content.replace("'", "'\\''")
    content = content.replace("\n", "\\n")
    content = content.replace('"', '\\"')
    runQnxCommandWait('echo "echo \'%s\' >> %s" | %s' % (content, path, user))

def prependToFileAs(path, content, user="/q/g_nto"):
    existingContent = readFileAs(path, user)
    writeFileAs(path, content + "\n" + existingContent, user)

def replaceInFileAs(path, searchString, replaceString, user="/q/g_nto"):
    content = readFileAs(path, user)
    content = content.replace(searchString, replaceString)
    writeFileAs(path, content, user)
    
def runAs(command, user="/q/g_nto"):
    command = command.replace("'", "'\\''")
    command = command.replace('"', '\\"')
    command = command.replace("$", "\\$")
    return runQnxCommandWait('echo "%s" | %s' % (command, user));

def setupOnStartup():
    content = "TERM=xterm\nexport PYTHONHOME=/usr\ncd /accounts/1000/shared/documents\npython3.2 ../.installer/main.py"
    writeFileAs("/accounts/1000/appdata/com.example.Term49.gYABgFEqMSpja5S6kL.DWpWmq50/data/.profile", content, "/q/__apps");

def enableDevMode():
    content = "[n]debug_token_author::\n[n]debug_token_expiration::\n[n]debug_token_installed:b:false\n[n]debug_token_timeout::10d\n[n]debug_token_valid:b:\n[n]debug_token_validation_error::no debug token found\n[n]debug_token_validation_error_code:n:1\n[n]dev_mode_enabled:b:true\n[n]dev_mode_expiration::10d\n[n]dev_mode_waiting:b:true"
    writeFileAs("/pps/system/development/devmode", content, "/q/__pps")
    runQnxCommandWait("echo 'echo \"devmode::enabled\" >> /pps/services/devmode/control' | /q/g_nto")

def getChargerInfo():
    result, _ = runQnxCommandWait("echo 'cat /pps/system/BattMgr/status' | /q/g_nto | grep 'ChargerInfo:json:'")
    if not result:
        return {}
    result = result.decode().strip().replace("ChargerInfo:json:", "")
    result = json.loads(result)
    return result;

def getInputSourceInfo():
    result, _ = runQnxCommandWait("echo 'cat /pps/system/BattMgr/status' | /q/g_nto | grep 'InputSourceInfo:json:'")
    if not result:
        return {}
    result = result.decode().strip().replace("InputSourceInfo:json:", "")
    result = json.loads(result)
    return result;

def generateRandomString(length):
    letters = "abcdefghijklmnopqrstuvwxyz"
    result = ''.join(random.choice(letters) for i in range(length))
    return result

def getPressedKeys():
    result,_ = runQnxCommandWait("echo 'cat /pps/system/buttons/status' | /q/g_nto | grep 'bid_'")
    if not result:
        return []
    result = result.decode().strip().split("\n")
    keys = {}
    for line in result:
        if "bid_" in line:
            splitLine = line.split("::")
            if len(splitLine) > 1:
                key = splitLine[0].strip()
                status = splitLine[1].strip() == "b_down"
                if key == "bid_minus":
                    keys["volumeDown"] = status
                elif key == "bid_plus":
                    keys["volumeUp"] = status
                elif key == "bid_playpause":
                    keys["playPause"] = status
                elif key == "bid_power":
                    keys["power"] = status
    return keys

def showProgress(id="progress", title="Test", message="message", buttons=None):
    data = {
        "className": "ProgressToast",
        "desc_obj": {
            "titleText": title,
            "messageText": message,
            "progress": 0,
            "groupId": None
        }
    }

    escapedJson = json.dumps(data).replace('"', '\\"')
    content = "msg::show"
    content += "\\nid::%s" % id
    content += "\\ndat:json:%s" % escapedJson
    command = "echo 'exec 5<> /pps/services/dialog/system-control\nprint \"%s\" >&5\nwhile :; do\nreply=$(cat <&5)\nif [ -n \"${reply}\" ]; then\necho \"${reply}\"\nbreak\nfi\nsleep 1\ndone\nexec 5<&-' | /q/g_nto | grep dat:json:" % content
    return runQnxCommand(command)

def switchZone(radio = True, os = True):
    data = {
        "radio":"true" if radio else "false",
        "os":"true" if os else "false"
    }
    escapedJson = json.dumps(data).replace('"', '\\"')
    identity = generateRandomString(8)
    content = "@%s\n" % identity
    content += "action::switchzone\n"
    content += "session::0\n"
    content += "zone:json:%s\n" % escapedJson
    command = "echo 'exec 5<> /pps/system/installer/upd/current/%s\nprint \"%s\" >&5\nwhile :; do\nreply=$(cat <&5)\nif [ -n \"${reply}\" ]; then\necho \"${reply}\"\nbreak\nfi\nsleep 1\ndone\nexec 5<&-' | /q/__upd | grep result::" % (identity, content)
    (out, err) = runQnxCommandWait(command)
    return out

def setupWatch(file, receipient, timeout):
    data = {
        "object": file,
        "exec": receipient,
        "timeout": timeout
    }
    escapedJson = json.dumps(data).replace('"', '\\"')
    content = "msg::watch\n"
    content += "dat:json:%s" % escapedJson
    content += "\nmsg::start"
    command = "echo 'exec 5<> /pps/system/ppsnetd/control\nprint \"%s\" >&5\nwhile :; do\nreply=$(cat <&5)\nif [ -n \"${reply}\" ]; then\necho \"${reply}\"\nbreak\nfi\nsleep 1\ndone\nexec 5<&-' | /q/__pps | grep msg::" % content
    (out, err) = runQnxCommandWait(command)
    print("Watch setup output:", out.decode().strip())
    return out

def showDialog(id="dlg", title="Test", message="message", buttons=None):
    data = {
        "className": "AlertDialog",
        "desc_obj": {
            "priority": True,
            "buttons": buttons if buttons else [
                {
                    "label": "Ok",
                }
            ],
            "titleText": title.replace('"', '\\"').replace("'", "\\'").replace("\n", "\\n"),
            "messageText": message.replace('"', '\\"').replace("'", "\\'").replace("\n", "\\n"),
            "groupId": None
        }
    }

    escapedJson = json.dumps(data).replace('"', '\\"')
    content = "msg::show"
    content += "\\nid::%s" % id
    content += "\\ndat:json:%s" % escapedJson
    command = "echo 'exec 5<> /pps/services/dialog/system-control\nprint \"%s\" >&5\nwhile :; do\nreply=$(cat <&5)\nif [ -n \"${reply}\" ]; then\necho \"${reply}\"\nbreak\nfi\nsleep 1\ndone\nexec 5<&-' | /q/g_nto | grep dat:json:" % content
    result, _ = runQnxCommandWait(command)
    jsonString = result.decode().strip().replace("dat:json:", "")
    jsonResult = json.loads(jsonString)
    return jsonResult
    
def fileExistsAs(path, user):
    out, _ = runAs('[ ! -e %s ]; echo RESULT::$?' % path, user)
    if out:
        result = out.decode().strip()
        match = re.search(r'RESULT::(\d+)', result)
        return match.group(1) == "1"
    return False

def fileExistsAsAny(path, users):
    for user in users:
        if fileExistsAs(path, user):
            return True
    return False

def getAppId(appSubStr):
    out, _ = runQnxCommandWait('echo "ls /apps" | /q/g_nto')
    for line in out.decode().splitlines():
        if appSubStr in line:
            return line.strip()
    return ""

def setPermanentFlag(appId, state):
    content = 'action::update_app_metadata\npackage_id::%s\nmetadata:json:{"permanent":"%s"}\n' % (appId, "true" if state else "false")
    path = os.path.join("/pps/system/installer/upd/current", "app_metadata.%s" % appId)
    writeFileAs(path, content, "/q/__upd")

def getPackageId(appSubStr):
    aID = getAppId(appSubStr)
    if not aID:
        return ""
    out, _ = runQnxCommandWait('echo "cat /apps/%s/META-INF/MANIFEST.MF" | /q/__apps | grep "Package-Id"' % aID)
    match = re.search(r'Package-Id.*:\s*(.*)', out.decode())
    return match.group(1).strip() if match else ""

def getAppIdByName(appName):
    out, _ = runQnxCommandWait('echo "cat %s" | /q/g_nto | grep %s' % ("/pps/system/installer/appdetails/.all", appName))
    if not out:
        return ""
    match = re.split(r':json:', out.decode(), 1)
    return match[0].strip() if len(match) > 1 else ""

def launchApp(appStr):
    aID = getAppId(appStr)
    if not aID:
        aID = getAppIdByName(appStr)
        if not aID:
            return
    runQnxCommandWait('echo "echo \'msg::launchApp\\ndat::%s\' >> /pps/system/navigator/control" | /q/g_nto' % aID)

def invokeFocusApp(serviceName):
    runQnxCommandWait('echo "echo \'msg::invoke\\ndat::%s://__focus__\' >> /pps/services/navigator/control" | /q/g_nto' % serviceName)

# print "msg::start_system\ndat::${android_player}\n" >> "/pps/services/launcher/control"
def startSystem(systemStr):
    aID = getAppId(systemStr)
    if not aID:
        aID = getAppIdByName(systemStr)
        if not aID:
            return
    runQnxCommandWait('echo "echo \'msg::start_system\\ndat::%s\\n\' >> /pps/services/launcher/control" | /q/g_nto' % aID)
    
def stopSystem(systemStr):
    aID = getAppId(systemStr)
    if not aID:
        aID = getAppIdByName(systemStr)
        if not aID:
            return
    runQnxCommandWait('echo "echo \'msg::stop_system\\ndat::%s\\n\' >> /pps/services/launcher/control" | /q/g_nto' % aID)

def terminateApp(appStr):
    aID = getAppId(appStr)
    if not aID:
        aID = getAppIdByName(appStr)
        if not aID:
            return
    runQnxCommandWait('echo "echo \'msg::terminateApp\\ndat::%s\' >> /pps/system/navigator/control" | /q/g_nto' % aID)

def vibrate(duration):
    if duration <= 0:
        return
    runQnxCommandWait('echo "echo \'@vibrator\\nduration:n:%s\\nintensity:n:1\\ntime_left:n:0\' >> /pps/services/vibrator" | /q/g_nto' % duration)

def showLockScreenMessage(message):
    if not message:
        return
    escapedMessage = message.replace('"', '\\"')
    escapedMessage = escapedMessage.replace("'", "\\'")
    escapedMessage = escapedMessage.replace("\n", "\\n")
    print("Showing lock screen message:", escapedMessage)
    runQnxCommandWait('echo "echo \'msg::lockDevice\\ndat::%s\' >> /pps/system/navigator/background" | /q/g_nto' % escapedMessage)

# Doesn't work, no idea why
def sendNavigatorCommand(command):
    user = getImpersonation("group", "nto")
    runAs("echo msg::%s >> /pps/system/navigator/control" % command, user)
    result = readFileAs("/pps/services/automation/navigator/output", user);
    maxTries = 10
    tries = 0
    print(result)
    while "dat::%s" % command not in result:
        time.sleep(0.1)
        tries += 1
        if tries >= maxTries:
            return []
        result = readFileAs("/pps/services/automation/navigator/output", user);
    
    match = re.search(r'response::(.*)', result)
    response = match.group(1).strip() if match else "";
    responseArray = []
    for item in response.split(";"):
        if "=" in item:
            key, value = item.split("=", 1)
            responseArray.append({key.strip(): value.strip()})
    return responseArray
    
def getForegroundAppInfo():
    result = sendNavigatorCommand("getForegroundAppInfo")
    if not result:
        return []
    return result

def getAllRunningApps():
    result = sendNavigatorCommand("getAllRunningApps")
    if not result:
        return []
    return result

def waitForFileAs(filePath, user, timeout=30):
    startTime = time.time()
    while time.time() - startTime < timeout:
        if fileExistsAs(filePath, user):
            return True
        time.sleep(0.5)
    return False