"""
adesklets commands handler module
"""
import re
from types import IntType
from error_handler import *
from singleton import _States

class _Commands_handler(_States):
    """
    Mid-Level command communication class.
    Handle command by sending it and waiting for its completion
    """
    def __init__(self):
        _States.__init__(self)
        self.__comm = self._States__shared_state['communicator']
        self.__command=''
        self.__record=True
        self.__re_command=re.compile('^command ([0-9]+) (ok|error): (.+)')
        self.__re_integer=re.compile('^-?[0-9]+$')
        self.__re_spaces=re.compile('\\s+')
        
    def write(self, command):
        """
        Give access to print chevron. This way, handler is used
        in a 'file-like' fashion.
        
        Example: print >> instance, 'window_resize', 100, 100

        We try to record the command 'as is' as long as it is
        less than 1K characters.
        """
        if command!='\n':
            if self.__record:
                if len(self.__command)+len(command)<1024:
                    self.__command+=command
                else:
                    if len(self.__command)>0:
                        print >> self.__comm, self.__command, command,
                    self.__record=False
            else:
                print >> self.__comm, command,
        else:
            if (self.__record):
                print >> self.__comm, self.__command
            else:
                print >> self.__comm
            self.__record=True
        
    def out(self):
        """
        Output routine. Should be called after each
        complete command emission using the print chevron. If it is not,
        all previous unprocessed command output will simply get discarded.

        This will block until adesklets finishes to process present command;
        hence, _never_ call this unless you already sent a complete command
        down the pipe.

        Returns a single value or None. If it is:
                1) an integer            , it is the return value ID
                                           (create_image and such)
        	2) a tuple of integers   , it is the numerical command output
                                           (context_get_color and such)
                3) a list of strings     , it is the ordered list of output
                                           strings (images_info and such)
                                           for commands
                                           that output multiple lines
                4) a description string  , a return line
                5) None                  , nothing of the above

        The routine first tries to retrieve integers from command message.
        If there is one or more integers present, it verifies message is
        not a mere copy of emitted command - if it is, it follows case 5
        (see above); otherwise, if only one integer if found, this is case 1;
        if more than one is found, case 2; if no integers are found and there
        is at list one line of output before command message, it then returns
        according to case 3. If return status is different from emited command,
        it is case 4. All remaining conditions are mapped on case 5.

        Things are done this fashion to automate adesklets.commands:
        this way, _Commands_handler.out() can be used pretty much everywhere to
        send back relevant command values.
        
        Note: in case of command error, an ADESKLETSError exception is raised
        instead (error 4: 'syntax error')
        
        """
        outputs=[]
        id=None
        message=''
        while 1:
            output=self.__comm.out(.01)
            if output:
                match=self.__re_command.match(output)
                if match:
                    # We have an output and a match
                    rank, status, message = match.groups()
                    if int(rank)==self.__comm.rank-1:
                        id=tuple([int(x) for x in message.split()
                                  if self.__re_integer.match(x)])
                        if status=='error':
                            raise ADESKLETSError(4,message)
                        else:
                            break
                    else:
                        outputs=[]
                else:
                    outputs.append(output.strip())
                    
        if len(id)>0:
            if len(id)==1:
                id=id[0]
            if len(message)<1024 and self.__record and re.sub('\s+',
                                            ' ',message).strip()==self.__command:
                return_value=None
            else:
                return_value=id
        else:
            if len(outputs)>0:
                return_value=outputs
            else:
                if len(message)<1024 and \
                       self.__record and re.sub('\s+',\
                          ' ',message).strip()!=self.__command:
                    return_value=re.sub('\s+', ' ', message).strip()
                else:
                    return_value=None

        self.__command=''
        self.__record=True
        return return_value
        
    def __del__(self):
        _States.__del__(self)

class _Static_commands_handler(_States,_Commands_handler):
   """
   Mid-level command class. It has the very same semantic as
   _Commands_handler. In fact, once constructed,
   it IS a _Commands_handler class: the one associated
   with the 'commands_handler' key of __States__shared_state,
   hence the _Commands_handler initialized by the _Initializer
   class for the whole package life-cycle.

   This is a trick not to reinstate a new _Commands_handler
   every time a command is performed in adesklets.functions,
   hence not having to recompile various regular expressions.

   Note: event if it is safe, as all others hidden class,
   this thing should never be instantiated directly from
   outside the package if it is not for debugging purposes.
   """
   def __init__(self):
      """
      This implementation is positively wicked... It dynamically
      overrides the object with a new one on creation. Weurk!
      At least, result is transparent.
      """
      _States.__init__(self)
      self.__dict__=self._States__shared_state['commands_handler'].__dict__
   def __del__(self):
      pass
