import sys
import xml.sax.saxutils
from math import *

class Kml():
    def __init__(self):
        self.out = sys.stdout
        self.placemark = Placemark(self)
        self.tour = Tour(self)
        self.playlist = Playlist(self)
        self.flyto = FlyTo(self)
        self.lookat = LookAt(self)
        self.camera = Camera(self)
        self.document = Document(self)
        self.folder = Folder(self)
        self.xml = Xml(self)

    def documentProlog(self, fn):
        self.xml.head()
        self.document.head(fn)

    def documentEpilog(self):
        self.document.tail()
        self.xml.tail()

    def folderProlog(self, fn):
        self.folder.head(fn)

    def folderEpilog(self):
        self.folder.tail()

    def tourProlog(self, fn):
        self.tour.head(fn)
        self.playlist.head()

    def tourEpilog(self):
        self.playlist.tail()
        self.tour.tail()

    def placemarkProlog(self, fn):
        self.xml.head()
        self.placemark.head(fn)

    def placemarkEpilog(self):
        self.placemark.tail()
        self.xml.tail()

    def urlPlacemark(self, url, path):
        # ref = '<a href="http://{}/index.py">{}</a>'.format(qualname, qualname)
        ref = '<a href="{}">{}</a>'.format(url, path)
        p = self.pr
        p('<Placemark>')
        p('<name>{ref}</name>'.format(ref = xml.sax.saxutils.escape(ref)))
        p('</Placemark>')

    def wait(self, dur):
        self.pr('\n<gx:Wait><gx:duration>{}</gx:duration></gx:Wait>'.format(dur))

    def comm(self, c):
        self.pr('\n<!-- {} -->'.format(c))

    def pr(self, s):
        print(s, file=self.out)

class Xml():
    def __init__(self, kml):
        self.kml = kml

    def head(self):
        self.kml.pr('<?xml version="1.0" encoding="UTF-8"?>')
        self.kml.pr('<kml')
        self.kml.pr('xmlns="http://www.opengis.net/kml/2.2"')
        self.kml.pr('xmlns:gx="http://www.google.com/kml/ext/2.2"')
        self.kml.pr('>')

    def tail(self):
        self.kml.pr('\n</kml>')

class Document():
    def __init__(self, kml):
        self.kml = kml

    def head(self, fn):
        self.kml.pr('<Document>')
        self.kml.pr('<name>{}</name>'.format(fn))
        self.kml.pr('<open>1</open>')

    def tail(self):
        self.kml.pr('\n</Document>')


class Folder():
    def __init__(self, kml):
        self.kml = kml

    def head(self, fn):
        self.kml.pr('<Folder>')
        self.kml.pr('<name>{}</name>'.format(fn))
        self.kml.pr('<open>1</open>')

    def tail(self):
        self.kml.pr('\n</Folder>')

class Tour():
    def __init__(self, kml):
        self.kml = kml

    def head(self, fn):
        self.kml.pr('\n<gx:Tour>')
        self.kml.pr('<name>{}</name>'.format(fn))

    def tail(self):
        self.kml.pr('\n</gx:Tour>')

class Playlist():
    def __init__(self, kml):
        self.kml = kml

    def head(self):
        self.kml.pr('\n<gx:Playlist>')

    def tail(self):
        self.kml.wait(1)
        self.kml.pr('\n</gx:Playlist>')

class FlyTo():
    def __init__(self, kml):
        self.kml = kml

    def head(self, dur, smooth):
        self.kml.pr('<gx:FlyTo>')
        self.kml.pr('<gx:duration>{}</gx:duration>'.format(dur))
        self.kml.pr('<gx:flyToMode>{}</gx:flyToMode>'.format('smooth' if smooth else 'bounce'))

    def tail(self):
        self.kml.pr('\n</gx:FlyTo>')

class LookAt():
    def __init__(self, kml):
        self.kml = kml

    def run(self, long,lat,alt,head,tilt,range):
        self.kml.pr('\n<LookAt>')
        self.kml.pr('<latitude>{}</latitude>'.format(lat))
        self.kml.pr('<longitude>{}</longitude>'.format(long))
        self.kml.pr('<altitude>{}</altitude>'.format(alt))
        self.kml.pr('<heading>{}</heading>'.format(head))
        self.kml.pr('<tilt>{}</tilt>'.format(tilt))
        self.kml.pr('<range>{}</range>'.format(range))
        self.kml.pr('<altitudeMode>relativeToGround</altitudeMode>')
        self.kml.pr('</LookAt>')

