from cubedata import CubeData

from pyscript import document, fetch, when

current_cube = CubeData()

SAFE_URL_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"


def quote_plus(text):
    """
    Percent-encodes a string for use in a query string, spaces become '+'.
    MicroPython has no urllib.parse.

    :param text: string to encode
    :return: encoded string
    """
    out = []

    for byte in text.encode("utf-8"):
        char = chr(byte)
        if char in SAFE_URL_CHARS:
            out.append(char)
        elif char == " ":
            out.append("+")
        else:
            out.append("%{:02X}".format(byte))

    return "".join(out)


def element(element_id):
    return document.getElementById(element_id)


def add_class(element_id, class_name):
    element(element_id).classList.add(class_name)


def remove_class(element_id, class_name):
    element(element_id).classList.remove(class_name)


def write(element_id, value):
    element(element_id).innerText = str(value)


def render_card(card, current_player, show_hidden=False, tag="li"):
    if show_hidden or "seen" not in card.keys() or card["seen"][current_player]:
        return '<{0} class="card-{1}">{2}</{0}>'.format(tag, card["color"], card["name"])
    else:
        return "<{0}>**hidden**</{0}>".format(tag)


def update_counts(cube):
    write("card_count", cube.card_count)
    write("main_pile_count", len(cube.shuffled_cards))
    write("used_count", cube.cards_used)


def update_pile_panels(cube):
    pile_panel_ids = ["pile-panel-one", "pile-panel-two", "pile-panel-three"]

    for ix, pile_panel_id in enumerate(pile_panel_ids):
        if ix == cube.current_pile:
            add_class(pile_panel_id, "current-pile-panel")
        else:
            remove_class(pile_panel_id, "current-pile-panel")


def update_piles(cube):
    pile_list_ids = ["pile_one", "pile_two", "pile_three"]

    for pile_list_id, pile in zip(pile_list_ids, cube.piles):
        list_entries = [
            render_card(c, cube.current_player, show_hidden=cube.finished_draft)
            for c in pile
        ]

        element(pile_list_id).innerHTML = "\n".join(list_entries)


def update_players(cube):
    player_list_ids = ["player_one_cards_list", "player_two_cards_list"]

    for ix, player_list_id in enumerate(player_list_ids):
        list_entries = [
            render_card(c, cube.current_player, show_hidden=cube.finished_draft)
            for c in cube.players[ix]
        ]

        element(player_list_id).innerHTML = "\n".join(list_entries)


def update_unused(cube):
    if cube.finished_draft:
        remove_class("unused_cards", "hidden")
        list_entries = [
            render_card(c, cube.current_player, show_hidden=cube.finished_draft)
            for c in cube.unused_cards
        ]
        element("unused_cards_list").innerHTML = "\n".join(list_entries)
    else:
        add_class("unused_cards", "hidden")
        element("unused_cards_list").innerHTML = ""


def update(cube):
    update_counts(cube)
    update_pile_panels(cube)
    update_piles(cube)
    update_players(cube)
    update_unused(cube)


@when("click", "#skip_button")
def action_skip(event):
    if not current_cube.finished_draft:
        current_cube.skip()
        current_cube.reveal_cards()
    update(current_cube)


@when("click", "#take_button")
def action_take(event):
    if not current_cube.finished_draft:
        current_cube.take_pile()
        current_cube.reveal_cards()
    update(current_cube)


@when("click", "#restart_button")
def action_restart(event):
    add_class("cube-modal", "active")


@when("click", "#start_url_button")
async def action_start_from_url(event):
    url = element("url-input").value

    try:
        text = await fetch(url).text()
    except Exception as e:
        write("cube-modal-error", "Could not load {0}: {1}".format(url, e))
        return

    start_draft(text)


@when("click", "#start_text_button")
def action_start_from_text(event):
    start_draft(element("text-input").value)


def start_draft(text):
    try:
        current_cube.read_cube_text(text)
    except Exception as e:
        write("cube-modal-error", "Could not parse cube list: {0}".format(e))
        return

    if current_cube.card_count < current_cube.draft_size:
        write(
            "cube-modal-error",
            "Need at least {0} cards, found {1}.".format(
                current_cube.draft_size, current_cube.card_count
            ),
        )
        return

    write("cube-modal-error", "")
    current_cube.init_game()
    update(current_cube)

    remove_class("cube-modal", "active")


async def get_image_uri(card_name):
    scryfall_api_url = "https://api.scryfall.com/cards/named?exact={0}".format(
        quote_plus(card_name)
    )
    scryfall_data = await fetch(scryfall_api_url).json()

    return str(scryfall_data["image_uris"]["normal"])


@when("click", "#pile_one, #pile_two, #pile_three, "
               "#player_one_cards_list, #player_two_cards_list, #unused_cards_list")
async def action_click_card(event):
    target = event.target

    if target.tagName != "LI":
        return

    card_name = target.innerText

    if card_name == "**hidden**":
        return

    element("card-image").setAttribute("src", "")
    add_class("card-modal", "active")

    try:
        image_uri = await get_image_uri(card_name)
    except Exception:
        return

    element("card-image").setAttribute("src", image_uri)


@when("click", "#close_card_modal")
def action_close_card_modal(event):
    remove_class("card-modal", "active")


## Start Here

remove_class("loading-modal", "active")
add_class("cube-modal", "active")
