from __future__ import with_statement

from hashlib import md5

import urllib2
import xmlrpclib

from .connector import ConnectorBase


class RPCConnector(ConnectorBase):

    DEFAULT_ENDPOINT = "https://services.ableton.com"

    def __init__(
        self,
        config,
        endpoint_prefix,
        session_id="",
        transport=None,
        urlopen=urllib2.urlopen,
        popen_factory=None,
        temp_dir_accessor=None,
    ):
        super(RPCConnector, self).__init__(
            config, popen_factory=popen_factory, temp_dir_accessor=temp_dir_accessor
        )
        self._frame = dict(sid=session_id)
        self.transport = transport
        self.proxies = {}
        self.urlopen = urlopen
        self.endpoint_prefix = endpoint_prefix.rstrip("/")

    def frame(self, frame=None):
        if frame is not None:
            self._frame.update(frame)
        else:
            return self._frame

    def call(self, handler):
        connector = self

        if handler not in self.proxies:
            self.proxies[handler] = xmlrpclib.ServerProxy(
                self.endpoint_prefix + "/" + handler,
                transport=self.transport,
                allow_none=True,
            )

        proxy = self.proxies[handler]

        class CallProxy(object):
            def __init__(self):
                self.method_names = []

            def __getattr__(self, method_name):
                self.method_names.append(method_name)
                return self

            def __call__(self, *args, **kwargs):
                method_name = ".".join(self.method_names)

                # pass our call-frame
                # and update it with the result
                frame = connector.frame()
                foo = getattr(proxy, method_name)(frame, args, kwargs)
                frame, res = foo
                connector.frame(frame)
                return res

        return CallProxy()

    def __repr__(self):  # pragma: no cover
        return "<%s %r>" % (self.__class__.__name__, self.frame())

    # Service Methods

    def upload_event_log(self, event_log_content):
        return self.call("reports").upload_event_log(xmlrpclib.Binary(event_log_content))

    def upload_log(self, run_id, kind, crashlog_content):
        return self.call("reports").upload_log(
            run_id, kind, xmlrpclib.Binary(crashlog_content)
        )

    def check_for_auto_updates(self, tags, current_version, update_to_version):
        return self.call("software").check_for_auto_updates(
            tags, current_version, update_to_version
        )

    def download_link_for_delta(self, delta_name):
        return self.call("software").download_link_for_delta(delta_name)

    # Base implementations
    def retrieve_delta_into(self, delta_name, dest):
        url = self.download_link_for_delta(delta_name)

        response = self.urlopen(url)

        h = md5()
        with dest.open("wb") as outf:
            block = response.read(4096)
            while block:
                outf.write(block)
                h.update(block)
                block = response.read(4096)

        return h.hexdigest()