class Camera():
    def __init__(self, kml):
        self.kml = kml

    def run(self,long,lat,alt,head,tilt,roll):
        self.kml.pr('<Camera>')
        self.kml.pr('<latitude>{}</latitude>'.format(lat))
        self.kml.pr('<longitude>{}</longitude>'.format(long))
        self.kml.pr('<altitude>{}</altitude>'.format(alt))
        self.kml.pr('<heading>{}</heading>'.format(head))
        self.kml.pr('<tilt>{}</tilt>'.format(tilt))
        self.kml.pr('<roll>{}</roll>'.format(roll))
        self.kml.pr('<altitudeMode>relativeToSeaFloor</altitudeMode>')
        self.kml.pr('</Camera>')

    def runL(self, long, lat, alt, head, tilt, range, roll):
        # dostává parametry LookAt, které konvertuje na parametry Camera tak, aby pohled byl stejný
        dLO, dLA, Z = l2c(lat, head, tilt, range)
        self.run(long + dLO, lat + dLA, Z, head, tilt, roll)

class Placemark():
    def __init__(self, kml):
        self.kml = kml

    def head(self, fn):
        self.kml.pr('<Placemark>')
        self.kml.pr('<name>{}</name>'.format(fn))
        self.kml.pr('<visibility>0</visibility>')
        self.kml.pr('<styleUrl>#PIN_YELLOW</styleUrl>')

    def tail(self):
        self.kml.pr('\n</Placemark>')

class SpeedAdjust():
    keys = ['none', 'linear', 'parabolic', 'tangential', 'exponential']
    abrv = {'none':'none', 'linear':'lin', 'parabolic':'par', 'tangential':'tang', 'exponential':'exp'}

    def __init__(self, const, koef, steps, config):
        self.slowdowns = {'none':self.Const, 'linear':self.Linear, 'parabolic':self.Parabolic, 'tangential':self.Tangent, 'exponential':self.Exponential}
        self.const = const
        self.koef = koef
        self.steps = steps
        self.c = config

    def phase(self):
        return 1 - self.c.xi / self.c.x0.val

    class Const():
        p = 1					# empirická konstanta
        def __init__(self, sa):
            self.sa = sa
        def koef(self):
            return self.p / self.sa.const

    class Linear():
        p = 2					# empirická konstanta
        def __init__(self, sa):
            self.sa = sa
        def koef(self):
            return self.p * self.sa.koef * self.sa.phase() / self.sa.const

    class Parabolic():
        p = 2					# empirická konstanta
        def __init__(self, sa):
            self.sa = sa
        def koef(self):
            return self.p * pow(self.sa.koef * self.sa.phase(), 2) / self.sa.const

    class Tangent():
        p = 1					# empirická konstanta
        def __init__(self, sa):
            self.sa = sa
        def koef(self):
            margin = atan(self.sa.koef)		# < pi/2
            return (1 + self.p * tan(self.sa.phase() * margin)) / self.sa.const

    class Exponential():
        p = 1					# empirická konstanta
        def __init__(self, sa):
            self.sa = sa
        def koef(self):
            return pow(e, self.p * self.sa.koef * self.sa.phase()) / self.sa.const

def canonGeodetic(a):
    if(a > 360 or a < -360): a = a % 360
    if(a > 180): a = a - 360
    if(a < -180): a = a + 360
    return a

def can360(a360):
    if(a360 < 0): a360 = a360 + 360
    elif(a360 > 360): a360 = a360 - 360
    return a360

def can180(a180):
    if(a180 < -180): a180 = a180 + 360
    elif(a180 > 180): a180 = a180 - 360
    return a180

def l2c(lat, head, tilt, range):
    RZ = 6378000
    RL = RZ * cos(radians(lat))
    Hc = can360(head + 180)
    Z = range * cos(radians(tilt))
    d = sqrt(pow(range,2) - pow(Z,2))
    dLA = degrees(atan(d * cos(radians(Hc)) / RZ))
    dLO = degrees(atan(d * sin(radians(Hc)) / RL))
    return dLO, dLA, Z

def deb(s):
    print('+++ {}'.format(s), file=sys.stderr)
