from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import ServerFactory
from twisted.internet import defer, task
#from twisted.web import client
import lx200msgs as lx
import logging
#from decra.ephem import Ephem 
import time

from decra import decra as eph

log = logging.getLogger("Lx200")

EPHEM=eph.dataSet()

class lx200Protocol(LineReceiver):

   version="1"
   line_mode = 1
   __buffer = ''
   delimiter = '\r\n'
   MAX_LENGTH = 16384
   state='UNAUTH'
   ccall = False
   crondelta = 2.5
   minute = False
   posnow = '0'
   lastpos = '0'
   lastlastpos = '0'
   is_moving = False
   is_focusing = False
   is_synching = False
   lastFWHM = False
   fwhmnow = '0,00'
   fwhm = '0,00'
   startSynching = False
   startFocusing = False
   connstatus = 'unknown'
   roof='Unknown'

   def lineReceived(self, line):
      if self.logged or line[:4] == 'AUTH':
         try:
            log.debug("Received from Telescope "+str(self.tnum)+": "+line)
         except:
            log.debug("Received from Telescope: "+line)
         
         act = self.factory.parser.parseMsg(line)
         if act and len(act) > 0:
            self.on_callback(act)
         if self.connstatus == 'offline':
            self.on_callback({'setStatus': 'online'})
      else:
         log.debug("ARRIVATA LINEA ERRATA DAL TELE: "+str(line))
         self.on_close('What?')
      
   def on_callback(self, cmd):
      for key in cmd:
         f=getattr(self, 'on_'+key, None)
         if f and callable(f):
            return f(cmd[key])
         else:
            try:
               return self.factory.command(self.tnum, key, cmd[key])
            except:
               try:
                  log.debug("Telescope %s command %s failed" %(str(self.tnum), key))
               except:
                  log.debug("Telescope command %s failed" %(key))
               return False

   def canAcceptCommand(self):
      if not self.is_moving and not self.is_synching and not self.is_focusing:
         return True
      return False


   def connectionMade(self):
      log.debug("New telescope Connection")
      self.logged = False
      self.askForAuth()
      
   def askForAuth(self):
      self.write(lx.reqauth())


   def connectionLost(self, reason):
      log.debug("TELESCOPE CONNECTION LOST")
      self.state='UNAUTH'
      self.on_sendPhotoYes(True)
      self.logged=False
      log.debug("Telescope set status offline")
      self.on_callback({'setStatus': 'offline'})
      self.on_callback({'setConnection':  [False, None] })
      try:
         self.stopCronCalls()
      except:
         pass

   def write(self, msg):
      log.debug("WRITE TO LX200: "+str(msg))
      self.transport.write(msg)

   def on_AUTH(self, uspw):
      try:
         self.stopCronCalls()
      except:
         pass
      self.factory.getAuth(uspw['usr'], uspw['pwd']
            ).addErrback(self.on_Error
            ).addCallback(self.AuthDone)

   def on_PING(self, *args, **kwargs):
      self.write(lx.PONG())

   def on_ROOFCLOSED(self, *args, **kwargs):
      log.debug("Setting roof to Closed")
      self.roof='Closed'

   def on_ROOFOPEN(self, *args, **kwargs):
      self.roof='Open'
      log.debug("Setting roof to Open")


   def on_getRaDec(self, name):
      log.debug("telescope is Asking for "+str(name)+" coordinates")
      ret = EPHEM.getDECRA(str(name).upper())
      if ret['ra'] and ret['dec']:
         self.write(lx.radec(ret['ra'], ret['dec']))
      else:
         self.write(lx.radec())



   def AuthDone(self, res=False):
      if res:
         self.logged = True
         self.tname = res
         self.tnum = self.cfg.get(self.tname, 'number')
         self.on_callback({'setConnection':  [True, self] })
         self.on_callback({'setStatus': 'online'})
         self.startCronCalls()
         self.write(lx.authok())
      else:
         log.warning("Wrong Auth received")
         self.on_close('WRONG AUTH')

   def on_Error(self, err='Unknown'):
      log.warning('ERRORE: '+err)

   def on_close(self, why=None):
      if why:
         self.write(why)
      self.close()

   def close(self):
      self.transport.loseConnection()

   def startCronCalls(self):
      if not self.ccall:
         self.ccall = task.LoopingCall(self.cronCalls)
         self.ccall.start(self.crondelta)

   def stopCronCalls(self):
      try:
         self.ccall.stop()
         self.ccall = False
      except:
         pass

   def cronCalls(self, *args):
      is_moving = True
      now = time.time()
      if self.lastpos == self.posnow:
         if self.lastlastpos == self.posnow:
            is_moving = False
      if not self.minute:
         self.minute = time.time()
      if int(now-self.minute) > 60 and not self.is_focusing:
         self.minute=time.time()
         self.write(lx.getFWHM())
      self.lastlastpos = self.lastpos
      self.lastpos = self.posnow   
      self.is_moving = is_moving
      if self.startFocusing and self.is_focusing:
         if int(now-self.startFocusing) > 40:
            if int(now-self.lastFWHM) > 40:
               self.on_autoFocusStop()
      if self.startSynching and self.is_synching:
         if int(now-self.startSynching) > 60:
            self.on_syncStop()

   def on_sendMoveCARDINAL(self, where):
      self.posnow = 'Moving'
      self.is_moving = True
      self.write(lx.moveCARDINAL(where))

   def on_sendMovePlanets(self, code, name):
      self.posnow = 'Moving'
      self.is_moving = True
      self.write(lx.movePLANETS(code, name))

   def on_sendMoveARDEC(self, ar, dec, desc=False):
      self.posnow = 'Moving'
      self.is_moving = True
      self.write(lx.moveARDEC(ar, dec, desc))

   def on_sendMoveMessier(self, messier):
      self.posnow = 'Moving'
      self.is_moving = True
      self.write(lx.moveMESSIER(messier))

   def on_sendMoveCngc(self, cngc):
      self.posnow = 'Moving'
      self.is_moving = True
      self.write(lx.moveCNGC(cngc))

   def on_sendCenter(self, center):
      self.posnow = 'Moving'
      self.is_moving = True
      self.write(lx.centre(center))

   def on_sendSETCCDTemp(self, temp):
      self.write(lx.SETCCDTemp(temp))

   def on_makePhoto(self, exp, codestr, username):
      self.write(lx.FOTO(exp, codestr, username))
 
   def on_autoFocus(self):
      self.write(lx.AUTOFOCUS())

   def on_focusPlus(self):
      self.write(lx.FOCUSPLUS())

   def on_focusLess(self):
      self.write(lx.FOCUSLESS())

   def on_getFwhm(self):
      self.write(lx.getFWHM())

   def on_sync(self):
      self.write(lx.SYNC())

   def on_sendPhotoYes(self, *args):
      self.on_callback({'photoIsEnd': True})

   def on_recObject(self, ob):
      self.on_callback({'setObject': str(ob)})

   def on_recGuideError(self, errors):
      self.on_callback({'setGuideError': errors})

   def on_telescopeStarted(self, *args):
      self.on_callback({'setTelescopeStarted': True})

   def on_telescopeStopped(self, *args):
      self.on_callback({'setTelescopeStarted': False})

   def on_setPOSITION(self, *args):
      self.posnow = str(args[0])
      self.on_callback({'setCOORDINATE': args[0]})

   def on_setFWHM(self, *args):
      self.lastFWHM = time.time()
      self.fwhmnow = str(args[0])
      if self.fwhmnow != self.fwhm:
         self.fwhm = self.fwhmnow
         self.on_callback({'settingFWHM': args[0]})

   def on_autoFocusStart(self, *args):
      self.startFocusing = time.time()
      self.is_focusing = True
      self.on_callback({'setAutoFocus': True})

   def on_autoFocusStop(self, *args):
      self.is_focusing = False
      self.on_callback({'setAutoFocus': False})

   def on_syncStart(self, *args):
      self.startSynching = time.time()
      self.is_synching = True
      self.on_callback({'setSynching': True})

   def on_syncStop(self, *args):
      self.is_synching = False
      self.on_callback({'setSynching': False})


class lx200Factory(ServerFactory):
	
   protocol = lx200Protocol

   def __init__(self):
      self.parser = lx.lx200Parser()

   def setConfig(self):
      self.protocol.cfg = self.cfg

   def getAuth(self, usr, pwd):
      try:
         res = self.cfg.get(usr, 'pwd')
         if res == pwd:
            return defer.succeed(usr)
      except: 
         pass
      return defer.succeed(False)


   
