import json
from threading import Lock
from xml.etree import cElementTree as etree

import pyscripts
from bustoken import (
    TK_ELECTRONIC_PAYMENT_CANCEL_PAYMENT,
    TK_ELECTRONIC_PAYMENT_SEND_RESPONSE
)
from msgbus import FM_PARAM
from mw_helper import show_message_dialog, show_qr_code
from sysactions import (
    close_asynch_dialog,
    update_any_dialog,
    show_keyboard,
    show_listbox,
    show_messagebox,
)

from actions.eft import get_electronic_payment_component_name


mb_context = pyscripts.mbcontext
_current_asynchronous_dialog_id = {}
_current_asynchronous_dialog_lock = Lock()
message_lock_dict = Lock()
message_lock = {}
qrcode_dialog_id = {}


def _close_current_asynchronous_dialog(pos_id):
    dialog_id = None
    with _current_asynchronous_dialog_lock:
        if pos_id in _current_asynchronous_dialog_id:
            dialog_id = _current_asynchronous_dialog_id[pos_id]
            del _current_asynchronous_dialog_id[pos_id]

    if dialog_id is not None:
        close_asynch_dialog(pos_id, dialog_id)


def _store_current_asynchronous_dialog(pos_id, dialog_id):
    _close_current_asynchronous_dialog(pos_id)

    with _current_asynchronous_dialog_lock:
        _current_asynchronous_dialog_id[pos_id] = int(dialog_id)


def _get_current_asynchronous_dialog(pos_id):
    with _current_asynchronous_dialog_lock:
        return _current_asynchronous_dialog_id.get(pos_id, None)


def _is_electronic_payment_event(event_type):
    return event_type == "ELECTRONIC_PAYMENT"


def _send_message_response(pos_id, response):
    payment_service = get_electronic_payment_component_name(pos_id=pos_id)
    if not payment_service:
        return

    mb_context.MB_EasySendMessage(
        dest_name=payment_service,
        token=TK_ELECTRONIC_PAYMENT_SEND_RESPONSE,
        format=FM_PARAM,
        data="{}|{}".format(pos_id, response)
    )


def _show_message(pos_id, message, title):
    current_dialog_id = _get_current_asynchronous_dialog(pos_id)
    if current_dialog_id is None:
        dialog = show_message_dialog(
            pos_id,
            title,
            message,
            "NOCONFIRM",
            assync=True
        )
        _store_current_asynchronous_dialog(pos_id, dialog)
    else:
        update_any_dialog(
            pos_id, current_dialog_id, "messageDialog", message, title, "", "", "", 60000, "NOCONFIRM", "", "", "", None
        )


def _show_question(pos_id, message, title, buttons):
    _close_current_asynchronous_dialog(pos_id)
    _send_message_response(pos_id, show_messagebox(pos_id, message, title, buttons=buttons))


def _show_qrcode(pos_id, code_data, title):
    _close_current_asynchronous_dialog(pos_id)
    dialog_id = show_qr_code(pos_id, code_data, '$CANCEL', title)
    qrcode_dialog_id[pos_id] = dialog_id


def _show_input(pos_id, message, title, input_type):
    _close_current_asynchronous_dialog(pos_id)
    if not input_type or input_type == "none":
        _send_message_response(pos_id, show_messagebox(pos_id, message, title, buttons="$OK"))
    else:
        _send_message_response(pos_id, show_keyboard(pos_id, message, title, numpad=input_type == "number"))


def _show_choices(pos_id, message, title, choices):
    _close_current_asynchronous_dialog(pos_id)
    choices_dict = json.loads(choices)
    choices_keys = sorted(choices_dict.keys())
    choices_values = [choices_dict[i] for i in choices_keys]

    index = show_listbox(
        pos_id,
        choices_values,
        message=message,
        title=title,
        buttons="$OK|$CANCEL",
        icon="info",
        timeout=720000
    )
    if index is None:
        _cancel_current_payment(pos_id)

    _send_message_response(pos_id, choices_keys[index] if index is not None else "1")


def _show_numpad(pos_id, message, title):
    _close_current_asynchronous_dialog(pos_id)
    value = show_keyboard(pos_id, message, title=title, mask="INTEGER", numpad=True)
    if value is None:
        _cancel_current_payment(pos_id)
    _send_message_response(pos_id, value if value is not None else "")


def _get_pos_id_from_data(data):
    return data.split('\\0')[0]


def _get_pos_lock(data):
    with message_lock_dict:
        pos_id = _get_pos_id_from_data(data)
        if pos_id not in message_lock:
            message_lock[pos_id] = Lock()
        return message_lock[pos_id]


def _parse_event_and_call(func):
    def inner_func(params):
        data, subject, event_type = params[:3]
        if not _is_electronic_payment_event(event_type):
            return

        with _get_pos_lock(data):
            return func(*data.split('\\0'))

    return inner_func


def _dialog_resp_received(params):
    xml = params[0]
    dialog = etree.XML(xml).find("Dialog")
    dlg_id = int(dialog.get("id"))

    pos_id = None
    with _current_asynchronous_dialog_lock:
        for pos, dlg in _current_asynchronous_dialog_id.iteritems():
            if dlg == dlg_id:
                pos_id = pos
                break

    if not pos_id:
        return

    _cancel_current_payment(pos_id)
    _close_current_asynchronous_dialog(pos_id)


def _cancel_current_payment(pos_id):
    payment_service = get_electronic_payment_component_name(pos_id=pos_id)
    if not payment_service:
        return

    mb_context.MB_EasySendMessage(
        dest_name=payment_service,
        token=TK_ELECTRONIC_PAYMENT_CANCEL_PAYMENT,
        data="{}".format(pos_id)
    )


def _close_dialogs(pos_id):
    _close_current_asynchronous_dialog(pos_id)


def _close_qrcode(pos_id):
    dialog_id = qrcode_dialog_id[pos_id]
    close_asynch_dialog(pos_id, dialog_id)
    del qrcode_dialog_id[pos_id]


def init():
    pyscripts.subscribe_event_listener("SHOW_MESSAGE", _parse_event_and_call(_show_message))
    pyscripts.subscribe_event_listener("SHOW_QUESTION", _parse_event_and_call(_show_question))
    pyscripts.subscribe_event_listener("SHOW_QRCODE", _parse_event_and_call(_show_qrcode))
    pyscripts.subscribe_event_listener("SHOW_INPUT", _parse_event_and_call(_show_input))
    pyscripts.subscribe_event_listener("SHOW_CHOICES", _parse_event_and_call(_show_choices))
    pyscripts.subscribe_event_listener("SHOW_NUMPAD", _parse_event_and_call(_show_numpad))
    pyscripts.subscribe_event_listener("CLOSE_DIALOGS", _parse_event_and_call(_close_dialogs))
    pyscripts.subscribe_event_listener("CLOSE_QRCODE", _parse_event_and_call(_close_qrcode))
    pyscripts.subscribe_event_listener("DIALOG_RESP", _dialog_resp_received)
