import asyncio
from .core import init, update
from .screens import Menu, Alert, QRAlert, Prompt, InputScreen
import lvgl as lv

class AsyncGUI:

    def __init__(self):
        # unlock event for host signalling
        # to avoid spamming GUI
        self.waiting = False
        # only one popup can be active at a time
        # another screen goes to the background
        self.background = None
        self.scr = None

    def release(self, *args, **kwargs):
        """
        Unlocks the GUI
        """
        self.args = args
        self.kwargs = kwargs
        self.waiting = True

    async def load_screen(self, scr):
        while self.background is not None:
            await asyncio.sleep_ms(10)
        old_scr = lv.scr_act()
        lv.scr_load(scr)
        self.scr = scr
        old_scr.del_async()

    async def open_popup(self, scr):
        # wait for another popup to finish
        while self.background is not None:
            await asyncio.sleep_ms(10)
        self.background = self.scr
        self.scr = scr
        lv.scr_load(scr)

    async def close_popup(self):
        scr = self.background
        self.background = None
        await self.load_screen(scr)

    async def get_input(self, title="Enter your bip-39 password:", 
            note="It is never stored on the device", suggestion=""):
        """
        Asks the user for a password
        """
        scr = InputScreen(title, note, suggestion)
        await self.load_screen(scr)
        return await scr.result()

    def start(self, rate:int=30):
        init()
        asyncio.create_task(self.update_loop(rate))

    async def update_loop(self, dt):
        while True:
            update(dt)
            await asyncio.sleep_ms(dt)

    async def menu(self, buttons:list=[], title:str="What do you want to do?", last=None):
        """
        Creates a menu with buttons. 
        buttons argument should be a list of tuples:
        (value, text)
        value is retured when the button is pressed
        text is the text on the button

        If add_back_button is set to True,
        < Back button is added to the bottom of the screen
        and if it is pressed AsyncGUI.BTN_CLOSE is returned (-99)
        """
        menu = Menu(buttons=buttons, title=title, last=last)
        await self.load_screen(menu)
        return await menu.result()

    async def alert(self, title, msg, button_text="OK"):
        """Shows an alert"""
        alert = Alert(title, msg, button_text=button_text)
        await self.load_screen(alert)
        await alert.result()

    async def qr_alert(self, title, msg, qr_msg, qr_width=None, button_text="OK"):
        """Shows an alert with QR code"""
        alert = QRAlert(title, msg, qr_msg, qr_width=qr_width, button_text=button_text)
        await self.load_screen(alert)
        return await alert.result()

    async def error(self, msg, popup=False):
        """Shows an error"""
        alert = Alert("Error!", msg, button_text="OK")
        if popup:
            await self.open_popup(alert)
        else:
            await self.load_screen(alert)
        res = await alert.result()
        if popup:
            await self.close_popup()
        return res

    async def prompt(self, title, msg):
        """Asks the user to confirm action"""
        scr = Prompt(title, msg)
        await self.load_screen(scr)
        return await scr.result()