import os
imgbase = u"E:\\Images"

def old_to_new(val):
    if str(val)[-1] not in "NSEW":
        return float(val)
    newval = float(val[:-1])/100.0
    if val[-1] in "SW":
        newval = -newval
    return newval

def mark_bad(when):
    open(os.path.join(imgbase, "%s.bad" % when),"w").close()

def scan_where():
    fields = {}
    for f in os.listdir(imgbase):
        if not f.endswith(".where"): continue
        imgpath = os.path.join(imgbase, f)
        if os.path.exists(imgpath.replace(".where",".bad")): continue
        bits = open(imgpath,"r").read()
        if bits.startswith("("): words=eval(bits)
        else: words=bits.split(" ")
        lat, lon = map(old_to_new,words[:2])
        when, discard = os.path.splitext(f)
        fields[when] = (lat, lon)
    return fields


import key_codes
import e32
lock=e32.Ao_lock()

running=1
def quit():
    global running,lock
    running=0
    lock.signal()

def refresh():
    # just wakes up, doesn't do anything directly
    global lock
    lock.signal()

import appuifw
appuifw.app.screen='full'
appuifw.app.exit_key_handler=quit

import graphics

c=appuifw.Canvas()
appuifw.app.body=c
c.bind(key_codes.EKey9,refresh)

def nextday():
    global dayno, pointno
    dayno += 1
    if dayno >= len(days):
        dayno = 0
    pointno = 0
    render()
def prevday():
    global dayno, pointno
    dayno -= 1
    if dayno < 0:
        dayno = len(days)-1
    pointno = 0
    render()

def nextpoint():
    global pointno
    pointno += 1
    render()
def prevpoint():
    global pointno
    pointno -= 1
    render()

# really need a "point" data type...
def pointstep(pt1, pt2, nsteps):
    pt1_x, pt1_y = pt1
    pt2_x, pt2_y = pt2
    return ((pt2_x - pt1_x)/nsteps, (pt2_y - pt1_y)/nsteps)

def addpoints(pt, dpt):
    pt_x, pt_y = pt
    dpt_x, dpt_y = dpt
    return (pt_x + dpt_x, pt_y + dpt_y)    

def animate_boxes(start_point, latscale, lonscale, steps):
    # ds.rectangle ( (pt1, pt2), color)
    # assume corners, so we just have to do 2 of them
    end_1 = (latscale.inmax, lonscale.inmin) # upper left
    end_2 = (latscale.inmin, lonscale.inmax) # lower right
    step_1 = pointstep(start_point, end_1, steps)
    step_2 = pointstep(start_point, end_2, steps)
    run_1 = start_point
    run_2 = start_point
    for _ in range(steps):
        run_1 = addpoints(run_1, step_1)
        run_2 = addpoints(run_2, step_2)
        # print (latlon_to_xy(run_1, latscale, lonscale), latlon_to_xy(run_2, latscale, lonscale))
        # print yellow
        #rectx1, recty1 = latlon_to_xy(run_1, latscale, lonscale)
        #rectx2, recty2 = latlon_to_xy(run_2, latscale, lonscale)
        #ds.rectangle( ((rect, ), yellow, width=3)
        ds.rectangle( (latlon_to_xy(run_1, latscale, lonscale), latlon_to_xy(run_2, latscale, lonscale)), yellow, width=3)
        e32.ao_sleep(0.1)
        ds.rectangle( (latlon_to_xy(run_1, latscale, lonscale), latlon_to_xy(run_2, latscale, lonscale)), 0, width=3)
        # ds.line( (latlon_to_xy(run_1, latscale, lonscale), latlon_to_xy(run_2, latscale, lonscale)), green, width=3)

def viewpicture():
    # dayno + pointno...
    print dayno, pointno
    # lifted from render():
    points = points_of_day(allpoints, days[dayno])
    latscale, lonscale, order = make_scalers(points)
    dkey = order[pointno]
    imgpath = os.path.join(imgbase, dkey + ".jpg")
    lat, lon = points[dkey]
    # animation!
    print lat, lon
    animate_boxes((lat, lon), latscale, lonscale, 5) # steps
    # now open the picture
    appuifw.Content_handler(refresh).open(imgpath)

def badpicture():
    # dayno + pointno...
    print dayno, pointno
    # lifted from render():
    points = points_of_day(allpoints, days[dayno])
    latscale, lonscale, order = make_scalers(points)
    dkey = order[pointno]
    del allpoints[dkey]
    mark_bad(dkey)
    render()

