# encoding: utf-8

"""A class that manages the engines connection to the controller."""

__docformat__ = "restructuredtext en"

#-------------------------------------------------------------------------------
#  Copyright (C) 2008  The IPython Development Team
#
#  Distributed under the terms of the BSD License.  The full license is in
#  the file COPYING, distributed as part of this software.
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
# Imports
#-------------------------------------------------------------------------------

import os
import cPickle as pickle

from twisted.python import log, failure
from twisted.internet import defer

from IPython.kernel.fcutil import find_furl
from IPython.kernel.enginefc import IFCEngine

#-------------------------------------------------------------------------------
# The ClientConnector class
#-------------------------------------------------------------------------------

class EngineConnector(object):
    """Manage an engines connection to a controller.
    
    This class takes a foolscap `Tub` and provides a `connect_to_controller` 
    method that will use the `Tub` to connect to a controller and register
    the engine with the controller.
    """
    
    def __init__(self, tub):
        self.tub = tub
        
    def connect_to_controller(self, engine_service, furl_or_file):
        """
        Make a connection to a controller specified by a furl.
        
        This method takes an `IEngineBase` instance and a foolcap URL and uses
        the `tub` attribute to make a connection to the controller.  The 
        foolscap URL contains all the information needed to connect to the 
        controller, including the ip and port as well as any encryption and 
        authentication information needed for the connection.
        
        After getting a reference to the controller, this method calls the 
        `register_engine` method of the controller to actually register the 
        engine.
        
        :Parameters:
            engine_service : IEngineBase
                An instance of an `IEngineBase` implementer
            furl_or_file : str
                A furl or a filename containing a furl
        """
        if not self.tub.running:
            self.tub.startService()
        self.engine_service = engine_service
        self.engine_reference = IFCEngine(self.engine_service)
        try:
            self.furl = find_furl(furl_or_file)
        except ValueError:
            return defer.fail(failure.Failure())
        else:
            d = self.tub.getReference(self.furl)
            d.addCallbacks(self._register, self._log_failure)
            return d
    
    def _log_failure(self, reason):
        log.err('EngineConnector: engine registration failed:')
        log.err(reason)
        return reason

    def _register(self, rr):
        self.remote_ref = rr
        # Now register myself with the controller
        desired_id = self.engine_service.id
        d = self.remote_ref.callRemote('register_engine', self.engine_reference, 
            desired_id, os.getpid(), pickle.dumps(self.engine_service.properties,2))
        return d.addCallbacks(self._reference_sent, self._log_failure)

    def _reference_sent(self, registration_dict):
        self.engine_service.id = registration_dict['id']
        log.msg("engine registration succeeded, got id: %r" % self.engine_service.id)
        return self.engine_service.id

