# Copyright 2021 Erfan Abdi
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
import os
from tools import helpers
import tools.config

from tkinter import *
from tkinter import ttk

import sys
import threading
class Daemon(threading.Thread):
    def __init__(self):
        super().__init__()
        self.daemon = True

def is_initialized(args):
    return os.path.isfile(args.config) and os.path.isdir(tools.config.defaults["rootfs"])

def get_vendor_type(args):
    vndk_str = helpers.props.host_get(args, "ro.vndk.version")
    ret = "MAINLINE"
    if vndk_str != "":
        vndk = int(vndk_str)
        if vndk > 19:
            ret = "HALIUM_" + str(vndk - 19)

    return ret

def setup_config(args):
    cfg = tools.config.load(args)
    args.arch = helpers.arch.host()
    cfg["waydroid"]["arch"] = args.arch

    preinstalled_images = tools.config.defaults["preinstalled_images_path"]
    if not args.images_path:
        if os.path.isdir(preinstalled_images):
            if os.path.isfile(preinstalled_images + "/system.img") and os.path.isfile(preinstalled_images + "/vendor.img"):
                args.images_path = preinstalled_images
            else:
                logging.error("Missing system or vendor on preinstalled images dir, fallback to default")
    if not args.images_path:
        args.images_path = tools.config.defaults["images_path"]
    cfg["waydroid"]["images_path"] = args.images_path

    channels_cfg = tools.config.load_channels()
    if not args.system_channel:
        args.system_channel = channels_cfg["channels"]["system_channel"]
    if not args.vendor_channel:
        args.vendor_channel = channels_cfg["channels"]["vendor_channel"]
    if not args.rom_type:
        args.rom_type = channels_cfg["channels"]["rom_type"]
    if not args.system_type:
        args.system_type = channels_cfg["channels"]["system_type"]

    args.system_ota = args.system_channel + "/" + args.rom_type + \
        "/waydroid_" + args.arch + "/" + args.system_type + ".json"
    system_request = helpers.http.retrieve(args.system_ota)
    if system_request[0] != 200:
        if args.images_path != preinstalled_images:
            raise ValueError(
                "Failed to get system OTA channel: {}, error: {}".format(args.system_ota, system_request[0]))
        else:
            args.system_ota = "None"

    device_codename = helpers.props.host_get(args, "ro.product.device")
    args.vendor_type = None
    for vendor in [device_codename, get_vendor_type(args)]:
        vendor_ota = args.vendor_channel + "/waydroid_" + \
            args.arch + "/" + vendor.replace(" ", "_") + ".json"
        vendor_request = helpers.http.retrieve(vendor_ota)
        if vendor_request[0] == 200:
            args.vendor_type = vendor
            args.vendor_ota = vendor_ota
            break

    if not args.vendor_type:
        if args.images_path != preinstalled_images:
            raise ValueError(
                "Failed to get vendor OTA channel: {}".format(vendor_ota))
        else:
            args.vendor_ota = "None"
            args.vendor_type = get_vendor_type(args)

    if args.system_ota != cfg["waydroid"].get("system_ota"):
        cfg["waydroid"]["system_datetime"] = tools.config.defaults["system_datetime"]
    if args.vendor_ota != cfg["waydroid"].get("vendor_ota"):
        cfg["waydroid"]["vendor_datetime"] = tools.config.defaults["vendor_datetime"]

    cfg["waydroid"]["vendor_type"] = args.vendor_type
    cfg["waydroid"]["system_ota"] = args.system_ota
    cfg["waydroid"]["vendor_ota"] = args.vendor_ota
    helpers.drivers.setupBinderNodes(args)
    cfg["waydroid"]["binder"] = args.BINDER_DRIVER
    cfg["waydroid"]["vndbinder"] = args.VNDBINDER_DRIVER
    cfg["waydroid"]["hwbinder"] = args.HWBINDER_DRIVER
    tools.config.save(args, cfg)

