from __future__ import print_function
import sys
from lxml import etree
from dateutil import parser as dateutil_parser
import locale

locale.setlocale(locale.LC_ALL, "it_IT.utf8")


def get_tag(el):
    return etree.QName(el).localname

# MAX=1000

HOURPIX=120
DEGREEPIX=6
KMPIX=300
LEFT=40
RIGHT=10
TOP=10
BOTTOM=40

def ora(min):
    return "%.2d:%2.2d" % (min/60, min%60)

def chop(s):
    pieces = s.split()
    return pieces[0], ' '.join(pieces[1:])

def getMin(s):
    try:
        hour, minute = map(int,s.split('.'))
        return hour * 60 + minute
    except ValueError:
        return int(s)

class Track(object):
    def __init__(self, filename):
        self.n = 0
        self.min = []
        self.quota = []
        self.temp = []
        self.note = []
        self.flag = []
        self.titolo = None
        self.data = None
        self.quotaMin = 20000
        self.quotaMax = -1000
        self.salita = 0
        self.discesa = 0
        self.tempoSalita = 0
        self.tempoDiscesa = 0
        self.salitaMax = 0
        self.discesaMax = 0
        self.imgs = []
        if filename.endswith(".gpx"):
            with open(filename, "rt") as fp:
                self.load_gpx(fp)
        else:
            with open(filename, "rt") as fp:
                self.load_txt(fp)

    def addImg(self, img):
        self.imgs.append(img)

    def outTxt(self, out):
      for i in range(self.n):
        print("%s %4d %c %s" % (ora(self.min[i]),self.quota[i],self.flag[i],self.note[i]), file=out)

    def outSrc(self, out):
        print('{% extends "base.src" %}', file=out)
        print('{% block header %}' + self.titolo + '{% endblock %}', file=out)
        print('{% block body %}', file=out)
        self.outCommon(out)
        print('{% endblock body %}', file=out)

    def outHtml(self, out):
      print("<h1>%s</h1>" % self.titolo, file=out)
      self.outCommon(out)

    def outCommon(self, out):
      print("<!--\n@T {}\n@D {}\n-->".format(self.titolo, self.data), file=out)
      print("<h3>%s</h3>" % self.data, file=out)
      print("<!--begin-->", file=out)
      print("<table bgcolor=green><tr><th>Ore</th><th>quota</th>", file=out)
      if self.has_temp:
          print("<th>temp.</th>", file=out)
      print("<th>note</th></tr>", file=out)
      for i in range(self.n):
        if (self.note[i] and len(self.note[i])) or self.flag[i] in "AP":
          print("<tr><td bgcolor=pink>%s</td><td bgcolor=pink>%.4d</td>" % (
              ora(self.min[i]),
              self.quota[i]), 
              file=out)
          if self.has_temp:
              print("<td bgcolor=pink>%2.2f</td>" % self.temp[i], file=out)
          print("<td bgcolor=pink>%s%s</td</tr>" % (
              self.note[i] or "",
              {'A': " (arrivo)", 'P': " (partenza)"}.get(self.flag[i],'')), file=out)
      print("</table>", file=out)
      print("<!--middle-->", file=out)

      self.outGraph(out)
      if self.has_temp:
          self.outTempGraph(out)

      print("<p><table bgcolor=green>", file=out)
      print(("<tr bgcolor=pink><td>Quota massima/minima: <td>%d m"
    	  + "<td>%d m</tr>") % (self.quotaMax, self.quotaMin), file=out)
      print(("<tr bgcolor=pink><td>Dislivello:</td><td colspan=2>%d m</td></tr>"
    	  % (self.quotaMax-self.quotaMin)), file=out)
      print(("<tr bgcolor=pink><td>Tempo totale (con soste):</td><td colspan=2>%s h</td></tr>"
    	  % ora(self.min[self.n-1]-self.min[0])), file=out)
      print(("<tr bgcolor=pink><td>Tempo salita/discesa:</td><td>%s h</td>"
    	  + "<td>%s h</td></tr>") % (
          ora(self.tempoSalita),
          ora(self.tempoDiscesa)), file=out)

      print("<tr bgcolor=pink><td>Dislivello salita/discesa:</td><td>%d m</td><td>%d m</td></tr>" % (self.salita, self.discesa), file=out)

      print("<tr bgcolor=pink><td>Velocit&agrave; media salita/discesa:</td><td>{} m/h</td><td>{} m/h</td></tr>".format(int(60*self.salita/self.tempoSalita+0.5) if self.tempoSalita else '--', int(60*self.discesa/self.tempoDiscesa+0.5) if self.tempoDiscesa else '--'), file=out)
      print("<tr bgcolor=pink><td>Velocit&agrave; massima salita/discesa: </td><td>%d m/h</td><td>%d m/h</td></tr>" %    	 (self.salitaMax, self.discesaMax), file=out)
      print("</table>", file=out)

      for img in self.imgs:
          print('<img src="{}">'.format(img), file=out)

    def outGraph(self, out, quota=True):
      series = self.quota if quota else self.temp
      margin = 60 if quota else 2
      quotaMax = max(series) + margin
      quotaMin = min(series) - margin
      minMax = max(self.min)
      minMin = min(self.min)
      VPIX = KMPIX/1000.0 if quota else DEGREEPIX
      maxx = LEFT + HOURPIX*(minMax-minMin)/60 + RIGHT
      maxy = BOTTOM + VPIX*(quotaMax-quotaMin) + TOP
      print('<svg width="{}" height="{}">'.format(maxx, maxy), file=out)

      back = 'rgb(96,192,255)'
      # gdImageColorTransparent(im,back);
      fore = 'rgb(0,0,0)'
      fill = 'rgb(0,255,0)'
      line = 'rgb(0,215,0)'
      pausa = line
      write = 'rgb(255,0,0)'
      grid = 'rgb(200,255,200)'

      # grid

      for i in range(minMin,minMax):
        x = LEFT + (i - minMin)*HOURPIX/60;
        if i%60 == 0:
            print('<line x1="{}" y1="{}" x2="{}" y2="{}" style="stroke:{};stroke-width:2" />'.format(x, TOP, x, maxy-BOTTOM, grid), file=out)
        elif i%15 == 0:
            print('<line x1="{}" y1="{}" x2="{}" y2="{}" style="stroke:{}" />'.format(x,TOP,x,maxy-BOTTOM,grid), file=out)

      for i in range(self.n):
        if i and self.min[i-1] > self.min[i]-4:
            continue
        print('<text x="{x}" y="{y}" fill="{color}" transform="rotate(-90 {x},{y})" style="font-size: 10px">{s}</text>'.format(
    		    x=LEFT+(self.min[i]-minMin)*HOURPIX/60+3,
    		    y=maxy-BOTTOM+26,
                s=ora(self.min[i]),
                color=fore), file=out)

      tick = 100 if quota else 5
      strong_tick = 1000 if quota else None

      for i in range(int(quotaMin),int(quotaMax)):
        y = maxy-(BOTTOM+(i-quotaMin)*VPIX)

        if strong_tick and (i % strong_tick) == 0:
            print('<line x1="{}" y1="{}" x2="{}" y2="{}" style="stroke:{};stroke-width:2" />'.format(
                LEFT, y, maxx-RIGHT, y, grid), file=out)
        elif i % tick == 0:
            print('<line x1="{}" y1="{}" x2="{}" y2="{}" style="stroke:{}" />'.format(
                LEFT, y, maxx-RIGHT, y, grid), file=out)
        if i % tick == 0:
          print('<text x="{}" y="{}" fill="{}" style="font-size: 10px">{}</text>'.format(
            LEFT-5*6, y+3, fore, i), file=out)

      # graph
      points = [
      (LEFT+(self.min[i]-minMin)*HOURPIX/60,
      maxy-(BOTTOM+(series[i]-quotaMin)*VPIX)) for i in range(self.n)]

      for step in range(3):
        for i in range(self.n):
          if i > 0:
            if step == 0 and quota:
              print('<polygon points="{}" style="fill: {};stroke: none"/>'.format(' '.join(['{},{}'.format(x,y) for x,y in [points[i], points[i-1],(points[i-1][0],maxy-BOTTOM),(points[i][0],maxy-BOTTOM)]]), pausa if self.flag[i-1]=='A' else fill), file=out)
            if step==1:
                print('<line x1="{p1[0]}" y1="{p1[1]}" x2="{p2[0]}" y2="{p2[1]}" style="stroke:{color}"/>'.format(p1=points[i-1], p2=points[i], color=fore), file=out)
          if self.note[i] and step==2:
              print('<line x1="{p1[0]}" y1="{p1[1]}" x2="{p2[0]}" y2="{p2[1]}" style="stroke:{color}"/>'.format(
                p1=points[i],
                p2=(points[i][0],maxy-BOTTOM),
                color=line), file=out)
              print('<text x="{p[0]}" y="{p[1]}" style="font-size=10px;stroke:{color}" transform="rotate(-90 {p[0]}, {p[1]})">{s}</text>'.format(
                p=(points[i][0] + {0: 10, self.n-1: 0}.get(i, 3),
                   maxy-BOTTOM),
                s=self.note[i],
                color=write), file=out)

      # frame
      print('<rect x="{}" y="{}" width="{}" height="{}" style="fill:none;stroke:{}"/>'.format(LEFT,TOP,maxx-RIGHT-LEFT,maxy-TOP-BOTTOM,fore), file=out)
      print("</svg>", file=out)

    def outTempGraph(self, out):
        self.outGraph(out, False)

    def calcola(self):
      # has temp?
      self.has_temp = (self.temp.count(None) < len(self.temp))

      # fill missing values
      intervals = []
      start = None
      for i in range(self.n):
          if start is None:
              if self.quota[i] is None:
                  start = i
          else:
              if self.quota[i] is not None:
                  intervals.append((start, i))
                  start = None

      for (a,b) in intervals:
        for i in range(a,b):
            self.quota[i] = self.quota[a-1] + (self.quota[b]-self.quota[a-1])/float(self.min[b] - self.min[a-1])*(self.min[i]-self.min[a-1])

      for i in range(self.n):
        if self.quota[i] < self.quotaMin:
            self.quotaMin = self.quota[i]
        if self.quota[i] > self.quotaMax:
            self.quotaMax = self.quota[i]
        if i and self.flag[i] == 'P' and self.flag[i-1] != 'A':
            print("Urka: c'e' una partenza senza arrivo!", file=sys.stderr)
        if i and self.flag[i-1] != 'A':
          deltamin = self.min[i] - self.min[i-1]
          if self.quota[i] >= self.quota[i-1]:
            self.salita += self.quota[i] - self.quota[i-1]
            self.tempoSalita += deltamin
            if deltamin > 0:
                v = 60*(self.quota[i]-self.quota[i-1])/deltamin
                if v > self.salitaMax: self.salitaMax = v
          else:
            self.discesa += self.quota[i-1] - self.quota[i]
            self.tempoDiscesa += deltamin
            if deltamin > 0:
                v = 60*(-self.quota[i]+self.quota[i-1])/deltamin
                if v > self.discesaMax: self.discesaMax=v

    def interpola(self, n1, quota1, n2, quota2):
      if n1 < 0 and n2 > self.n:
          return

      if n1 < 0:  # solo shift
        for i in range(n2):
          if self.quota[i] is not None:
              self.quota[i] += quota2 - self.quota[n2]
      elif n2 >= self.n:
        s = quota1 - self.quota[n1]
        for i in range(n1, self.n):
          if self.quota[i] is not None:
              self.quota[i] += s
      else:
        #  if (n2<=n1) return;
        r1 = quota1 - self.quota[n1]
        r2 = quota2 - self.quota[n2]
        for i in range(n1,n2):
          if self.quota[i] is not None:
              self.quota[i] += int(((n2-i)*r1+(i-n1)*r2)/float(n2-n1))

    def load_txt(self, fp):
      lastgiven = -1
      lastgivenquota = 0
      self.n = 0
      for line in fp.readlines():
        line = line.strip()
        if not line:
            continue
        if line[0] == '#':
            continue
        if line[0] == '@':
            if line[1] == 'T':
                self.titolo = line[2:]
                self.titolo = self.titolo.strip()
                continue
            elif line[1] == 'D':
                self.data = line[2:]
                self.data = self.data.strip()
                continue
            else:
                continue
        line = line.strip()
        if not line:
            continue
        if line[0].isalpha():
            self.flag.append(line[0].upper())
            line = line[1:]
            line = line.strip()
        else:
            self.flag.append(' ')
        chunk, line = chop(line)
        self.min.append(getMin(chunk))
        chunk, line = chop(line)
        self.temp.append(None if chunk == '?' else float(chunk))
        chunk, line = chop(line)
        self.quota.append(None if chunk=='?' else float(chunk))