c.bind(key_codes.EKeyLeftArrow, prevday)
c.bind(key_codes.EKeyRightArrow, nextday)
c.bind(key_codes.EKeyDownArrow, nextpoint)
c.bind(key_codes.EKeyUpArrow, prevpoint)
c.bind(key_codes.EKeyDevice3, viewpicture)
c.bind(key_codes.EKeyStar, badpicture)

ds=graphics.Draw(c)

class scaler:
    def __init__(self, insamp):
        self.inmin = insamp
        self.inmax = insamp
        self.outmin = None
        self.outmax = None
    def addval(self, v):
        self.inmin = min(self.inmin, v)
        self.inmax = max(self.inmax, v)
    def scale(self, v):
        inrange = self.inmax - self.inmin
        outrange = self.outmax - self.outmin
        return self.outmin + outrange *(v - self.inmin)/inrange
    def center(self):
        inrange = self.inmax - self.inmin
        return self.inmin + inrange/2

def make_scalers(points):
    order = points.keys()
    order.sort()
    samplat, samplon = points[order[0]]
    latscale = scaler(samplat)
    lonscale = scaler(samplon)

    for lat, lon in points.values():
        latscale.addval(lat)
        lonscale.addval(lon)

    max_x,max_y = c.size

    latscale.outmin = 0
    latscale.outmax = max_y
    lonscale.outmin = 0
    lonscale.outmax = max_x

    return latscale, lonscale, order

def find_days(points):
    days = {}
    for k in points.keys():
        d,t = k.split("t")
        days[d] = days.get(d,0) + 1
    daylist = days.keys()
    daylist.sort()
    # require 2 points for a day to count
    return [day for day in daylist if days[day] > 2]

#def find_hours(points):
#    days = {}
#    for k in points.keys():
#        d,t = k.split("t")
#        dd = d + "t" + t[:2]
#        days[dd] = days.get(dd,0) + 1
#    daylist = days.keys()
#    daylist.sort()
#    # require 2 points for a day to count
#    return [day for day in daylist if days[day] > 2]
        
def points_of_day(points, day):
    subpoints = {}
    for k,v in points.items():
        d,t = k.split("t")
        if d == day:                    # or say d.startswith(day) to handle prefix hours?
            subpoints[k] = v
    return subpoints

allpoints = scan_where()
days = find_days(allpoints)
#hours = find_hours(allpoints)
dayno = 0
pointno = 0 # point within day

def bothscale(point, latscale, lonscale):
    lat, lon = point
    return (latscale.scale(lat), lonscale.scale(lon))

def latlon_to_xy(point, latscale, lonscale):
    neglat, lon = bothscale(point, latscale, lonscale)
    return lon, latscale.outmax - neglat

blue = (0,0,255)
green = (0,255,0)
red = (255,0,0)
yellow = (255,255,0)

def render():
    ds.clear(0)
    max_x,max_y = c.size

    points = points_of_day(allpoints, days[dayno])
    latscale, lonscale, order = make_scalers(points)
    # note that %.3g doesn't truncate at all, though that may be documented
    label = u"Day %s/%s: c %.4s,%.4s" % (dayno+1, len(days), 
                                         latscale.center(),lonscale.center())
    ds.text( (0,max_y), label, yellow)

    # do this here instead of in prevpoint/nextpoint because they don't know our bounds
    global pointno
    if pointno >= len(order):
        pointno = len(order) - 1
    if pointno < 0:
        pointno = 0
    pointkey = order[pointno]
    lastpoint = points[order.pop(0)]
    if pointno == 0:
        ds.point(latlon_to_xy(lastpoint, latscale, lonscale), yellow, width=4)
    else:
        ds.point(latlon_to_xy(lastpoint, latscale, lonscale), blue, width=4)

    # lastd, lastt = order[0].split("t")
    for k in order:
        # sleep makes the rendering order visible, but we don't need that with a cursor
        # e32.ao_sleep(0.1)
        thispoint = points[k]
        # plot from lastpoint to thispoint, scaled by size
        ds.line((latlon_to_xy(lastpoint, latscale, lonscale), latlon_to_xy(thispoint, latscale, lonscale)), green)
        lastpoint = thispoint
        if k == pointkey:
            # cursor
            ds.point(latlon_to_xy(thispoint, latscale, lonscale), yellow, width=4)
        # d,t = k.split("t")
        # if d != lastd: 
        #     ds.point(latlon_to_xy(lastpoint, latscale, lonscale), yellow, width=2)
        #     lastd = d

# what about pictures?
#   keep an "nth" point, use up-down to select
#   click to show, just "open" it? or imgviewer render it?
# add "delete this point" too, make a .bad file?

while running:
    e32.ao_sleep(0.5)
    render()
    lock.wait()