def do_init(args):
    if not is_initialized(args) or args.force:
        setup_config(args)
        status = "STOPPED"
        if os.path.exists(tools.config.defaults["lxc"] + "/waydroid"):
            status = helpers.lxc.status(args)
        if status != "STOPPED":
            logging.info("Stopping container")
            helpers.lxc.stop(args)
        helpers.images.umount_rootfs(args)
        if args.images_path != tools.config.defaults["preinstalled_images_path"]:
            helpers.images.get(args)
        if not os.path.isdir(tools.config.defaults["rootfs"]):
            os.mkdir(tools.config.defaults["rootfs"])
        helpers.lxc.setup_host_perms(args)
        helpers.lxc.set_lxc_config(args)
        helpers.lxc.make_base_props(args)
        if status != "STOPPED":
            logging.info("Starting container")
            helpers.images.mount_rootfs(args, args.images_path)
            helpers.lxc.start(args)

        helpers.ipc.notify(channel="init", msg="done")
    else:
        logging.info("Already initialized")

def init(args):
    if args.gui:
        gui_init(args)
    else:
        do_init(args)

def gui_init(args):
    if is_initialized(args) and not args.force:
        return

    root = Tk()
    root.title("Initialize Waydroid")
    root.iconphoto(True, PhotoImage(file="/usr/lib/waydroid/data/AppIcon.png"))
    frm = ttk.Frame(root, padding=10)
    frm.grid()

    systemChannel = StringVar(frm, args.system_channel or tools.config.channels_defaults["system_channel"])
    ttk.Label(frm, text="System OTA").grid(row=0, column=0)
    ttk.Entry(frm, textvariable=systemChannel).grid(row=0, column=1, ipadx=20)

    vendorChannel = StringVar(frm, args.vendor_channel or tools.config.channels_defaults["vendor_channel"])
    ttk.Label(frm, text="Vendor OTA").grid(row=1, column=0)
    ttk.Entry(frm, textvariable=vendorChannel).grid(row=1, column=1, ipadx=20)

    systemType = StringVar(frm)
    systemTypes = ["VANILLA", "GAPPS"]
    ttk.Label(frm, text="Android Type").grid(row=2, column=0)
    ttk.OptionMenu(frm, systemType, args.system_type or systemTypes[0], *systemTypes).grid(row=2, column=1)

    done = ttk.Button(frm, text="Done", command=root.destroy)

    logBox = Text(frm, borderwidth=3, relief="sunken", height=5)
    logBox.bind("<Key>", lambda e: "break")

    class StdoutRedirect(logging.StreamHandler):
        def write(self, s):
            if s.startswith('\r'):
                logBox.delete("end-1l", "end")
                logBox.insert(END, '\n')
                s = s[1:]

            logBox.insert(END, s)
            logBox.see(END)
        def flush(self):
            pass
        def emit(self, record):
            if record.levelno >= logging.INFO:
                self.write(self.format(record) + self.terminator)

    out = StdoutRedirect()
    sys.stdout = sys.stderr = out
    logging.getLogger().addHandler(out)

    def runInit():
        download["state"] = DISABLED
        logBox.grid(row=4, columnspan=2)

        args.system_channel = systemChannel.get()
        args.vendor_channel = vendorChannel.get()
        args.system_type = systemType.get()

        class Runner(Daemon):
            def run(self):
                try:
                    do_init(args)
                    if is_initialized(args):
                        done.grid(row=5, columnspan=2)
                        print("Done")
                    else:
                        download["state"] = NORMAL
                except Exception as e:
                    print("ERROR: " + str(e))
                    download["state"] = NORMAL

        Runner().start()

    download = ttk.Button(frm, text="Download", command=runInit)
    download.grid(row=3, columnspan=2)
    root.mainloop()

    sys.stdout = sys.__stdout__
    sys.stderr = sys.__stderr__
    logging.getLogger().removeHandler(out)
