#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Interpreter version: brython (http://brython.info) (like python3)
#
# Imports =====================================================================
from browser import html
from browser import alert
from browser import window
from browser import document

from virtual_fs import conspectus


# Functions & classes =========================================================
class ConspectHandler(object):
    """
    Conspect is this dataset, where there is 21 categories (conspects) and
    hundreds of sub-categories (sub-conspects).

    This class allow user to pick the conspect / subconspect elements using
    following widgets:

        - two selects
        - one searchable typeahead tag

    First approach may be better when you don't exactly know what do you want
    and feel like browsing the conspects.

    Second approach is better when you just simply need to pick one.

    User may switch between this two approaches without the loss of the
    information.
    """
    input_el = document["conspect_subconspect"]
    conspect_el = document["konspekt"]
    subconspect_el = document["subkonspekt"]

    is_twoconspect = True

    @classmethod
    def _save_value(cls, value):
        """
        Callback used to automatically store value in :attr:`value` property
        each time the user selects value.
        """
        cls.value = value

    @classmethod
    def _get_subconspects(cls, consp_id):
        subconspects = conspectus.consp_dict[consp_id]["subconspects"].values()

        return sorted(subconspects, key=lambda x: x["name"])

    @classmethod
    def _draw_subconspects(cls, consp_id):
        cls.subconspect_el.html = ""
        for sub_conspect in cls._get_subconspects(consp_id):
            name = sub_conspect["name"]
            mdt = sub_conspect["mdt"]

            cls.subconspect_el <= html.OPTION(name, value=mdt)

    @classmethod
    def _draw_conspects(cls):
        for name, uid in conspectus.cosp_id_pairs:
            cls.conspect_el <= html.OPTION(name, value=uid)

        cls.conspect_el.bind(
            'change',
            lambda ev: cls._draw_subconspects(ev.target.value)
        )

    @classmethod
    def _create_searchable_typeahead(cls):
        """
        Create typeahead <input> element and fill it with data.
        """
        window.destroy_typeahead_tag("#conspect_subconspect_typeahead")
        args = [
            {
                "name": name,
                "data": [x["name"] for x in cls._get_subconspects(uid)],
            }
            for name, uid in conspectus.cosp_id_pairs
        ]
        window.make_multi_searchable_typeahead_tag(
            "#conspect_subconspect_typeahead",
            *args
        )

    @classmethod
    def get(cls):
        """
        Get code selected by user.

        Returns:
            str: Code or None in case that user didn't selected anything yet.
        """
        if cls.is_twoconspect:
            return cls.subconspect_el.value or None

        input_value = cls.input_el.value.strip()

        # blank user input -> no value was yet set
        if not input_value:
            return None

        mdt = conspectus.mdt_by_name.get(input_value)

        if not mdt:
            alert("Invalid sub-conspect `%s`!" % input_value)
            return None

        return mdt

    @classmethod
    def set(cls, mdt):
        """
        Set value for <input> / <select> tags based on MDT.

        Args:
            mdt (str): MDT of the conspect / subconspect.
        """
        if type(mdt) in [list, tuple]:
            mdt = mdt[0]

        if isinstance(mdt, dict):
            mdt = mdt["val"]

        if not mdt:
            cls.conspect_el.value = ""
            cls.subconspect_el.html = ""
            cls.input_el.value = ""
            return

        # set twoconspect
        data = conspectus.subs_by_mdt.get(mdt)

        if data:
            cls.conspect_el.value = data["conspect_id"]

            cls._draw_subconspects(data["conspect_id"])
            cls.subconspect_el.value = mdt

            # set input
            cls.input_el.value = data["name"]
        else:
            cls.input_el.value = mdt + " not found"

    @classmethod
    def get_dict(cls):
        """
        Return dictionary with conspect / subconspect info.
        """
        mdt = cls.get()

        if not mdt:
            return {}

        return conspectus.subs_by_mdt.get(mdt, {})


class ConspectDescriptor(ConspectHandler):
    switcher_els = document.get(selector=".conspect_switcher")
    two_conspect_el = document.get(selector=".two_conspect")
    whole_conspect_subconspect_el = document["whole_conspect_subconspect"]

    @classmethod
    def bind_switcher(cls):
        """
        Bind the switch checkbox to functions for switching between types of
        inputs.
        """
        def show_two_conspect():
            cls.is_twoconspect = True

            # search by class
            for el in cls.two_conspect_el:
                el.style.display = "block"

            cls.whole_conspect_subconspect_el.style.display = "none"

        def hide_two_conspect():
            cls.is_twoconspect = False

            for el in cls.two_conspect_el:
                el.style.display = "none"

            cls.whole_conspect_subconspect_el.style.display = "block"

        def show_or_hide_two_conspect(ev):
            val = cls.get()

            # check / uncheck both checkboxes
            for el in cls.switcher_els:
                el.checked = ev.target.checked

            if ev.target.checked:
                hide_two_conspect()
                cls.set(val)
                return

            show_two_conspect()
            cls.set(val)

        # bind both `conspect_switchers`
        for el in document.get(selector=".conspect_switcher"):
            el.bind("change", show_or_hide_two_conspect)

    @classmethod
    def show_error(cls, error=True):
        """
        Show `error` around the conspect elements. If the `error` is ``False``,
        hide it.
        """
        if error:
            cls.input_el.style.border = "2px solid red"
            cls.conspect_el.style.border = "2px solid red"
            cls.subconspect_el.style.border = "2px solid red"
        else:
            cls.input_el.style.border = "0"
            cls.conspect_el.style.border = "0"
            cls.subconspect_el.style.border = "0"

    @classmethod
    def init(cls):
        """
        Bind elements to callbacks.
        """
        for el in cls.switcher_els:
            el.checked = False

        cls.bind_switcher()

        cls._draw_conspects()
        cls._create_searchable_typeahead()

    @classmethod
    def __get__(cls, obj, objtype):
        return cls.get_dict()

    @classmethod
    def __set__(cls, obj, value):
        cls.show_error(False)
        cls.set(value)

    @classmethod
    def validate(cls):
        """
        Make sure, that conspect element is properly selected. If not, show
        error.
        """
        if cls.get_dict():
            cls.show_error(False)
            return True

        cls.show_error(True)
        return False

    @classmethod
    def reset(cls):
        """
        Reset the conspect elements to initial state.
        """
        cls.input_el.value = ""
        cls.subconspect_el.html = ""
        cls.show_error(False)


ConspectDescriptor.init()
