#!/usr/bin/python

# Copyright 2009 Leigh L. Klotz, Jr.
# See file MIT-LICENSE.txt

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import time
from threading import Thread
import serial

SERVER_HOST='localhost'
SERVER_PORT=9150

SERIAL_PORT="/dev/ttyUSB0"
BAUD=4800

POSITS = None
MAPXML = None

NAMES = dict()
NAMES['A'] = "Voldemort"
NAMES['B'] = "Harry"
NAMES['B'] = "Hedwig"

DESCRIPTIONS = dict()
DESCRIPTIONS['A'] = "You Know Who (A)"
DESCRIPTIONS['B'] = "The boy who lived (B)"
DESCRIPTIONS['C'] = "The Trusy Flier (C)"

class Posit:
  def __init__(self, who):
    global NAMES, DESCRIPTIONS
    self.who = who
    self.name = NAMES.get(who)
    self.description = DESCRIPTIONS.get(who)
    self.lat = None
    self.lon = None

  def ready(self):
    return self.lat != None and self.lon != None

class Posits:
  def __init__(self):
    self.d = dict()

  def set_lat(self, who, lat):
    print "%s lat %s" % (who, lat)
    self.posit(who).lat = lat

  def set_lon(self, who, lon):
    print "%s lon %s" % (who, lon)
    self.posit(who).lon = lon

  def ready(self):
    return any(self.posit(who).ready() for who in self.d)

  def posits(self):
    return [ self.posit(who) for who in self.d if self.posit(who).ready() ]

  def posit(self, who):
    x = self.d.get(who)
    if x == None:
      x = Posit(who)
      self.d[who] = x
    return x

class PositHandler(BaseHTTPRequestHandler):
  def do_GET(self):
      global POSITS
      if not POSITS.ready():
          self.send_response(404)
          self.send_header('Content-type', 'text/plain')
          self.end_headers()
          self.wfile.write("Not Ready")        
      elif (self.path=="/"):
          self.send_response(200)
          self.send_header('Content-type', 'text/plain')
          self.send_header('Expires', '0')
          self.end_headers()
          for posit in POSITS.posits():
            self.wfile.write("latlon %s %s %s" % (posit.who, posit.lat, posit.lon))
      elif self.path.startswith("/map.xml") or self.path.startswith("/map.kml"):
          if self.path.startswith("/map.xml"):
            self.send_response(200)
            self.send_header('Content-type', 'application/xml')
          elif self.path.startswith("/map.kml"):
            self.send_response(200)
            self.send_header('Content-type', 'application/vnd.google-earth.kml+xml')
          self.send_header('Expires', '0')
          self.end_headers()
          self.wfile.write(MAPXML.generate(POSITS.posits()))
      else:
          self.send_response(403)
          self.send_header('Content-type', 'text/plain')
          self.end_headers()
          self.wfile.write("Forbidden")

class MapXML:
  def __init__(self):
    self.mapstring = open('map.xml', 'r').read()
    self.placestring = open('placemark.xml', 'r').read()

  // see http://code.google.com/apis/kml/documentation/expiration.html
  def expires(self):
    now = time.time()
    future = time.gmtime(now + 10)
    y = future[0]
    mo = future[1]
    d = future[2]
    h = future[3]
    mi = future[4]
    s = future[5]
    iso8601 = '%04d-%02d-%02dT%02d:%02d:%02dZ' % (y,mo,d,h,mi,s)
    return iso8601

  def generate(self, posits):
    lon_center = sum([p.lon for p in posits])/len(posits)
    lat_center = sum([p.lat for p in posits])/len(posits)
    pmarks = " ".join([ self.placestring % (posit.description,
                                            posit.name,
                                            posit.lon,
                                            posit.lat)
                        for posit in posits])
    return (self.mapstring % (self.expires(), lon_center, lat_center, pmarks))

class SerialThread(Thread):
    def __init__(self):
        global SERIAL_PORT, BAUD
        Thread.__init__(self)
        self.ser = serial.Serial(SERIAL_PORT, BAUD, timeout=2)
    
    def dms(self,direction,deg,min):
      try:
        dms = float(deg) + float(min)/60
      except:
        print "fail"
        return None
      if direction == 'W' or direction=='S':
        dms = -dms
      return dms
      
    def run(self):
        while True:
            line = self.ser.readline().rstrip()
            if line != "":
              who = line[0]
              self.handle(who, line[1:])

    def handle(self, who, line):
        global POSITS
        if line != "":
          d = line[0]
        else:
          d = ""
        if d == "E" or d == "W":
          lon = self.dms(d, line[1:4], line[4:])
          if lon != None:
            POSITS.set_lon(who, lon)
        elif d == "N" or d == "S":
          lat = self.dms(d, line[1:3], line[3:])
          if lat != None:
            POSITS.set_lat(who, lat)
        else:
          print "%s FAIL %s" % (who, line)

class WebThread(Thread):
    def __init__(self):
        Thread.__init__(self)
    
    def run(self):
        global SERVER_HOST, SERVER_PORT
        server = HTTPServer((SERVER_HOST, SERVER_PORT), PositHandler)
        print 'started httpserver...'
        server.serve_forever()

def main():
  try:
    global POSITS, MAPXML
    POSITS = Posits()
    MAPXML = MapXML()
    thr1=SerialThread()
    thr1.start()
    thr2=WebThread()
    thr2.start()
    thr1.join()
    thr2.join()
  except KeyboardInterrupt:
    print '^C received, shutting down server'
    server.socket.close()

if __name__ == '__main__':
  main()
