#!/usr/bin/python
#  identify.py
#  An asteroid_server client which identifies measurements in MPC format
#  Jure Skvarc, March 1999
#

import socket
import select
from sys import * 
from string import *
from math import *
import getopt
import os
import errno
import fileinput

#===========================================================================
#  Define a class to access asteroid_server
#===========================================================================
class Asteroid_client:
  #  Create a socket
  def __init__(self, host, port):
    self.port = port
    self.host = host
    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

  def connect(self):
    err = self.sock.connect_ex(self.host, self.port)
    #  If connection did not succeed, exit
    if err != 0:
      print 'Can not connect to server', host, 'at port', port
      print 'Reason:', os.strerror(err)
      exit()

  def get(self):
    return self.sock.recv(1024)

  def send(self, request):
    self.sock.send(request)

  def close(self):
    self.sock.close()

  def get_answer(self, timeout):
    ins = 1
    ans = ''
    #  The answer may be pretty long, so be ready to receive in parts
    while ins:
    #  Wait for the information
       a, b, c = select.select([self.sock], [], [], 10)
       if len(a) > 0:
         ans1 = self.get()
	 if len(ans1) == 0:
	   ins = 0
	 ans = ans + ans1
	 if ans[-1] == '\0':
	   ins = 0
       else:
         #  Timeout, end the loop
         ins = 0
    return ans


#====================================
# class which holds coordinate pairs
#====================================
class coorpair:
  pass

#===========================================================================
#  Extract hour from the decimal day and output hours in hh:mm:ss.ss format
#===========================================================================
def tohours(day):
  day = day - int(day)
  hours = int(24 * day)
  day = 24 * day - hours
  minutes = int(60 * day)
  day = 60 * day - minutes
  seconds = 60 * day
  return "%02d:%02d:%05.2f" % (hours, minutes, seconds)


#===========================================================================
#  Translate designation to packed designation
#===========================================================================
def designation_to_packed(name):
  packed = ''
  year, code = split(name)
  year = atoi(year)
#  Does it look like a year? 
  if find(code, 'P-L') > 0 or find(code, 'T-1') > 0 or find(code, 'T-2') > 0 or find(code, 'T-3') > 0:
    packed = "%c%cS%4s" % (name[5], name[7], year)
  elif year > 1900 and  year < 2100:
    cent = chr(year / 100 - 19 + ord('J'))
    num = atoi(code[2:])
    if num < 100:
      n1 = chr(num / 10 + ord('0'))
    else:
      n1 = chr(num / 10 - 10 + ord('A'))
    n2 = chr(num % 10 + ord('0'))
    packed = "%c%02d%c%c%c%c" % (cent, year % 100, code[0], n1, n2, code[1])
  return packed




#  Calculate the value of the haversine function
def hav(x):
  x = sin(0.5 * x)
  return x * x

#  Inverse haversine function
def invhav(x):
  return 2.0 * asin(sqrt(x))

#===================================================
#  Calculate the angular distance between two points
#===================================================
def angular_distance(ra1, dec1, ra2, dec2):
  return invhav(hav(dec1 - dec2) + cos(dec1) * cos(dec2) * hav(ra1 - ra2))


#  Counter for the unknown asteroids
astcount = 1
#  Prefix for the unknown asteroids
prefix = 'TT'
#  Whether to send the prepare command
prepare = 0
prevdist = 120
#==========================
#  The program begins here
#==========================

#====================
#  First get options
#====================
try:
  optlist, args = getopt.getopt(argv[1:], 'p:n:d:', ['prefix=', 'number=', 'prevdist='])
except getopt.error, err:
  print 'Wrong option!', err
  exit(1)

for a in optlist:
  if a[0] == '-p':
    prefix = a[1]
  elif a[0] == '-n':
    astcount = int(float(a[1]))
  elif a[0] == '-d':
    prevdist = a[1]
  else:
    prepare = 1

#==========================================
#  Check if enough arguments are specified
#==========================================
if len(args) < 5:
  print "Usage::  python [options] identify.py server_address id input_file distance magnitude"
  exit(1)
#  Default host address
host = args[0]
#  User id
id = args[1]
#  Input file name
filename = args[2]
#  maximal acceptable distance to the object
distance = atof(args[3])
#  maximum acceptable magnitude of the object
magnitude = atof(args[4])
#  Port number for the astreoid server
port = 58972
# try to connect to the server
clnt = Asteroid_client(host, port)
clnt.connect()
#  send id
clnt.send(id + '\0')
#  Get an intro message
hello = clnt.get()
#  Close the connection
clnt.close()
del clnt

clnt = Asteroid_client(host, port)
clnt.connect()
#  send id
clnt.send(id + '\0')
#  Get an intro message
hello = clnt.get()
#  List of coordinates of all asteroids
coords = []
for line in fileinput.input(filename):
  if len(line) < 80: 
    print line,
    continue
  a = line[0:11]
  a = strip(a)
  year = atoi(line[15:19])
  month = atoi(line[20:22])
  day = atof(line[23:31])
  rah = atoi(line[32:34])
  ram = atoi(line[35:37])
  ras = atof(line[38:43])
  dsign = line[44:45]
  dech = atoi(line[45:47])
  decm = atoi(line[48:50])
  decs = atof(line[51:55])
  hour = tohours(day)
  query = "check \"%d %d %d %s\" %02d:%02d:%05.2f %s%02d:%02d:%04.1f %f %f\n" % \
	(year, month, day, hour, rah, ram, ras, dsign, dech, decm, decs, distance, magnitude)
  ra = 15.0 * (rah + (ram + ras / 60.0) / 60.0) * 1.74532925199e-2
  dec = (dech + (decm + decs / 60.0) / 60.0)  * 1.74532925199e-2
  if dsign == '-':
    dec = -dec
  
  query = query + '\0'
#  print query,
  clnt.send(query)
  list = clnt.get()
  #  Find the object with the smallest distance
  mdist = distance * 60
  lines = split(list, '\n')
  b = 'nothing'
  for a in lines:
    if len(a) <= 1: continue
#    print a
    dist = atof(split(a)[-1])
    if dist < mdist:
      b = a
      mdist = dist

  line = 13 * ' ' + line[13:]
  discovery = ' '
  if b == 'nothing':
    #  There were no asteroids found within the given distance
    #  Check if any of the previously processed asteroids is close to this one
    #  (within 2 arcminutes)
    for p in coords:
      dist = angular_distance(p.ra, p.dec, ra, dec) * 206264.806247
      if dist < prevdist:
         desig = p.desig
	 break
    else:
      #  Construct a new temporary designation
      desig = prefix + ("%04d" % astcount)
      astcount = astcount + 1
      discovery = '*'
    desig = desig + '       '
    line = line[0:5] + desig[0:7] + discovery + line[13:]
  else:
    num = atoi(strip(b[0:6]))
    if num == 0:
      desig = designation_to_packed(b[6:25])
      line = line[0:5] + desig + line[12:]
    else:
      desig = ("%05d" % num)
      line = desig + line[5:]
  as = coorpair()
  as.ra = ra
  as.dec = dec
  as.desig = desig
  coords.append(as)
  print '%s' % line,
clnt.close()
exit(0)



