"""
     Copyright (C) 2007-2009 Franco Lanza <nextime@nexlab.it>
     Copyright (C) 2007-2009 Sandro Aliano <ita595@hotmail.com>
     Copyright (C) 2007-2009 Ivan Bellia <skylive@skylive.it>

     Web site: http://www.astronomix.org/trac/Skylive-NG

     This file is part of Skylive-NG.

     Skylive-NG is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation, either version 3 of the License, or
     (at your option) any later version.

     Skylive-NG is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with Skylive-NG.  If not, see <http://www.gnu.org/licenses/>.
"""

#import SocketServer
import math
from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import ServerFactory
from twisted.internet import reactor, main
import sys

try:
   if int("".join(sys.version[0:3].split("."))) < 25:
      import elementtree.ElementTree as elemtree
   else:
      import xml.etree.ElementTree as elemtree
except:
   import lxml.etree as elemtree


device = """
<defSwitchVector
  device='Skylive-NG'
  name='CONNECTION'
  label='Connection'
  group='Communication'
  state='Idle'
  perm='rw'
  rule='OneOfMany'
  timeout='0'
>
  <defSwitch
    name='CONNECT'
    label='Connect'>
      On
  </defSwitch>
  <defSwitch
    name='DISCONNECT'
    label='Disconnect'>
      Off
  </defSwitch>
</defSwitchVector>
<defNumberVector
  device='Skylive-NG'
  name='EQUATORIAL_EOD_COORD'
  label='Equatorial JNow'
  group='Main Control'
  state='Idle'
  perm='rw'
  timeout='0'
>
  <defNumber
    name='RA'
    label='RA  H:M:S'
    format='%7.8m'
    min='0'
    max='24'
    step='0'>
      0
  </defNumber>
  <defNumber
    name='DEC'
    label='Dec D:M'
    format='%6.8m'
    min='-90'
    max='90'
    step='0'>
      0
  </defNumber>
</defNumberVector>
<defSwitchVector
  device='Skylive-NG'
  name='ON_COORD_SET'
  label='On Set'
  group='Main Control'
  state='Idle'
  perm='rw'
  rule='OneOfMany'
  timeout='0'
>
  <defSwitch
    name='SLEW'
    label='Slew'>
      On
  </defSwitch>
</defSwitchVector>
"""

connected = False

def _DECtoINDI(text):
   if text != '+00:00' and text != '-00:00' and text != '-':
      d, m = text.split(':')
      if (int(m)) != 0:
         min = int(m)/60.0
      else:
         min = 0
      if '-' in d:
         output = str(int(d)-min)
      else:
         output = str(int(d)+min)
   else:
      output = 0
   return str(output)

def _RAtoINDI(text):
   if text != '00:00:0' and text != '-':
      h, m, s = text.split(':')
      sec = float('0.'+s)
      if (sec+int(m)) != 0:
         min = (sec+int(m))/60.0
      else:
         min = 0
      output = str(int(h)+min)
   else:
      output = 0
   return str(output)


def punta(coo):
   #print "INDI: Punta "+str(coo)
   try:
      _parent.udpQueue.put({'udpcmd' : coo})
   except:
      pass

def _sexagesimal(r):
   std = int( math.floor( r ));
   r -= float( math.floor( r ));
   r *= 60.0;
   min = int (math.floor( r ));
   r -= float( min);
   #r *= 60.0;
   sec = r;
   output=''+ str(std).zfill(2)+":"+str(min).zfill(2)+"."+str(sec).split('.')[0]
   return output;

def _dectext(r):
   num = r.split('.')
   degr = num[0]
   first = str((float('0.'+num[1])*60.0)).split('.')[0]
   sec = str(float('0.'+(str((float('0.'+num[1])*60.0)).split('.')[1]))*60.0)
   if int(sec.split('.')[1]) > 50:
      arr = 1
   else:
      arr = 0
   second = str(int(sec.split('.')[0])+arr)
   if int(second) > 60:
      second = '00'
      first = str(int(first)+1)
   if int(first) > 60:
      first = '00'
      if '-' in degr:
         degr = str(int(degr)-1)
      else:
         degr = str(int(degr)+1)
   if '-' in degr:
      degr = degr.zfill(3)
   else:
      degr = degr.zfill(2)
   output = ''+degr+':'+first.zfill(2)
   return output



# XXX
# This is a very bad and minimal implementation.
# We really need to redoit
#
class INDIProtocol(LineReceiver):

   disconnect_forced = False

   def lineReceived(self, line):
      pass

   def rawDataReceived(self, data):
      #print 'RECEIVED:', data
      tree = elemtree.fromstring('<indi>'+data+'</indi>')
      nv = tree.find('newNumberVector')
      if nv is not None:
         on = nv.findall('oneNumber')
         if len(on) == 2:
            coo = {}
            for num in on:
               if num.get('name') in ['RA', 'DEC']:
                  coo[num.get('name')] = num.text.strip()
            if len(coo) == 2:
               RA=_sexagesimal(float(coo['RA']))
               DEC=_dectext(coo['DEC'])
               if '-' not in DEC and '+' not in DEC:
                  DEC = '+'+DEC
               self.factory.TwistedLoop.indiDriverMessage(str(RA)+str(DEC))

   def connectionMade(self):
      if self.factory.indi_connected:
         self.disconnect_forced = True
         # only one connection is permitted!
         self.transport.loseConnection()
         self.__ignoreBuffer = True
         self.transport.connectionLost(main.CONNECTION_DONE)
      else:
         self.factory.indi_connected = True
         self.factory.indiclient = self
         self.transport.write(device)
         self.setRawMode()
         self.factory.indiDriver.TwistedLoop.indistarted=True

   def connectionLost(self, reason):
      if not self.disconnect_forced:
         self.factory.indi_connected = False
         del self.factory.indiclient
      self.disconnect_forced = False



   def setTelePos(self, RA, DEC):
      try:
         telePos = """
<setNumberVector device='Skylive-NG' name='EQUATORIAL_EOD_COORD'>
  <oneNumber name='RA' >"""+_RAtoINDI(RA)+"""</oneNumber>
  <oneNumber name='DEC' >"""+_DECtoINDI(DEC.replace('*', ':'))+"""</oneNumber>
</setNumberVector>
"""
         self.transport.write(telePos)
      except:
         pass


class INDIDriverFactory(ServerFactory):
   
   indi_connected = False
   protocol = INDIProtocol

   def setIndiDriver(self, indiDriver):
      self.indiDriver = indiDriver
   

class IndiDriver:

   connector = False

   def __init__(self, TwistedLoop):
      self.TwistedLoop = TwistedLoop

   def startDriver(self):
      self.factory = INDIDriverFactory()
      self.factory.TwistedLoop = self.TwistedLoop
      self.factory.setIndiDriver(self)
      # XXX Is there any way to get authentication on 
      # the indi protocol?
      # If not, maybe we need to let's users configure a 
      # white/blacklist of ip or to change the default listen address.
      self.connector = reactor.listenTCP(43334, self.factory)

   def stopDriver(self):
      if self.connector:
         self.connector.stopListening()
         self.connector =  False

   def setTelePos(self, RA, DEC):
      try:
         self.factory.indiclient.setTelePos(RA, DEC)
      except:
         pass
