import display
import custom_shapes
import utils
import easydraw
import buttons
import game

import uasyncio
import buttons
import main
import mch22
import sys
import errno
from backend import GameClient


class Lobby:
    def __init__(self, code, player_uid, game_info):
        self.code = code
        self.game_info = game_info
        self.player_index = -1
        self.player_uid = player_uid
        self.is_host = False
        self.player_count = game_info["players"]
        self.can_start = False  # If host, nobody joined yet. If joined, only host can start
        for i, slot in enumerate(game_info["slots"]):
            if slot is not None and slot["uid"] == player_uid:
                print("Found ourselves in slot[%s]:" % i, slot)
                self.is_host = slot["host"]
                self.player_index = i

    def update_game_info(self, game_info):
        self.game_info = game_info
        self.player_count = game_info["players"]
        self.can_start = self.is_host and self.player_count > 1

    def draw_lobby(self):
        player_id = self.player_index

        utils.draw_background(wifi_indicator=True, logo=False)

        if self.can_start:
            easydraw.lineCentered(25, "PRESS START", utils.ARCADE_FONT[12], utils.TEXT_MAIN)
            easydraw.lineCentered(45, "TO PLAY", utils.ARCADE_FONT[12], utils.TEXT_MAIN)
        elif self.is_host:
            easydraw.lineCentered(25, "WAITING FOR", utils.ARCADE_FONT[12], utils.TEXT_MAIN)
            easydraw.lineCentered(45, "PLAYERS TO JOIN...", utils.ARCADE_FONT[12], utils.TEXT_MAIN)
        else:
            easydraw.lineCentered(25, "WAITING FOR", utils.ARCADE_FONT[12], utils.TEXT_MAIN)
            easydraw.lineCentered(45, "HOST TO PRESS START", utils.ARCADE_FONT[12], utils.TEXT_MAIN)

        display.drawRect(50, 65, 220, 50, False, utils.HIGHLIGHT)
        for i, action in enumerate(self.code):
            self.draw_sign(action, i)

        if player_id == 0:
            display.drawRect(25, 125, 60, 60, False, utils.COLOR_SECONDARY)
        display.drawPng(25, 125, utils.DOWNTOWN)
        display.drawText(27, 190, "DOWNTOWN", self.get_text_color(0, player_id), utils.ARCADE_FONT[6])

        if player_id == 1:
            display.drawRect(95, 125, 60, 60, False, utils.COLOR_SECONDARY)
        display.drawPng(95, 125, utils.SCIENCE_PARK if self.player_count >= 2 else utils.SCIENCE_PARK_OFF)
        display.drawText(100, 190, "SCIENCE", self.get_text_color(1, player_id), utils.ARCADE_FONT[6])
        display.drawText(111, 197, "PARK", self.get_text_color(1, player_id), utils.ARCADE_FONT[6])

        if player_id == 2:
            display.drawRect(165, 125, 60, 60, False, utils.COLOR_SECONDARY)
        display.drawPng(165, 125, utils.COMMERCIAL_AVENUE if self.player_count >= 3 else utils.COMMERCIAL_AVENUE_OFF)
        display.drawText(160, 190, "COMMERCIAL", self.get_text_color(2, player_id), utils.ARCADE_FONT[6])
        display.drawText(174, 197, "AVENUE", self.get_text_color(2, player_id), utils.ARCADE_FONT[6])

        if player_id == 3:
            display.drawRect(235, 125, 60, 60, False, utils.COLOR_SECONDARY)
        display.drawPng(235, 125, utils.HACKY_HARBOUR if self.player_count >= 4 else utils.HACKY_HARBOUR_OFF)
        display.drawText(247, 190, "HACKY", self.get_text_color(3, player_id), utils.ARCADE_FONT[6])
        display.drawText(240, 197, "HARBOUR", self.get_text_color(3, player_id), utils.ARCADE_FONT[6])

        display.flush()

    def get_text_color(self, district_id, player_id):
        if district_id == player_id:
            return utils.TEXT_HIGHLIGHT
        elif self.player_count > district_id:
            return utils.TEXT_MAIN
        else:
            return utils.TEXT_GRAY

    def draw_sign(self, action, i):
        _code_x_start = 68
        _code_x_delta = 36
        _code_y = 90
        if action == "A" or action == "B":
            display.drawText(
                _code_x_start + (_code_x_delta * i) - 5,
                _code_y - 5,
                action,
                utils.TEXT_HIGHLIGHT,
                utils.ARCADE_FONT[18],
            )
        else:
            custom_shapes.draw_code_arrow(
                _code_x_start + (_code_x_delta * i), _code_y, action
            )


