# sub_engine.py
#
# Copyright 2017 Eiichi Inohira
# This software may be modified and distributed under the terms
# of the MIT license
#
# For python 3
# For HRI Engine

"""sub_engine
"""

from __future__ import print_function

import os
import sys
import threading

from pyrois import RoIS_HRI, RoIS_Service

if sys.version_info.major == 2:
    import SocketServer
    import SimpleXMLRPCServer

    class ThreadingXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
        """ThreadingXMLRPCServer
        """
        pass
else:
    import socketserver
    import xmlrpc.server

    class ThreadingXMLRPCServer(socketserver.ThreadingMixIn, xmlrpc.server.SimpleXMLRPCServer):
        """ThreadingXMLRPCServer
        """
        pass


class SystemIF(RoIS_HRI.SystemIF):
    """SystemIF
    """
    def __init__(self, Engine):
        self._engine = Engine

    def connect(self):
        # print("connect")
        if self._engine.state is False:
            self._engine.state = True
            status = RoIS_HRI.ReturnCode_t.OK.value
        else:
            status = RoIS_HRI.ReturnCode_t.ERROR.value
        # time.sleep(30)
        return status

    def disconnect(self):
        if self._engine.state is True:
            self._engine.state = False
            status = RoIS_HRI.ReturnCode_t.OK.value
        else:
            status = RoIS_HRI.ReturnCode_t.ERROR.value
        #status = RoIS_HRI.ReturnCode_t.OK.value
        return status

    def get_profile(self, condition):
        status = RoIS_HRI.ReturnCode_t.OK.value
        # RoIS_HRI_Profile
        profile = "Unsupported"
        return (status, profile)

    def get_error_detail(self, error_id, condition):
        status = RoIS_HRI.ReturnCode_t.OK.value
        # ResultLists
        results = "None"
        return (status, results)


class CommandIF(RoIS_HRI.CommandIF):
    """CommandIF
    """
    def __init__(self, Engine):
        self._engine = Engine

    def search(self, condition):
        status = RoIS_HRI.ReturnCode_t.OK.value
        # List< RoIS_Identifier>
        component_ref_list = ["None"]
        return (status, component_ref_list)

    def bind(self, component_ref):
        # print("state:", self._engine.state)
        status = RoIS_HRI.ReturnCode_t.OK.value
        return status

    def bind_any(self, condition):
        status = RoIS_HRI.ReturnCode_t.OK.value
        component_ref_list = ["None"]
        return (status, component_ref_list)

    def release(self, component_ref):
        status = RoIS_HRI.ReturnCode_t.OK.value
        return status

    def get_parameter(self, component_ref):
        status = RoIS_HRI.ReturnCode_t.OK.value
        parameter_list = ["None"]
        return (status, parameter_list)

    def set_parameter(self, component_ref, parameters):
        status = RoIS_HRI.ReturnCode_t.OK.value
        command_id = "0"
        return (status, command_id)

    def execute(self, command_unit_list):
        status = RoIS_HRI.ReturnCode_t.OK.value
        return status

    def get_command_result(self, command_id, condition):
        status = RoIS_HRI.ReturnCode_t.OK.value
        results = ["None"]
        return (status, results)


class QueryIF(RoIS_HRI.QueryIF):
    """
    class QueryIF(object):
    """

    def __init__(self, Engine):
        self._engine = Engine

    def query(self, query_type, condition):
        status = RoIS_HRI.ReturnCode_t.OK.value
        results = ["None"]
        return (status, results)


class EventIF(RoIS_HRI.EventIF):
    """
    class QueryIF(object):
    """

    def __init__(self, Engine):
        self._engine = Engine

    def subscribe(self, event_type, condition):
        """
        subscribe(self, event_type, condition) -> (status,subscribe_id)
        """
        status = RoIS_HRI.ReturnCode_t.OK.value
        subscribe_id = "0"
        return (status, subscribe_id)

    def unsubscribe(self, subscribe_id):
        """
        unsubscribe(self,subscribe_id) -> status
        """
        status = RoIS_HRI.ReturnCode_t.OK.value
        return status

    def get_event_detail(self, event_id, condition):
        """
        get_event_detail(self,event_id,condition) -> (status,results)
        """
        status = RoIS_HRI.ReturnCode_t.OK.value
        results = ["None"]
        return (status, results)


from pyrois.Service_Application_Base_example import Service_Application_Base
from Navigation_client import Navigation_Client as NavClient
from System_Information_client import System_Information_Client as SysClient
from Dummy_Speech_Recognition_client import Speech_Recognition_Client as SRecogClient
from Person_Localization_client import Person_Localization_Client as PLClient
from pyrois.RoIS_Common import Component_Status
from pyrois.RoIS_Service import Completed_Status
import queue
import time