#        if self.n == 0:
#            lastgiven = 0
#            lastgivenquota = self.quota[0]
        if line and line[0] == '(':
            chunk, line = chop(line)
            assert chunk[-1] == ')'
            chunk = chunk[1:-1]
            chunk = chunk.strip()
            given = float(chunk)
            self.interpola(lastgiven, lastgivenquota, self.n, given)
            lastgiven = self.n
            lastgivenquota = given
        self.note.append(line)
        self.n += 1
      if lastgiven >= 0:
          self.interpola(lastgiven, lastgivenquota, self.n, 0)
      self.calcola()

    def load_gpx(self, fp):
        tree = etree.parse(fp)
        gpx = tree.getroot()
        self.data = None
        self.n = 0
        self.titolo = ""
        for trk in gpx:
            if get_tag(trk) == 'metadata':
                for el in trk:
                    if get_tag(el) == "name":
                        if not self.titolo:
                            self.titolo = el.text
            elif get_tag(trk) != 'trk': continue
            for el in trk:
                if get_tag(el) == "name":
                    if not self.titolo:
                        self.titolo = el.text
                elif get_tag(el) == "desc":
                    self.descrizione = el.text
                elif get_tag(el) != "trkseg":
                    continue
                for trkpt in el:
                    if get_tag(trkpt) != "trkpt":
                        continue
                    # maybe use in the future:
                    lat, lon = trkpt.get('lat'), trkpt.get('lon')
                    quota = None
                    flag = " "
                    temp = None 
                    min = None
                    for e in trkpt:
                        if get_tag(e) == "ele":
                            quota = float(e.text)
                        elif get_tag(e) == "time":
                            dt = dateutil_parser.parse(e.text)
                            if self.data is None:
                                self.data = dt.strftime("%d %B %Y")
                            min = dt.hour*60 + dt.minute
                    self.quota.append(quota)
                    self.flag.append(flag)
                    self.temp.append(temp)
                    self.min.append(min)
                    self.note.append(None)
                    self.n += 1
        if (len(self.flag)>1):
            self.flag[0] = "P"
            self.flag[-1] = "A"
        self.calcola()

#        assert False, "not yet implemented"

def usage(name):
  print("{} <inputfile> [-img=<filename>] [-[txt|apng|tpng|html][=<filename>]...] ".format(name))

if __name__ == '__main__':
    track = None
    for arg in sys.argv[1:]:
        if arg[:5] == '-img=':
            track.addImg(arg[5:])
        elif arg[0] == '-':
            try:
                format, filename = arg[1:].split('=')
            except ValueError:
                format = arg[1:]
                filename = None
            try:
                fun = {
                    'txt': track.outTxt,
                    'html': track.outHtml,
                    'src': track.outSrc, }[format]
            except KeyError:
                usage(sys.argv[0])
                raise RuntimeError("unknown option {}".format(arg))
            if filename:
                out = open(filename, "w")
            else:
                out = sys.stdout
            fun(out)
        else:
            track = Track(arg)
