import os
import time
import logging
import threading
from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.server import SimpleXMLRPCRequestHandler
import xmlrpc.client
import json

LOG = logging.getLogger(__name__)
LOCAL = "127.0.0.1"
LOCALPORT = 2001
DEVICE_DESCRIPTIONS = "devicetypes/json/device_descriptions.json"

class LockingServerProxy(xmlrpc.client.ServerProxy):
    """
    ServerProxy implementation with lock when request is executing
    """

    def __init__(self, *args, **kwargs):
        """
        Initialize new proxy for server and get local ip
        """
        self.lock = threading.Lock()
        xmlrpc.client.ServerProxy.__init__(self, *args, **kwargs)

    def __request(self, *args, **kwargs):
        """
        Call method on server side
        """

        with self.lock:
            parent = xmlrpc.client.ServerProxy
            # pylint: disable=E1101
            return parent._ServerProxy__request(self, *args, **kwargs)

    def __getattr__(self, *args, **kwargs):
        """
        Magic method dispatcher
        """

        return xmlrpc.client._Method(self.__request, *args, **kwargs)

# Object holding the methods the XML-RPC server should provide.
class RPCFunctions():
    def __init__(self):
        LOG.debug("RPCFunctions.__init__")
        self.remotes = {}
        try:
            script_dir = os.path.dirname(__file__)
            rel_path = DEVICE_DESCRIPTIONS
            with open(os.path.join(script_dir, rel_path)) as fptr:
                self.devices = json.load(fptr)
        except Exception as err:
            LOG.debug("RPCFunctions.listDevices: Exception: %s" % err)
            self.devices = []

    def _askDevices(self, interface_id):
        LOG.debug("RPCFunctions._askDevices: waiting")
        time.sleep(0.5)
        knownDevices = self.remotes[interface_id].listDevices(interface_id)
        LOG.debug("RPCFunctions._askDevices: %s" % knownDevices)
        t = threading.Thread(name='_pushDevices', target=self._pushDevices, args=(interface_id, ))
        t.start()

    def _pushDevices(self, interface_id):
        LOG.debug("RPCFunctions._pushDevices: waiting")
        time.sleep(0.5)
        self.remotes[interface_id].newDevices(interface_id, self.devices)
        LOG.debug("RPCFunctions._pushDevices: pushed")

    def listDevices(self, interface_id):
        LOG.debug("RPCFunctions.listDevices: interface_id = %s" % interface_id)
        return self.devices

    def getServiceMessages(self):
        LOG.debug("RPCFunctions.getServiceMessages")
        return [['VCU0000001:1', 'ERROR', 7]]

    def getValue(self, address, value_key):
        LOG.debug("RPCFunctions.getValue: address=%s, value_key=%s" % (address, value_key))
        return True

    def setValue(self, address, value_key, value):
        LOG.debug("RPCFunctions.getValue: address=%s, value_key=%s, value=%s" % (address, value_key, value))
        return ""

    def init(self, url, interface_id=None):
        LOG.debug("RPCFunctions.init: url=%s, interface_id=%s" % (url, interface_id))
        if interface_id:
            try:
                self.remotes[interface_id] = LockingServerProxy(url)
                t = threading.Thread(name='_askDevices', target=self._askDevices, args=(interface_id, ))
                t.start()
            except Exception as err:
                LOG.debug("RPCFunctions.init:Exception: %s" % (err))
        return ""

class RequestHandler(SimpleXMLRPCRequestHandler):
    """We handle requests to / and /RPC2"""
    rpc_paths = ('/', '/RPC2',)

class ServerThread(threading.Thread):
    """XML-RPC server thread to handle messages from CCU / Homegear"""
    def __init__(self, local=LOCAL, localport=LOCALPORT):
        self._local = local
        self._localport = localport
        LOG.debug("ServerThread.__init__")
        threading.Thread.__init__(self)

        # Create proxies to interact with CCU / Homegear
        LOG.debug("__init__: Registering RPC methods")
        self._rpcfunctions = RPCFunctions()

        # Setup server to handle requests from CCU / Homegear
        LOG.debug("ServerThread.__init__: Setting up server")
        self.server = SimpleXMLRPCServer((self._local, self._localport),
                                         requestHandler=RequestHandler,
                                         logRequests=False)
        self._localport = self.server.socket.getsockname()[1]
        self.server.register_introspection_functions()
        self.server.register_multicall_functions()
        LOG.debug("ServerThread.__init__: Registering RPC functions")
        self.server.register_instance(
            self._rpcfunctions, allow_dotted_names=True)

    def run(self):
        LOG.info("Starting server at http://%s:%i" %
                 (self._local, self._localport))
        self.server.serve_forever()

    def stop(self):
        """Shut down our XML-RPC server."""
        LOG.info("Shutting down server")
        self.server.shutdown()
        LOG.debug("ServerThread.stop: Stopping ServerThread")
        self.server.server_close()
        LOG.info("Server stopped")
