#!/usr/bin/python
#
# $Id: SimpleServerClient.py,v 1.2 2009-12-02 14:46:44 jloden Exp $
#
# REQUIRES: re module, functools (part of Python 2.5)
#

import re
import sys

# functools is Python 2.5 only, so we create a different partialfn if we are
# running a version without functools available
try:
    import functools
    partialfn = functools.partial
except ImportError:
    def partialfn(func, *args, **keywords):
        def newfunc(*fargs, **fkeywords):
            newkeywords = keywords.copy()
            newkeywords.update(fkeywords)
            return func(*(args + fargs), **newkeywords)
        newfunc.func = func
        newfunc.args = args
        newfunc.keywords = keywords
        return newfunc

# Traverse module local imports
from pytraverse import NameSpace
from pytraverse.SocketServer import SocketServer
from pytraverse.error import *


class SimpleServerClient(SocketServer):
    """
    Inherits from Traverse.SocketServer, used as a base class for all Traverse
    API client types (Provisioning, EDF, ISM). the _get_cmd_name() method is
    the only method required to be overriden, all others have default
    implementations.
    """

    def __getattr__(self, name):
        """Overloaded __getattr__() call handles dynamic method names at runtime."""
        try:
            # if it's already defined, just return it normally
            return getattr(self.__class__, name)
        except AttributeError:
            # otherwise, return  a partial function that
            # passes our name from __getattr__ over as if _api_func
            # were defined as _api_func(self, name='name, **args)
            # this way _api_func can know what its own name is for
            # reflection purposes. If the name doesn't match our known
            # API format, raise AttributeError
            try:
                self._get_cmd_name(name)
            except InvalidCommand:
                raise AttributeError("'%s' class has no attribute %s" % (self.__name__, name))
            return partialfn(self._api_func, name)


    def _get_cmd_name(self, method_name):
        """Take a method name as input, return a parsed API command of the form
        object.action - must be overriden in the client class. Should raise
        InvalidCommand exception if the name is not valid for this namespace.

        Raises NotImplementedError if not overriden in the client class.
        """

        raise NotImplementedError("_get_cmd_name() must be overriden by the client class!")


    def _mk_cmd(self, name, **args):
        """
        Given a method name and args, return a translated BVE API command. with
        the arguments quoted/formatted appropriately.

        Example:
            ListDevice(deviceName="foo") -> device.list "deviceName=foo".
        """

        cmd = self._get_cmd_name(name) # raises an exception if the command parsing fails
        cmd_args = ""
        for i in args:
            cmd_args += '"%s=%s",' % (i, args[i])
        return cmd + " " + cmd_args


    def _api_func(self, name, **args):
        """
        Execute API command string. Return True/False for success/failure
        response code from BveAPI. Raises NotConnectedError if not connected
        to the socket server.
        """

        # name is passed over as part of the partial function
        # definition from our overloaded __getattr__
        cmd = self._mk_cmd(name, **args)
        self._api_send(cmd)
        return self.GetSuccess()


    def _api_send(self, cmd, api_waitfor_reply=True):
        """
        Issue the BVE API command and call _parse_response() to parse the API
        response code/message. Calls SocketIO.api_send(), which Raises
        NotConnectedError if not connected to an API socket server, and
        SocketSendError on failures sending.
        """

        self.mysock.api_send(cmd)
        if api_waitfor_reply:
            self._parse_response()


