from module.base.button import ButtonWrapper
from module.base.decorator import run_once, Config
from module.base.timer import Timer
from module.base.utils import get_color
from module.exception import GameNotRunningError, GamePageUnknownError, RequestHumanTakeover
from module.logger import logger
from module.ocr.ocr import Ocr, Digit
from tasks.base.assets.assets_base_page import BACK
from tasks.base.main_page import MainPage
from tasks.base.page import Page, page_main
from tasks.login.assets.assets_login import LOGIN_LOADING, OCR_YEAR


class UI(MainPage):
    ui_current: Page
    ui_main_confirm_timer = Timer(0.2, count=2)

    @Config.when(Emulator_GameLanguage='zhs')
    def appear_trademark_year(self):
        ocr_year = Digit(OCR_YEAR).ocr_single_line(self.device.image)
        return ocr_year == 2023

    # temporary block check for jp
    @Config.when(Emulator_GameLanguage='jp')
    def appear_trademark_year(self):
        return False

    @Config.when(Emulator_GameLanguage=None)
    def appear_trademark_year(self):
        ocr_year = Digit(OCR_YEAR).ocr_single_line(self.device.image)
        return ocr_year == 2021

    def ui_page_appear(self, page):
        """
        Args:
            page (Page):
        """
        return self.appear(page.check_button)

    def ui_get_current_page(self, skip_first_screenshot=True):
        """
        Args:
            skip_first_screenshot:

        Returns:
            Page:

        Raises:
            GameNotRunningError:
            GamePageUnknownError:
        """
        logger.info("UI get current page")

        @run_once
        def app_check():
            if not self.device.app_is_running():
                raise GameNotRunningError("Game not running")

        @run_once
        def minicap_check():
            if self.config.Emulator_ControlMethod == "uiautomator2":
                self.device.uninstall_minicap()

        @run_once
        def rotation_check():
            self.device.get_orientation()

        timeout = Timer(10, count=20).start()
        back_timer = Timer(0.5, count=2)
        u2_back = True
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
                if not hasattr(self.device, "image") or self.device.image is None:
                    self.device.screenshot()
            else:
                self.device.screenshot()

            # End
            if timeout.reached():
                break

            # Known pages
            for page in Page.iter_pages():
                if page.check_button is None:
                    continue
                if self.ui_page_appear(page=page):
                    logger.attr("UI", page.name)
                    self.ui_current = page
                    return page

            # Unknown page but able to handle
            logger.info("Unknown ui page")
            if self.ui_additional():
                logger.info("Additional ui page handled")
                timeout.reset()
                continue
            if back_timer.reached_and_reset():
                # this might be bad but it works
                if self.match_color(LOGIN_LOADING, interval=5, threshold=80) or self.appear_trademark_year():
                    from tasks.login.login import Login
                    Login(self.config, self.device).handle_app_login()
                    timeout.reset()
                    continue
                logger.info("Unknown page, try to back")
                # allows TooManyClicks to be triggered in case something goes wrong
                if u2_back:
                    self.device.back()
                    u2_back = False
                else:
                    self.device.click(BACK)
                    u2_back = True

            app_check()
            minicap_check()
            rotation_check()

        # Unknown page, need manual switching
        logger.warning("Unknown ui page")
        logger.attr("EMULATOR__SCREENSHOT_METHOD", self.config.Emulator_ScreenshotMethod)
        logger.attr("EMULATOR__CONTROL_METHOD", self.config.Emulator_ControlMethod)
        logger.attr("Lang", self.config.LANG)
        logger.warning("Starting from current page is not supported")
        logger.warning(f"Supported page: {[str(page) for page in Page.iter_pages()]}")
        logger.warning('Supported page: Any page with a "HOME" button on the upper-right')
        logger.critical("Please switch to a supported page before starting AAS")
        raise GamePageUnknownError

    def ui_goto(self, destination, skip_first_screenshot=True):
        """
        Args:
            destination (Page):
            skip_first_screenshot:
        """
        # Create connection
        Page.init_connection(destination)
        self.interval_clear(list(Page.iter_check_buttons()))

        # loading_timer = Timer(0.5)
        back_timer = Timer(15, 15)
        logger.hr(f"UI goto {destination}")
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            # # Loading
            # if self.appear(LOADING_CHECK):
            #     loading_timer.reset()
            #     continue
            #
            # if not loading_timer.reached():
            #     continue

            # Destination page
            if self.ui_page_appear(destination):
                logger.info(f'Page arrive: {destination}')
                self.close_popup(destination.check_button)
                if self.ui_page_confirm(destination):
                    logger.info(f'Page arrive confirm {destination}')
                break

            # Other pages
            clicked = False
            for page in Page.iter_pages():
                if page.parent is None or page.check_button is None:
                    continue
                if self.appear(page.check_button, interval=5):
                    logger.info(f'Page switch: {page} -> {page.parent}')
                    self.close_popup(page.check_button)
                    # self.handle_lang_check(page)
                    if self.ui_page_confirm(page):
                        logger.info(f'Page arrive confirm {page}')
                    button = page.links[page.parent]
                    self.device.click(button)
                    self.ui_button_interval_reset(button)
                    clicked = True
                    break
            if clicked:
                continue

            # Additional
            if self.ui_additional():
                continue

            back_timer.start()
            if back_timer.reached():
                if self.match_color(LOGIN_LOADING, interval=5, threshold=80) or self.appear_trademark_year():
                    from tasks.login.login import Login
                    Login(self.config, self.device).handle_app_login()
                # don't click back when screen is black. 
                # Useful for loading screen after switching between pages
                elif [x for x in get_color(self.device.image, BACK.area) if x > 50]:
                    self.device.back()
                    logger.info("Unknown page, try to back")
                back_timer.reset()

        # Reset connection
        Page.clear_connection()

    def ui_ensure(self, destination, acquire_lang_checked=True, skip_first_screenshot=True):
        """
        Args:
            destination (Page):
            acquire_lang_checked:
            skip_first_screenshot:

        Returns:
            bool: If UI switched.
        """
        logger.hr("UI ensure")
        self.ui_get_current_page(skip_first_screenshot=skip_first_screenshot)

        # self.ui_leave_special()

        # if acquire_lang_checked:
        #     if self.acquire_lang_checked():
        #         self.ui_get_current_page(skip_first_screenshot=skip_first_screenshot)

        if self.ui_current == destination:
            logger.info("Already at %s" % destination)
            self.close_popup(destination.check_button)
            return False
        else:
            logger.info("Goto %s" % destination)
            self.ui_goto(destination, skip_first_screenshot=True)
            return True

    def ui_ensure_index(
            self,
            index,
            letter,
            next_button,
            prev_button,
            skip_first_screenshot=False,
            fast=True,
            interval=(0.2, 0.3),
    ):
        """
        Args:
            index (int):
            letter (Ocr, callable): OCR button.
            next_button (Button):
            prev_button (Button):
            skip_first_screenshot (bool):
            fast (bool): Default true. False when index is not continuous.
            interval (tuple, int, float): Seconds between two click.
        """
        logger.hr("UI ensure index")
        retry = Timer(1, count=2)
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            if isinstance(letter, Ocr):
                current = letter.ocr_single_line(self.device.image)
            else:
                current = letter(self.device.image)

            logger.attr("Index", current)
            diff = index - current
            if diff == 0:
                break
            if current == 0:
                logger.warning(f'ui_ensure_index got an empty current value: {current}')
                continue

            if retry.reached():
                button = next_button if diff > 0 else prev_button
                if fast:
                    self.device.multi_click(button, n=abs(diff), interval=interval)
                else:
                    self.device.click(button)
                retry.reset()

    def ui_click(
            self,
            click_button,
            check_button,
            appear_button=None,
            additional=None,
            retry_wait=5,
            skip_first_screenshot=True,
    ):
        """
        Args:
            click_button (ButtonWrapper):
            check_button (ButtonWrapper, callable, list[ButtonWrapper], tuple[ButtonWrapper]):
            appear_button (ButtonWrapper, callable, list[ButtonWrapper], tuple[ButtonWrapper]):
            additional (callable):
            retry_wait (int, float):
            skip_first_screenshot (bool):
        """
        if appear_button is None:
            appear_button = click_button
        logger.info(f'UI click: {appear_button} -> {check_button}')

        def process_appear(button):
            if isinstance(button, ButtonWrapper):
                return self.appear(button)
            elif callable(button):
                return button()
            elif isinstance(button, (list, tuple)):
                for b in button:
                    if self.appear(b):
                        return True
                return False
            else:
                return self.appear(button)

        click_timer = Timer(retry_wait, count=retry_wait // 0.5)
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            # End
            if process_appear(check_button):
                break

            # Click
            if click_timer.reached():
                if process_appear(appear_button):
                    self.device.click(click_button)
                    click_timer.reset()
                    continue
            if additional is not None:
                if additional():
                    continue

    def is_in_main(self):
        if self.appear(page_main.check_button):
            if self.image_color_count(page_main.check_button, color=(235, 235, 235), threshold=221, count=400):
                return True
        # if self.appear(MAP_EXIT):
        #     if self.image_color_count(MAP_EXIT, color=(235, 235, 235), threshold=221, count=50):
        #         return True
        return False

    def ui_goto_main(self):
        return self.ui_ensure(destination=page_main)

    # def ui_additional(self) -> bool:
    #     """
    #     Handle all possible popups during UI switching.
    #
    #     Returns:
    #         If handled any popup.
    #     """
    #     if self.handle_reward():
    #         return True
    #     if self.handle_battle_pass_notification():
    #         return True
    #     if self.handle_monthly_card_reward():
    #         return True
    #     if self.appear(COMBAT_PREPARE, interval=5):
    #         logger.info(f'UI additional: {COMBAT_PREPARE} -> {CLOSE}')
    #         self.device.click(CLOSE)
    #     if self.appear_then_click(COMBAT_EXIT, interval=5):
    #         return True
    #     if self.appear_then_click(INFO_CLOSE, interval=5):
    #         return True
    #
    #     return False

    def ui_additional(self) -> bool:
        """
        Handle all possible popups during UI switching.

        Returns:
            If handled any popup.
        """
        if self.handle_loading():
            return True
        if self.handle_reward_skip():
            return True
        if self.handle_reward():
            return True
        if self.handle_daily_news():
            return True
        if self.handle_quit():
            return True
        if self.handle_network_reconnect():
            return True
        if self.handle_affection_level_up():
            return True
        if self.handle_new_student():
            return True
        # disabled because will exit the game if quit appears

        # if self.handle_ap_exceed():
        #    return True
        # if self.handle_insufficient_inventory():
        #    return True
        # if self.handle_item_expired():
        #    return True

        return False

    def _ui_button_confirm(
            self,
            button,
            confirm=Timer(0.1, count=0),
            timeout=Timer(2, count=6),
            skip_first_screenshot=True
    ):
        confirm.reset()
        timeout.reset()
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            if timeout.reached():
                logger.warning(f'_ui_button_confirm({button}) timeout')
                break

            if self.appear(button):
                if confirm.reached():
                    break
            else:
                confirm.reset()

    def ui_page_confirm(self, page):
        """
        Args:
            page (Page):

        Returns:
            bool: If handled
        """
        if page == page_main:
            self._ui_button_confirm(page.check_button)
            return True

        return False

    def ui_button_interval_reset(self, button):
        """
        Reset interval of some button to avoid mistaken clicks

        Args:
            button (Button):
        """
        pass

    def close_popup(self, check_button):
        if not self.match_color(check_button):
            timer = Timer(5, 5).start()
            wait = Timer(1).start()
            while 1:
                self.device.screenshot()
                if self.match_color(check_button) or not self.appear(check_button):
                    break
                self.device.back()
                if timer.reached():
                    logger.error("Failed to close popup")
                    raise RequestHumanTakeover
                while not wait.reached():
                    pass
