# -*- coding: utf-8 -*-
"""HydraTK core commmunication messages processing fuctionality

.. module:: core.messagehead
   :platform: Unix
   :synopsis: HydraTK core commmunication messages processing fuctionality
.. moduleauthor:: Petr Czaderna <pc@hydratk.org>

"""

import multiprocessing
import zmq
import base64
import sys
import traceback
import time
from hydratk.lib.debugging.simpledebug import dmsg

try:
    import cPickle as pickle
except ImportError:
    import pickle


class MessageHead(object):
    """Class MessageHead
    """

    _async_ticket_seq = 0
    _async_ticket_seq_max = 9999999999
    _current_async_ticket_id = None

    def _new_async_ticket_id(self):
        """Method creates new ticket

        Args:
           none

        Returns:            
           str: ticket id

        """

        if self._async_ticket_seq == self._async_ticket_seq_max:
            self._reset_async_ticket_seq()
        self._async_ticket_seq += 1
        thrid = '0' if multiprocessing.current_process(
        ).name == 'MainProcess' else multiprocessing.current_process().name
        ticket_id = "{timestamp}-{thrid}-{ticket_seq}".format(
            timestamp=time.time(), thrid=thrid, ticket_seq=self._async_ticket_seq)

        return ticket_id

    def _new_async_ticket(self, ticket_id):
        """Method creates dummy ticket

        Args:
           ticket_id (str): ticket

        Returns:            
           void

        """

        self._async_fn_tickets[ticket_id] = {
            'completed': False,
            'error_no': None,
            'error_msg': '',
            'data': None
        }

    def _delete_async_ticket(self, ticket_id):
        """Method deletes ticket

        Args:
           ticket_id (str): ticket

        Returns:            
           void

        Raises:
           error: KeyError
           error: TypeError

        """

        if ticket_id is not None and ticket_id != '':
            if ticket_id in self._async_fn_tickets:
                del self._async_fn_tickets[ticket_id]
            else:
                raise KeyError(
                    "Ticket id: {0} doesn't exists".format(ticket_id))
        else:
            raise TypeError(
                "Invalid ticket_id: {0}".format(type(ticket_id).__name__))

    def _reset_async_ticket_seq(self):
        """Method resets ticket id sequence

        Args:
           none

        Returns:            
           void

        """

        self._async_ticket_seq = 0

    def _reg_msg_handlers(self):
        """Method registers message handlers to functionality hooks

        Args:
           none

        Returns:            
           void

        """

        hook = [
            {'fn_id': 'cmsg_async_fn_ex', 'callback': self._msg_async_fn_ex},
            {'fn_id': 'cmsg_async_fn',    'callback': self._msg_async_fn},
            {'fn_id': 'cmsg_async_ext_fn', 'callback': self._msg_async_ext_fn}
        ]
        self.register_fn_hook(hook)

    def _process_cmsg(self, ev, msg):
        """Method processes message

        Message contains functionality hook to be executed

        Args:
           ev: not used
           msg (obj): message in Base64 format

        Returns:            
           void

        """

        pickled = base64.b64decode(msg)
        msg = pickle.loads(pickled)
        dmsg("Processing message: {0}".format(msg))
        if type(msg).__name__ == 'dict' and 'type' in msg and msg['type'] is not None and msg['type'] != '':
            fn_id = "cmsg_{0}".format(msg['type'])
            dmsg("Running hook {0}".format(fn_id), 3)
            self.run_fn_hook(fn_id, msg)
        else:
            dmsg("Invalid message {0}".format(str(msg)), 3)

    def _send_msg(self, msg):
        """Method sends message

        Args:
           msg (dict): message

        Returns:            
           bool: True

        """

        current = multiprocessing.current_process()
        mq = current.msgq
        msg = base64.b64encode(pickle.dumps(msg))
        try:
            mq.send(msg)  # zmq.NOBLOCK
        except zmq.ZMQError as exc:
            ex_type, ex, tb = sys.exc_info()
            print(ex_type)
            print(ex)
            traceback.print_tb(tb)
        #print("Message send successfully {0}".format(msg))
        return True

    def _msg_async_fn(self, msg):
        print("Processing async_fn")
        return True

    def _msg_async_fn_ex(self, msg):
        print("Processing async_fn_ext")
        return True

    def _msg_async_ext_fn(self, msg):
        """Method runs funcionality hook callback from message

        Args:
           msg (dict): message

        Returns:            
           bool: True

        """

        ext_name = msg['data']['callback']['ext_name']
        meth = msg['data']['callback']['method']
        args = msg['data']['callback']['args']
        kwargs = msg['data']['callback']['kwargs']
        self._current_async_ticket_id = msg['data']['ticket_id']
        ticket_id = msg['data']['ticket_id']
        ticket_content = self._async_fn_tickets[ticket_id]
        cb = getattr(self.get_ext(ext_name), meth)
        if callable(cb):
            cb_error_no = None
            cb_error_msg = ''
            try:
                cb_result = cb(*args, **kwargs)
                if type(cb_result).__name__ == 'tuple':
                    cb_error_no, cb_error_msg = cb_result
            except Exception as ex:
                cb_error_no = -1
                ex_type, ex, tb = sys.exc_info()
                print(ex_type)
                print(ex)
                traceback.print_tb(tb)

            ticket_content['completed'] = True
            ticket_content['error_no'] = cb_error_no
            ticket_content['error_msg'] = cb_error_msg
            self._async_fn_tickets[ticket_id] = ticket_content
        else:
            print("Invalid async_ext_fn callback")
            print(msg)
        return True
