import json
import os

import targets
from kml import *


class Config:
    class Value:
        def __init__(self, key, val):
            self.key = key
            self.val = val
            self.chk = True
            self.keyact = key + '_actual'

        def extract(self, p):
            if self.key in p:
                self.val = p[self.key][0]
            self.chk = True

    class ValueN(Value):
        def extract(self, p):
            if self.key in p:
                try:
                    self.val = float(p[self.key][0])
                    self.chk = True
                except:
                    self.val = p[self.key][0]
                    self.chk = False

    def __init__(self):
        # -------------------------------------------------
        # zadání - vstupní veličiny
        # -------------------------------------------------
        self.name = self.Value('NA', 'flyby')
        self.targname = self.Value('TA', '')
        self.lat = self.ValueN('LA', targets.Stalin.lat)
        self.long = self.ValueN('LO', targets.Stalin.long)
        self.h0 = self.ValueN('H0', 300)
        self.x0 = self.ValueN('X0', 8000)
        self.y0 = self.ValueN('Y0', -12000)
        self.z0 = self.ValueN('Z0', 12000)
        self.x1 = self.ValueN('X1', 0)
        self.y1 = self.ValueN('Y1', -150)
        self.z1 = self.ValueN('Z1', 100)
        self.exp = self.ValueN('EX', 2)
        self.steps = self.ValueN('ST', 600)
        self.speedfact = self.ValueN('SPF', 1)
        self.slowdown = self.Value('SLD', 'tangential')
        self.slowdfact = self.ValueN('SLF', 1)
        self.flyby = self.ValueN('FL', 1)
        self.influentials = ['name', 'targname', 'lat', 'long', 'h0', 'x0', 'y0', 'z0',
                       'x1', 'y1', 'z1', 'exp', 'steps', 'speedfact', 'slowdown', 'slowdfact', 'flyby']

        # -------------------------------------------------
        # počáteční podmínky
        # -------------------------------------------------
        self.dXmin = 0.1            # min delta X při numerické konvergenci
        self.minZ = 60				# minimální přípustná výška nad terénem v metrech
        self.dHmax = 5				# empirické maximální přípustné otočení pohledu v jednom kroku ve stupních
        self.durOpt = 23			# empirické optimum: rychlost otáčení pohledu = 23°/sec
        self.stepsMax = 9999

        # -------------------------------------------------
        # provozní konstanty
        # -------------------------------------------------
        self.configsrv = 'kml'
        self.dnldsrv = 'flyby'
        if 'HTTP_HOST' in os.environ:
            h = os.environ['HTTP_HOST'].split('.')
            self.srvhost = h[0].lower()
            self.srvdomain = '.'.join(h[1:])
            self.dnldqual = '.'.join((self.dnldsrv, self.srvdomain))
            self.configqual = '.'.join((self.configsrv, self.srvdomain))
            _reqPath = os.environ['HTTP_HOST'] + os.environ['REQUEST_URI'].split('?')[0]
            _configKeyWord = 'kml'
            _dnldKeyWord = 'flyby'
            self.configReq = _reqPath.lower().find(_configKeyWord) > -1
            self.dnldReq = _reqPath.lower().find(_dnldKeyWord) > -1
            _reqUrl = '{scheme}://{host}:{port}{path}'.\
                format(scheme=os.environ['REQUEST_SCHEME'],
                       host=os.environ['HTTP_HOST'],
                       port=os.environ['SERVER_PORT'],
                       path=os.environ['REQUEST_URI'].split('?')[0])
            self.dnldUrl = _reqUrl.lower().replace(_configKeyWord, _dnldKeyWord)
            deb('self.dnldUrl=%s' % self.dnldUrl)
            self.dnldPath = _reqPath.lower().replace(_configKeyWord, _dnldKeyWord)
            deb('self.dnldPath=%s' % self.dnldPath)
        self.defaultlat = 50.1
        self.defaultlong = 14.3
        self.parmsdir = 'params'
        self.targsdir = 'targets'
        self.kmlfn = 'flyby'		# jméno výstupu - neměnné
        self.zipped = False
        # zipping se nedá v současnosti používat, protože Google Earth se s ním nechová standardně
        # - viz .../memo/googleearth
        self.kmlfile = '{}.{}'.format(self.kmlfn, 'kmz' if self.zipped else 'kml')
        self.urlfile = 'url.kml'
        self.msg = ''
        self.flagged = False

    def adjust(self):
        self.x0.val = fabs(self.x0.val)
        if self.x0.val < self.dXmin:
            self.x0.val = 0
        self.x1.val = 0
        if self.z0.val < self.minZ:
            self.z0.val = self.minZ
        if self.z1.val < self.minZ:
            self.z1.val = self.minZ
        self.steps.val = int(round(self.steps.val))
        if self.steps.val <= 0:
            self.steps.val = 20
        if self.steps.val > self.stepsMax:
            self.steps.val = self.stepsMax
        self.exp.val = fabs(self.exp.val)
        self.flyby.val = int(fabs(self.flyby.val))

        if self.x0.val > 0:
            self.r0 = degrees(atan(self.y0.val / self.x0.val))				# lokální azimut na startu
            self.r1 = 90 if self.y1.val < 0 else -90						# lokální azimut v průletu
            if self.y1.val == 0:
                if self.y0.val == 0:
                    self.r1 = 90
                else:
                    self.r1 = 90 * fabs(self.y0.val) / self.y0.val
                    if self.exp.val <= 1:
                        self.r1 = -self.r1
            self.h1 = can360(self.h0.val + self.r0 + self.r1)				# azimut v průletu
            self.h2 = can360(self.h0.val + 2 * (self.h1 - self.h0.val))		# azimut na konci

        dY = self.y1.val - self.y0.val
        dZ = self.z1.val - self.z0.val
        if dY == 0:
            self.A = pi / 2 if dZ < 0 else -pi / 2
        else:
            self.A = atan(dZ / dY)											# sklon roviny letu, horiz = 0
            if dY > 0:
                self.A = self.A + pi if dZ <= 0 else self.A - pi

        F = sqrt(pow(dY, 2) + pow(dZ, 2))									# maximální funkční hodnota
        if self.x0.val > 0:
            self.K = F / pow(self.x0.val, self.exp.val)		# koeficient paraboly

        sa = SpeedAdjust(self.speedfact.val, self.slowdfact.val, self.steps.val, self)
        self.speedAdjust = sa.slowdowns[self.slowdown.val](sa)

        self.lastH = self.h0.val
        self.lastX, self.lastY, self.lastZ = self.x0.val, self.y0.val, self.z0.val
        self.dX = -2 * self.x0.val / self.steps.val
        if fabs(self.dX) < self.dXmin:
            self.dX = -self.dXmin
        self.sumSteps, self.sumDur = 0, 0
        self.rr = []

    def update_influentials(self, p):
        for n in self.influentials:
            v = eval('self.{}'.format(n))
            v.extract(p)
            self.flagged += (not v.chk)

        v = self.lat
        if v.chk:
            v.chk = (v.val >= -90 and v.val <= 90)
        v = self.long
        if v.chk:
            v.chk = v.val >= -180 and v.val <= 180
        v = self.slowdown
        v.chk = v.val in SpeedAdjust.keys

        self.flagged = False
        for n in self.influentials:
            v = eval('self.{}'.format(n))
            self.flagged += (not v.chk)

    def parmspath(self):
        return os.path.join(self.parmsdir, self.name.val)

    def parmsls(self):
        l = os.listdir(self.parmsdir)
        l.sort()
        #deb("l={}".format(l))
        return l

    def targspath(self, name):
        return os.path.join(self.targsdir, name)

    def targsls(self):
        l = os.listdir(self.targsdir)
        l.sort()
        return l

    def gettarget(self, name):
        fn = name if name != '' else 'point'
        path = self.targspath(fn)
        long = self.defaultlong
        lat = self.defaultlat
        if os.path.exists(path):
            g = {}
            for p in open(path).read().split(','):
                k, v = p.split('=')
                g[k] = v
            if self.long.key in g:
                long = g[self.long.key]
            if self.lat.key in g:
                lat = g[self.lat.key]
        return long, lat

    def storetarget(self, name, long, lat):
        fn = name if name != '' else 'point'
        path = self.targspath(fn)
        open(path, mode='w').write('{}={},{}={}'.format(self.lat.key, lat, self.long.key, long))

    def restoreparms(self):
        if not os.path.exists(self.parmspath()):
            return
        parms = dict(json.load(open(self.parmspath())))
        # for p in open(self.parmspath()).read().split(','):
        #     k,v = p.split('=')
        #     parms[k] = [v]
        self.update_influentials(parms)

    def storeparms(self):
        if not self.name:
            return
        # s = []
        p = {}
        for n in self.influentials:
            v = eval('self.{}'.format(n))
            # s = s + ['='.join((v.key,str(v.val)))]
            p[v.key] = [str(v.val)]
        # open(self.parmspath(), mode='w').write(','.join(s))
        json.dump(p, open(self.parmspath(), mode='w'), ensure_ascii=False)

    def printparms(self):
        s = []
        for n in self.influentials:
            v = eval('self.{}'.format(n))
            s = s + ['{}={}'.format(v.key, v.val)]
        return '\n'.join(s)

    def adjustenv(self):
        p = {}
        for k in os.environ:
            p[k] = [os.environ[k]]
        return p