def create_lobby():
    """
    Display create lobby screen when nobody has joined
    Params:
    """
    if not utils.connect_wifi():
        utils.draw_background()
        easydraw.lineCentered(
            180, "WiFi is required to play", utils.ARCADE_FONT[9], utils.TEXT_MAIN
        )
        utils.draw_lines(
            "Press MENU to return to the main menu",
            y_start=195,
            vertical_padding=3,
            width=display.width() - 50,
            font=utils.ARCADE_FONT[8],
        )
        display.flush()
        return
    if not utils.check_version():
        return

    utils.draw_background()
    easydraw.lineCentered(180, "Creating game...", utils.ARCADE_FONT[12], utils.TEXT_MAIN)
    display.flush()

    loop = uasyncio.get_event_loop()
    try:
        game_client = GameClient(loop, create_game=True, namespace="/")
    except OSError:
        connection_failed_screen(joining=False)
        return

    loop.create_task(connect_game_client(game_client, joining=False))
    loop.create_task(buttons_helper_task(game_client))
    loop.run_forever()


def join_lobby(code):
    """
    Placeholder as this needs to contact the server
    """
    utils.draw_background()
    easydraw.lineCentered(180, "Joining game...", utils.ARCADE_FONT[12], utils.TEXT_MAIN)
    display.flush()

    loop = uasyncio.get_event_loop()
    try:
        game_client = GameClient(loop, create_game=False, namespace="/", game_code=utils.code_to_code_string(code))
    except OSError:
        connection_failed_screen(joining=True)
        return

    game_client.joining_game = True
    loop.create_task(connect_game_client(game_client, joining=True))
    loop.create_task(buttons_helper_task(game_client))
    loop.run_forever()


async def connect_game_client(game_client, joining):
    try:
        await game_client.connect()
    except Exception:
        # Probably one of: [errno.ECONNRESET, errno.ENOTCONN, errno.ECONNABORTED]:
        connection_failed_screen(joining)


def connection_failed_screen(joining):
    action = "Joining" if joining else "Creating"
    print("Exception when %s game received:" % action.lower())
    utils.draw_background()
    easydraw.lineCentered(160, action + " Game failed,", utils.ARCADE_FONT[12], utils.TEXT_MAIN)
    easydraw.lineCentered(180, "Please try again.", utils.ARCADE_FONT[12], utils.TEXT_MAIN)
    utils.draw_lines(
        "Press MENU to return to the main menu",
        y_start=195,
        vertical_padding=3,
        width=display.width() - 50,
        font=utils.ARCADE_FONT[8],
    )
    display.flush()


async def buttons_helper_task(game_client):
    # Somehow, the "await" on the socketio is still blocking for
    # the button callbacks. So we use this async old-school button
    # press loop instead.
    # We start by disabling the default mch22 button handler,
    # This prevents uPy scheduler warnings on button presses.
    mch22.set_handler(None)

    game_button_states = {
        buttons.BTN_A:     False,
        buttons.BTN_B:     False,
        buttons.BTN_UP:    False,
        buttons.BTN_DOWN:  False,
        buttons.BTN_LEFT:  False,
        buttons.BTN_RIGHT: False,
    }
    start_debounce = False

    while True:
        if buttons.value(buttons.BTN_HOME):
            print("Home Button Pressed, rebooting to launcher")
            await game_client.exit_game()
            mch22.exit_python()
            break
        if buttons.value(buttons.BTN_MENU):
            print("Menu Button Pressed, ending game")
            await game_client.exit_game()
            mch22.set_handler(buttons.hwButtons.__callback)
            main.main_menu()
            break
        if not start_debounce and game_client.lobby and game_client.lobby.can_start and buttons.value(buttons.BTN_START):
            print("Starting Game")
            start_debounce = True
            await game_client.start_game()
        elif not start_debounce and game_client.instruction_task and buttons.value(buttons.BTN_START):
            print("Skipping instructions")
            start_debounce = True
            game_client.instruction_task.cancel()
            game_client.instruction_task = None
        elif start_debounce:
            if not buttons.value(buttons.BTN_START):
                start_debounce = False

        if game_client.game_in_progress:
            for game_button in game_button_states:
                # Only consider button presses after the button was previously released
                pressed = buttons.value(game_button)
                value = game_button_states[game_button]
                if not value and pressed:
                    game_button_states[game_button] = True
                    try:
                        await game_client.game_button_pressed(game_button)
                    except Exception as exception:
                        print("Exception on key %d press:" % game_button)
                        sys.print_exception(exception)
                        await game_client.report_exception(exception)
                    except OSError as oserror:
                        if oserror.errno in [errno.ECONNRESET, errno.ENOTCONN, errno.ECONNABORTED]:
                            print("OSError on key %d press:" % game_button)
                            sys.print_exception(oserror)
                            game_client.player_disconnected()

                elif value and not pressed:
                    game_button_states[game_button] = False

        await uasyncio.sleep_ms(10)