class IF(SystemIF, CommandIF, QueryIF, EventIF, Service_Application_Base):
    """IF
    """
    def __init__(self, Engine, engine_port):
        super().__init__(Engine)
        self.command_id = 0
        self.subscribe_id = 0
        self.event_id = 0
        self.event_queue = queue.Queue()
        self.component_event_queue = queue.Queue()
        self.compoent_clients = {
            'Navigation':          NavClient('http://localhost:' + str(engine_port+1)),
            'System_Information':  SysClient('http://localhost:' + str(engine_port+2)),
            'Speech_Recognition':  SRecogClient('http://localhost:' + str(engine_port+3), self.component_event_queue),
            'Person_Localization': PLClient('http://localhost:8043', self.component_event_queue)
        }
        self.command_id_status_table = {}
        self.event_ref_subscribe_id_table = {}
        self.event_id_result_table = {}

        th = threading.Thread(target=self.monitor_event, daemon=True)
        th.start()

    def set_parameter(self, component_ref, *parameters):
        status = None
        self.command_id += 1
        if not component_ref in self.compoent_clients:
            status = RoIS_HRI.ReturnCode_t.ERROR.value
        else:
            target_component_client = self.compoent_clients[component_ref]
            if component_ref == 'Navigation':
                status = target_component_client.set_parameter(*parameters).value
                if status == RoIS_HRI.ReturnCode_t.OK.value:
                    th = threading.Thread(
                            target=self.analyze_c_status,
                            daemon=True,
                            args=(target_component_client, str(self.command_id)))
                    th.start()
            elif component_ref == 'System_Information':
                status = RoIS_HRI.ReturnCode_t.OK.value
                result = target_component_client.robot_position()
                result = (result[0].value, result[1], result[2], result[3])  # FIXME
                self.command_id_status_table[str(self.command_id)] = result
        return (status, str(self.command_id))

    def get_command_result(self, command_id, condition):
        status = RoIS_HRI.ReturnCode_t.OK.value
        results = self.command_id_status_table[command_id]
        return (status, results)

    def analyze_c_status(self, component, command_id):
        while True:
            time.sleep(5)
            (_, component_status) = component.component_status()
            if component_status == Component_Status.READY:
                self.completed(command_id, Completed_Status.OK.value)
                return
            if component_status == Component_Status.ERROR:
                self.completed(command_id, Completed_Status.ABORT.value)
                return

    def monitor_event(self):
        while True:
            msg = self.component_event_queue.get()
            self.event_id += 1
            event_id = str(self.event_id)
            params, methodname = xmlrpc.client.loads(msg)
            #  print(f"Received event: methodname => {methodname}, params => {params}")
            if methodname == 'speech_recognized':
                timestamp, recognized_text, target_robot = params
                try:
                    subscribed_id = self.event_ref_subscribe_id_table['speech_recognized']
                except KeyError:
                    #  print("This is un-subscribed event. (speech_recognized)")
                    continue
                self.event_id_result_table[event_id] = params
                self.notify_event(event_id, 'speech_recognized', subscribed_id, "")
            elif methodname == 'person_localized':
                timestamp, person_ref, position_data = params
                try:
                    subscribed_id = self.event_ref_subscribe_id_table['person_localized']
                except KeyError:
                    #  print("This is un-subscribed event. (person_localized)")
                    continue
                self.event_id_result_table[event_id] = params
                self.notify_event(event_id, 'person_localized', subscribed_id, "")
            else:
                raise RuntimeError("[main_engine]Unknown method-name of event.")
            time.sleep(1)

    def subscribe(self, event_type, condition):
        status = RoIS_HRI.ReturnCode_t.OK.value
        self.subscribe_id += 1
        subscribe_id = str(self.subscribe_id)
        self.event_ref_subscribe_id_table[event_type+condition] = subscribe_id
        return (status, subscribe_id)

    def unsubscribe(self, subscribe_id):
        status = RoIS_HRI.ReturnCode_t.OK.value
        for key, value in self.event_ref_subscribe_id_table.items():
            if value == subscribe_id: event_ref = key
        del self.event_ref_subscribe_id_table[event_ref]
        return status

    def get_event_detail(self, event_id, condition):
        status = RoIS_HRI.ReturnCode_t.OK.value
        results = self.event_id_result_table[event_id]
        return (status, results)


class IF_server:
    """IF_Server
    """
    def __init__(self, port):
        self._addr = os.getenv("HRIENGINE")
        self._server = ThreadingXMLRPCServer(
            ("0.0.0.0", port), logRequests=False)

    def run(self, _IF):
        """IF_Server
        """
        self._server.register_instance(_IF)
        self._server.register_introspection_functions()
        # print("server running")
        self._server.serve_forever()


class RouteGuidanceEngine:
    """IF_Server
    """
    def __init__(self):
        self.state = False


def test_engine(port):
    """test_engine
    """
    IF_server(port).run(IF(RouteGuidanceEngine(), port))


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("py sub_engine.py <port>")
        raise RuntimeError

    port = int(sys.argv[1])
    test_engine(port)
