#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Interpreter version: brython (http://brython.info) (like python3)
#
"""
Module with interface for picking authors from authority base in NK's Aleph.
"""
#
# Imports =====================================================================
import json
from os.path import join

from browser import html
from browser import alert
from browser import document

from .shared import make_request
from .shared import func_on_enter

from .log_view2 import LogView
from .progressbar import AuthorBar
from .author_switcher import SWITCHER_EL
from .overlay_controller import BlackOverlay

# virtual filesystem / modules provided by REST API
from virtual_fs import settings


# Functions & classes =========================================================
class AuthorPicker(object):
    """
    This component is responsible for generating the interface for picking
    authors from Aleph.
    """
    original_author_el = document["author"]

    div_el = document["author_picker"]  # whole <div>
    input_el = document["author_input"]  # <input> for author's name
    search_btn_el = document["search_author"]  # trigger the search of author
    select_el = document["author_select"]  # <select> element with authors
    storno_btn_el = document["storno_author"]  # used to storno the picking
    pick_btn_el = document["pick_author"]  # used to accept the selected author

    overlay = BlackOverlay()

    code_to_data = {}  # this is used to translate select back to data
    selected_code = None

    @classmethod
    def show(cls):
        """
        Show the author picker.
        """
        cls.div_el.style.display = "block"
        cls.hide_errors()

        cls.overlay.show()
        cls.overlay.el.bind("click", lambda x: cls.hide())

    @classmethod
    def hide(cls):
        """
        Hide the author picker.
        """
        cls.overlay.hide()
        cls.div_el.style.display = "none"

    @classmethod
    def hide_errors(cls):
        """
        Hide errors shown by validators.
        """
        cls.select_el.style.border = "0"
        cls.input_el.style.border = "0"

    @classmethod
    def _pick_selected_option(cls):
        """
        Select handler for authors.
        """
        for option in cls.select_el:
            # if the select is empty
            if not hasattr(option, "selected"):
                return None

            if option.selected:
                return option.value

        return None

    @classmethod
    def on_pick_button_pressed(cls, ev):
        """
        Callback called when the user press the button for accepting the picked
        author.

        This element calls validations, before accepting the choice.
        """
        cls.hide_errors()

        selected_code = cls._pick_selected_option()
        if not selected_code:
            cls.select_el.style.border = "2px solid red"
            return

        cls.selected_code = selected_code
        cls.code = str(selected_code)
        cls.original_author_el.value = cls.code_to_data[selected_code].get(
            "alt_name",
            str(cls.code_to_data[selected_code])
        )
        cls.hide()

    @classmethod
    def set_select(cls, authors):
        """
        Put data into ``<select>`` element.

        Args:
            authors (dict): Dictionary with author informations returned from
                aleph REST API. Format:
                ``{"name": .., "code": .., "linked_forms": ["..",]}``.
        """
        cls.select_el.html = ""

        if not authors:
            cls.select_el.disabled = True
            cls.select_el <= html.OPTION("Nic nenalezeno!")
            return

        cls.select_el.disabled = False
        for author_dict in authors:
            name = author_dict.get("name")
            code = author_dict.get("code")
            alt_name = author_dict.get("alt_name", name)

            if not (name and code):
                continue

            cls.code_to_data[code] = author_dict
            cls.select_el <= html.OPTION(alt_name, value=code)

    @classmethod
    def bind(cls):
        """
        Bind the callbacks to the buttons.
        """
        document["show_author_picker"].bind("click", lambda x: cls.show())

        cls.storno_btn_el.bind("click", lambda x: cls.hide())
        cls.pick_btn_el.bind("click", cls.on_pick_button_pressed)

    @classmethod
    def __get__(cls, obj, objtype):
        if not SWITCHER_EL.checked:
            return None

        return cls.code_to_data[cls.selected_code]

    @classmethod
    def __set__(cls, obj, value):
        # called by reset()
        if not value:
            cls.selected_code = None
            cls.original_author_el.value = ""
            return

        if type(value) in [list, tuple]:
            value = value[0]

        code = value["code"]
        cls.code_to_data[code] = value
        cls.selected_code = code

        cls.original_author_el.value = cls.code_to_data[code].get(
            "alt_name",
            str(cls.code_to_data[code])
        )

    @classmethod
    def validate(cls):
        """
        Validate required elements.
        """
        if not SWITCHER_EL.checked:
            return True

        if not cls.selected_code:
            cls.original_author_el.style.border = "2px solid red"
            return False

        cls.original_author_el.style.border = "0"
        return True


class AuthorPickerAdapter(AuthorPicker):
    """
    Adapter of the :class:`AuthorPicker` to aleph REST API.

    This adapter is mapped to the key / button press. It then launches querying
    of the REST API in periodic interval, updating the progress bar and so on.
    """
    @classmethod
    def on_complete(cls, req):
        """
        Callback called when the request was received.
        """
        # handle http errors
        if not (req.status == 200 or req.status == 0):
            alert("Couldn't connect to authority base.")
            LogView.add(
                "Error when calling Aleph authority base: %s" % req.text
            )
            AuthorBar.hide()
            return

        AuthorBar.hide()
        cls.set_select(json.loads(req.text))

    @classmethod
    def start(cls, ev):
        """
        Event handler which starts the request to REST API.
        """
        # somehow the first call doesn't stop the propagation
        ev.stopPropagation()
        ev.preventDefault()

        # make sure, that `author` was filled
        author = cls.input_el.value.strip()
        if not author:
            cls.input_el.style.border = "2px solid red"
            return

        cls.hide_errors()

        AuthorBar.show(50)
        make_request(
            url=join(settings.API_PATH, "aleph/authors_by_name"),
            data={"name": author},
            on_complete=cls.on_complete
        )

    @classmethod
    def bind(cls):
        """
        Bind the buttons to adapter's event handler.
        """
        super(cls, cls).bind()

        cls.search_btn_el.bind("click", cls.start)
        cls.input_el.bind("keypress", func_on_enter(cls.start))


AuthorPickerAdapter.bind()
