"""
Low-level adesklets communicator module
"""
import tempfile
import signal
import os
import sys
import select

from error_handler import *
from singleton import _States

class _Communicator:
   """
   Low-level communication handler with adesklets standalone
   binary. Its job is to:
   
   - Start adesklets program in a forked process with correct
     named pipes plugged into stdin, stdout and stderr.
   - Monitor its health
   - Let us talk to it through print statements
   - Let us retrieve back data from its stdout and stderr,
     one line at a time.
   """
   def __init__(self, program="adesklets", args=[ ' :' ]):
      self.__infile  = tempfile.mktemp('adesklets_fifo')
      self.__outfile = tempfile.mktemp('adesklets_fifo')
      self.__errfile = tempfile.mktemp('adesklets_fifo')

      os.mkfifo(self.__infile,0600)
      os.mkfifo(self.__outfile,0600)
      os.mkfifo(self.__errfile,0600)

      self.__pid=os.fork()
      if self.__pid==0:
         os.dup2(os.open(self.__infile,os.O_RDONLY),0)
         os.dup2(os.open(self.__outfile,os.O_WRONLY),1)
         os.dup2(os.open(self.__errfile,os.O_WRONLY),2)
         args.insert(0,program)
         try:
            os.execvp(program,args)
         except OSError, (errno, strerror):
            print errno,strerror, program, args
         os._exit(1)
      while 1:
         try:
            self.__stdin=file(self.__infile,"w",0)
            self.__stdout=file(self.__outfile,"r",0)
            self.__stderr=file(self.__errfile,"r",0)
         except IOError:
            pass
         else:
            break
      self.rank=0

   def active(self):
      """
      Test if child process still exists
      """
      try:
         os.waitpid(self.__pid,os.WNOHANG)
      except OSError:
         return False
      else:
         return True
         
   def write(self,command):
      """
      Give access to print chevron. This way, communicator is used
      in a 'file-like' fashion.
      
      Example: print >> instance, 'window_resize', 100, 100
      """
      if self.active():
         print >> self.__stdin, command,
         if command=='\n':
            self.rank+=1
      else:
         raise ADESKLETSError(1)
      
   def out(self, delay=0):
      """
      Return a line from child stdout if available.
      An optionnal waiting delay can be specified.
      """
      if self.active():
         rd, wr, ex = select.select([self.__stdout],[],[],delay)
         if len(rd)==1:
            return self.__stdout.readline()
      else:
         raise ADESKLETSError(1)
      
   def err(self, delay=0):
      """
      Return a line from child stderr if available.
      An optionnal waiting delay can be specified.
      """
      if self.active():
         rd, wr, ex = select.select([self.__stderr],[],[],delay)
         if len(rd)==1:
            return self.__stderr.readline()
      else:
         raise ADESKLETSError(1)
   
   def __del__(self):
      try:
         os.kill(self.__pid,signal.SIGTERM)
      except OSError:
         pass
      self.__stdin.close()
      os.unlink(self.__infile)
      self.__stdout.close()
      os.unlink(self.__outfile)
      self.__stderr.close()
      os.unlink(self.__errfile)
