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

import bustoken
import pyscripts
from msgbus import FM_PARAM
from mw_helper import show_message_dialog
from sysactions import close_asynch_dialog, show_messagebox, get_used_service, get_model, show_any_dialog, \
    show_keyboard, show_listbox

TK_ELECTRONIC_PAYMENT_SEND_RESPONSE = bustoken.create_token("0", "0E", "5")
TK_ELECTRONIC_PAYMENT_CANCEL_PAYMENT = bustoken.create_token("0", "0E", "2")

mbcontext = pyscripts.mbcontext
_current_dialog_id = {}
_current_dialog_lock = Lock()


def _close_current_dialog(pos_id):
    dialog_id = None
    with _current_dialog_lock:
        if pos_id in _current_dialog_id:
            dialog_id = _current_dialog_id[pos_id]
            del _current_dialog_id[pos_id]

    if dialog_id is not None:
        logger.debug("will close dialog {}".format(dialog_id))
        close_asynch_dialog(pos_id, dialog_id)


def _store_current_dialog(pos_id, dialog_id):
    _close_current_dialog(pos_id)

    with _current_dialog_lock:
        _current_dialog_id[pos_id] = int(dialog_id)


def _get_current_dialog(pos_id):
    with _current_dialog_lock:
        return _current_dialog_id.get(pos_id, None)


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


def _send_message_response(pos_id, response):
    payment_service = get_used_service(get_model(pos_id), "electronicpayment")
    if not payment_service:
        return
    mbcontext.MB_EasySendMessage(payment_service, token=TK_ELECTRONIC_PAYMENT_SEND_RESPONSE,
                                 format=FM_PARAM, data="{}|{}".format(pos_id, response))


def _show_message(pos_id, message, title):
    _close_current_dialog(pos_id)
    _store_current_dialog(pos_id, show_message_dialog(pos_id, title, message, "NOCONFIRM", assync=True))


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


def _show_barcode(pos_id, code_data, title, code_type):
    _close_current_dialog(pos_id)
    logger.debug("will show barcode")
    _store_current_dialog(pos_id, show_any_dialog(
        pos_id, "barcode", code_data, title, "", "$CANCEL",
        code_type, 60000, "", "", "", "", "", True))
    logger.debug("barcode is shown")


def _show_input(pos_id, message, title, input_type):
    _close_current_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_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 "")


def _show_numpad(pos_id, message, title):
    _close_current_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_posid_from_data(data):
    return data.split('\\0')[0]


message_lock_dict = Lock()
message_lock = {}


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


logger = logging.getLogger("PosActions")


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

        with _get_pos_lock(data):
            logger.debug("electronic event: {} data {}".format(func, 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_dialog_lock:
        for pos, dlg in _current_dialog_id.iteritems():
            if dlg == dlg_id:
                pos_id = pos
                break

    if not pos_id:
        return

    _cancel_current_payment(pos_id)
    _close_current_dialog(pos_id)


def _cancel_current_payment(pos_id):
    payment_service = get_used_service(get_model(pos_id), "electronicpayment")
    if not payment_service:
        return
    mbcontext.MB_EasySendMessage(payment_service, token=TK_ELECTRONIC_PAYMENT_CANCEL_PAYMENT)


def _close_dialogs(pos_id):
    _close_current_dialog(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_BARCODE", _parse_event_and_call(_show_barcode))
    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("DIALOG_RESP", _dialog_resp_received)
