########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/FtRpc/Commands.py,v 1.9 2005/03/22 09:58:58 mbrown Exp $
"""
RPC server commands

Copyright 2005 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""

from Ft.Server import Client
from Ft.Server.FtRpc import FtServerFtRpcException, Error
from Ft.Server.FtRpc import CommandCodes, ResponseCodes

import Marshal, Responses

class FtRpcCommand:
    expected = None
    def __init__(self):
        pass

    def send(self, stream):
        """Send a Command and get a response from the server"""

        Marshal.Send(stream, self.command,self.data)
        rspCode, data = Marshal.Receive(stream)

        if rspCode not in Responses.g_responseMapping:
            raise FtServerFtRpcException(Error.UNKNOWN_RESPONSE,
                                         rspCode = rspCode)

        #Handle some general stuff
        if rspCode == ResponseCodes.GENERAL_ERROR:
            raise FtServerFtRpcException(Error.SERVER_ERROR,
                                         traceback=data['traceback']
                                         )
        # In the event of an error response from the server, the data will be
        # an FtServerServerException. We want to try to return this to the
        # client as an FtServerClientException.
        #FIXME: this seems wrong.  If the exception was on the server, then why are we returning it as a client exception?
        elif rspCode == ResponseCodes.FTSERVER_ERROR:
            errorCode = data['errorCode']
            if errorCode in vars(Client.Error).values():
                params = data['params']
                if isinstance(params, dict):
                    raise Client.FtServerClientException(errorCode, **params)
                else:
                    raise Client.FtServerClientException(errorCode, *params)
            else:
                raise FtServerFtRpcException(Error.FTSERVER_ERROR,
                                             message = data['error']
                                             )

        if self.expected and rspCode != self.expected:
            raise FtServerFtRpcException(Error.INVALID_SERVER_RESPONSE,
                                         got=rspCode,
                                         expected=expected)

        return apply(Responses.g_responseMapping[rspCode], (), data)


class LoginCommand(FtRpcCommand):
    """
    Login a user to the repository
    """
    command = CommandCodes.LOGIN
    expected = ResponseCodes.OK
    def __init__(self,userName,password):
        self.userName = userName
        self.password = password
        self.data = {'userName':userName,
                     'password':password}

class LogoutCommand(FtRpcCommand):
    """
    Logout a user from the repository
    """
    command = CommandCodes.LOGOUT
    expected = ResponseCodes.OK
    def __init__(self,commit):
        self.commit = commit
        self.data = {'commit':commit}


class ResourceTypeCommand(FtRpcCommand):
    """
    This command will return the resource type of an object given a certain path
    It can also be used to check for the exsistence of an object.  It always returns an error or
    a resource type response
    """
    command = CommandCodes.RESOURCE_TYPE
    expected = ResponseCodes.RESOURCE_TYPE
    def __init__(self, path, baseObject):
        self.path = path
        self.baseObject = baseObject
        self.data = {'path':path,
                     'baseObject':baseObject,
             }
        FtRpcCommand.__init__(self)



class RemoteMethodCommand(FtRpcCommand):
    """
    Execute a Remote Method on an object.  Return its results
    """
    command = CommandCodes.REMOTE_METHOD
    expected = ResponseCodes.METHOD_RESULTS
    def __init__(self, methodName, baseObject, args):
        self.methodName = methodName
        self.baseObject = baseObject
        self.args = args
        self.data = {'baseObject':baseObject,
                     'args':args,
                     'methodName':methodName}
        FtRpcCommand.__init__(self)


class RemoteRdfCommand(FtRpcCommand):
    """
    Execute a Remote Method on the model.  Return its results
    """
    command = CommandCodes.REMOTE_RDF
    expected = ResponseCodes.RDF_RESULTS
    def __init__(self,methodName,baseObject,args,kw=None):
        self.methodName = methodName
        self.baseObject = baseObject
        self.args = args
        self.kw = kw or {}
        self.data = {'baseObject':baseObject,
                     'args':args,
                     'kw':kw,
                     'methodName':methodName}
        FtRpcCommand.__init__(self)


g_commandMapping = {CommandCodes.LOGIN:LoginCommand,
                    CommandCodes.LOGOUT:LogoutCommand,
                    CommandCodes.RESOURCE_TYPE:ResourceTypeCommand,
                    CommandCodes.REMOTE_METHOD:RemoteMethodCommand,
                    CommandCodes.REMOTE_RDF:RemoteRdfCommand,
                 }
