"""
Tracker mit MQTT client

Es gibt zwei Modi: Track und Race
  A) Track: Es wird nur der Track gesended / aufgezeichnet
  B) Race: Es werden zusätzlich Regattadaten angezeigt, das beinhaltet
    die Auswahl einer Regatta, das Teilnehmen, das Aufgeben und die
    Signalisierung vor und während einer Wettfahrt.

  Das Tracking kann über eine Taste ein- und ausgeschaltet werden.
  Um versehentliches Umschalten zu vermeiden ist eine Nachfrage
  integriert.

 - Momentan wird nur Regatta Hero unterstützt

Behandlung von Verbindungsabbrüchen:
  - on_disconnect

TODO
    

"""

import os
import cairo
from .page import Page

class RaceTracker(Page):

    def __init__(self, pageno, cfg, appdata, boatdata):
        super().__init__(pageno, cfg, appdata, boatdata)
        self.ttype = self.app.track.ttype
        self.bv_lat = boatdata.getRef("LAT")
        self.bv_lon = boatdata.getRef("LON")
        self.bv_sog = boatdata.getRef("SOG")
        self.bv_hdop = boatdata.getRef("HDOP")
        self.races = None
        self.raceid = None           
        if self.ttype == 'HERO':
            self.raceid = self.app.track.hero_raceid # Ausgewählte Regatta
            self.buttonlabel[1] = 'MODE'
            self.buttonlabel[2] = 'INFO'
            self.buttonlabel[5] = 'ABORT'
        elif self.ttype in ('LOCAL','SDCARD'):
            self.buttonlabel[5] = 'ON'
        self.menupos = 0
        self.mode = 'N' # (N)ormal, (C)onfiguration, (M)itteilung
        self.query_active = False
        self.savebuttons = None

        # Flaggengröße: 96 x 64 Pixel
        self.flagpos = ((208, 140), (308, 140), (208, 210), (308, 210))

        # Flaggen laden
        flag = ('alpha', 'answer', 'black', 'blue', 'charlie', 'class',
                'finish', 'foxtrot', 'hotel', 'india', 'november',
                'orange', 'papa', 'repeat_one', 'sierra', 'start',
                'uniform', 'xray', 'yankee', 'zulu')
        # Mapping
        self.flagmap = {
              3: 'blue',        # Zielflagge
              7: 'xray',        # Einzelrückruf
              8: 'sierra',      # Bahnverkürzung
              9: 'november',    # Abbruch
             10: 'yankee',      # Schwimmwesten
             11: 'repeat_one',  # Allgemeiner Rückruf
             12: 'answer',      # Startverschiebung
             14: 'start',       # Startflagge
             15: 'class',       # Klassenflagge
             18: 'alpha',
             19: 'hotel',
             20: 'charlie',
            100: 'papa',        # Vorbereitung, Frühstart: Zurückfallen über Startlinie
            101: 'india',       # Vorbereitung, Frühstart: Starttonne umrunden
            102: 'zulu',        # Frühstart: 20%-Strafe
            103: 'uniform',     # Frühstart: Disqualifikation, Wiederholung erlaubt
            104: 'black'        # Frühstart: Disqualifikation, Wiederholung nicht erlaubt
        }
        self.sym_flag = {}
        for f in flag:
            flagfile = os.path.join(cfg['imgpath'], 'flags', f + '.png')
            self.sym_flag[f] = cairo.ImageSurface.create_from_png(flagfile)

    def handle_key(self, buttonid):
        if self.ttype == 'NONE':
            return False
        if self.query_active:
            if buttonid == 2:
                self.query_active = False
                self.app.track.hero_giveup()
                self.buttonlabel = self.savebuttons.copy()
            elif buttonid == 5:
                self.query_active = False
                self.buttonlabel = self.savebuttons.copy()
            return True
        if buttonid == 1:
            if not self.ttype == 'HERO':
                return False
            # Modus umschalten
            if self.mode == 'N':
                self.mode = 'C'
                self.buttonlabel[2] = '#UP'
                self.buttonlabel[3] = '#DOWN'
                self.buttonlabel[4] = 'SET'
                if self.app.track.is_active():
                    self.buttonlabel[5] = 'OFF'
                else:
                    self.buttonlabel[5] = 'ON'
            else:
                self.mode = 'N'
                self.buttonlabel[2] = 'INFO'
                self.buttonlabel[3] = '#PREV'
                self.buttonlabel[4] = '#NEXT'
                self.buttonlabel[5] = 'ABORT'
            return True
        elif buttonid == 2:
            if not self.ttype == 'HERO':
                return False
            if self.mode == 'N':
                self.mode = 'M' # Nachrichten der Wettfahrtleitung
                self.buttonlabel[2] = ''
                self.buttonlabel[5] = ''
                return True
            if self.mode == 'C':
                # Up
                if self.menupos > 1:
                    self.menupos -= 1
                else:
                    self.menupos = len(self.races)
                return True
        elif buttonid == 3:
            if self.mode == 'C':
                # Down
                if self.menupos < len(self.races):
                    self.menupos += 1
                else:
                    self.menupos = 1
                return True
        elif buttonid == 4:
            if self.mode == 'C':
                # Set / Select regatta
                # TODO Nur möglich wenn nicht in einer anderen Wettfahrt gerade aktiv
                if self.menupos > 0:
                    self.raceid = self.races[self.menupos - 1] # Nullbasiert
                    last_id = self.app.track.hero_raceid
                    self.app.track.hero_raceid = self.raceid
                    self.app.track.hero_mqtt_subscribe(self.raceid, last_id)
                    self.app.log.info(f"Selected race '{self.raceid}'")
                return True
        elif buttonid == 5:
            if self.mode == 'N':
                if self.ttype in ('LOCAL','SDCARD'):
                    # Tracking ein/-ausschalten
                    if self.app.track.is_active():
                        self.app.track.set_active(False)
                        self.buttonlabel[5] = 'ON'
                    else:
                        self.app.track.set_active(True)
                        self.buttonlabel[5] = 'OFF'
                else:
                    if self.ttype == ('HERO'):
                        # Rennabbruch verarbeiten
                        self.query_active = True;
                        self.savebuttons = self.buttonlabel.copy()
                        self.buttonlabel[1] = ""
                        self.buttonlabel[2] = "YES"
                        self.buttonlabel[3] = ""
                        self.buttonlabel[4] = ""
                        self.buttonlabel[5] = "NO"
                        self.buttonlabel[6] = ""
                        # Taste 2 = JA, Taste 5 = NEIN
            elif self.mode == 'C':
                # Tracking ein/-ausschalten
                if self.app.track.is_active():
                    self.app.track.set_active(False)
                    self.buttonlabel[5] = 'ON'
                else:
                    self.app.track.set_active(True)
                    self.buttonlabel[5] = 'OFF'
            elif self.mode == 'M':
                self.app.frontend.flashled.setColor('yellow')
                #self.app.frontend.flashled.switchOn(4)
                self.app.frontend.flashled.doFlash(2)
            return True
        return False

    def draw_none(self, ctx):
        """
        TODO Funktion schreiben, die einen längeren Text mit
        Absätzen auf dem Bildschirma ausgibt.
        """
        x = 8
        y = 48
        ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
        ctx.set_font_size(24)
        ctx.move_to(x, y)
        ctx.show_text("Tracker")

        y += 25
        ctx.set_font_size(16)
        ctx.move_to(x, y)
        ctx.show_text("Disabled by 'NONE' in configuration.")
        y += 30
        ctx.move_to(x, y)
        ctx.show_text("Currently only tracker types 'HERO' and 'LOCAL'")
        y += 20
        ctx.move_to(x, y)
        ctx.show_text("are implemented.")
        y += 30
        ctx.move_to(x, y)
        ctx.show_text("'LOCAL' tracks positions in file system.")
        y += 30
        ctx.move_to(x, y)
        ctx.show_text("For 'HERO' pleast visit the Regatta Hero")
        y += 20
        ctx.move_to(x, y)
        ctx.show_text("web page for detailed description.")
        y += 20
        ctx.move_to(x, y)
        ctx.show_text("Additionally you should contact your local")
        y += 20
        ctx.move_to(x, y)
        ctx.show_text("race officer to get configuration data.")

    def draw_local(self, ctx):
        x = 8
        x1 = 130
        y = 48
        ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
        ctx.set_font_size(24)
        ctx.move_to(x, y)
        ctx.show_text("Local Tracking")

        ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
        ctx.set_font_size(16)

        y += 30
        ctx.move_to(x, y)
        ctx.show_text("Activated: ")
        ctx.show_text("Yes" if self.app.track.is_active() else "No")
        y += 20
        ctx.move_to(x, y)
        ctx.show_text(f"Log interval: {self.app.track.local_dt} seconds")
        #ctx.show_text(str(self.app.track.local_dt))
        y += 20
        ctx.move_to(x, y)
        ctx.show_text("Log entries written: ")
        ctx.show_text(str(self.app.track.local_lfdno))

        # Anzeige
        # - LAT, LON
        # - bisher gespeicherte Anzahl Positionen
        # - Zeitabstand zwischen den einzelnen Messungen
        # - Hinweis wo gespeichert wird
        y += 30
        ctx.move_to(x, y)
        ctx.show_text("Latitude: ")
        ctx.move_to(x1, y)
        ctx.show_text(self.bv_lat.format())
        y += 20
        ctx.move_to(x, y)
        ctx.show_text("Longitude: ")
        ctx.move_to(x1, y)
        ctx.show_text(self.bv_lon.format())
        y += 20
        ctx.move_to(x, y)
        ctx.show_text("HDOP: ")
        ctx.move_to(x1, y)
        ctx.show_text(self.bv_hdop.format() or '---')
        y += 20
        ctx.move_to(x, y)
        ctx.show_text("Speed: ")
        ctx.move_to(x1, y)
        ctx.show_text(self.bv_sog.format())

    def draw_hero(self, ctx):
        """
        Regatta Hero Normalansicht
        TODO
        racephase anzeige zu Debuggingzwecken
        """

        ctx.select_font_face("DSEG7 Classic")
        ctx.set_font_size(80)

        if self.app.track.is_active():
            if self.app.track.hero_racestatus:
                counter = self.app.track.hero_racestatus['time']
                minutes, seconds = divmod(abs(counter), 60)
                if counter < 0:
                    ctx.move_to(16, 120)
                    ctx.show_text(f"-{minutes:02d}:{seconds:02d}")
                else:
                    ctx.move_to(28, 120)
                    ctx.show_text(f"{minutes:03d}:{seconds:02d}")
            else:
                ctx.move_to(48, 120)
                ctx.show_text("--:--")
        else:
            ctx.move_to(100, 120)
            ctx.show_text("off")

        if self.app.track.hero_timedelta > 5:
            ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
            ctx.set_font_size(16)
            ctx.move_to(8, 260)
            ctx.show_text(f"!!! Time drift of {self.app.track.hero_timedelta} seconds")


        x0 = 8
        x1 = 96
        y0 = 150
        yoffset = 18
        ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
        ctx.set_font_size(16)

        # Debug: Phase anzeigen
        ctx.move_to(360, 35)
        ctx.show_text(str(self.app.track.hero_racephase))

        ctx.move_to(x0, y0)
        ctx.show_text("Type")
        ctx.move_to(x1, y0)
        ctx.show_text(self.app.track.ttype)

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Regatta")
        ctx.move_to(x1, y0)
        ctx.show_text(self.app.track.hero_raceid or '[not selected]')

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Course")
        ctx.move_to(x1, y0)
        if self.app.track.hero_orgstatus and self.app.track.hero_raceid:
            ctx.show_text(self.app.track.hero_orgstatus['races'][self.app.track.hero_raceid]['courseid'])
        else:
            ctx.show_text('[not selected]')

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Latitude")
        ctx.move_to(x1, y0)
        ctx.show_text(self.bv_lat.format())

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Longitude")
        ctx.move_to(x1, y0)
        ctx.show_text(self.bv_lon.format())

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Speed")
        ctx.move_to(x1, y0)
        ctx.show_text(self.bv_sog.format())

        # Flaggen
        if self.app.track.hero_racestatus:
            pos = 0
            for f in self.app.track.hero_racestatus['flags']:
                if f in self.flagmap:
                    # TODO Context save/restore erforderlich?
                    ctx.save()
                    ctx.set_source_surface(self.sym_flag[self.flagmap[f]], *self.flagpos[pos])
                    ctx.paint()
                    ctx.restore()
                pos += 1
                
    def draw_config(self, ctx):

        ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
        ctx.set_font_size(24)
        ctx.move_to(4, 42)
        ctx.show_text("Tracker configuration")

        # Linke Spalte mit Daten

        x0 = 8  # Labelspalte
        x1 = 88 # Datenspalte
        y0 = 70
        yoffset = 16

        # Bootsdaten
        ctx.set_font_size(20)
        ctx.move_to(x0, y0)
        ctx.show_text("Boat data")

        y0 += yoffset + 5
        ctx.set_font_size(16)

        ctx.move_to(x0, y0)
        ctx.show_text("Name")
        ctx.move_to(x1, y0)
        ctx.show_text(self.cfg['boat']['name'])

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Class")
        ctx.move_to(x1, y0)
        ctx.show_text(self.cfg['boat']['class'])

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Handicap")
        ctx.move_to(x1, y0)
        ctx.show_text(str(self.cfg['boat']['handicap']))

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Club")
        ctx.move_to(x1, y0)
        ctx.show_text(self.cfg['boat']['club'])

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Sailno.")
        ctx.move_to(x1, y0)
        ctx.show_text(self.cfg['boat']['sailno'])

        # Trackerdaten
        y0 += yoffset + 10
        ctx.set_font_size(20)
        ctx.move_to(x0, y0)
        ctx.show_text("Tracker info")

        y0 += yoffset + 5
        ctx.set_font_size(16)

        ctx.move_to(x0, y0)
        ctx.show_text("Type")
        ctx.move_to(x1, y0)
        ctx.show_text(self.app.track.ttype)

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Org.")
        ctx.move_to(x1, y0)
        if self.app.track.hero_orgstatus:
            ctx.show_text(self.app.track.hero_orgstatus['orgname'])
        else:
            ctx.show_text("n/a")

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Status")
        ctx.move_to(x1, y0)
        if not self.app.track.hero_racestatus:
            ctx.show_text("inactive")
        else:
            #TODO Mehr Details
            ctx.show_text("active")

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Team")
        ctx.move_to(x1, y0)
        ctx.show_text(self.app.track.team)

        y0 += yoffset
        ctx.move_to(x0, y0)
        ctx.show_text("Server")
        ctx.move_to(x1, y0)
        if self.app.track.mqtt_connected:
            ctx.show_text("MQTT")
        else:
            ctx.show_text("offline")

        # Rechte Spalte mit Regattaauswahl

        x = 208
        y = 70
        yoffset = 21

        # Mögliche Regatten
        self.races = self.app.track.hero_get_races()
        if len(self.races) == 1:
            self.hero_raceid = self.races[0]
            self.menupos = 1

        ctx.set_font_size(20)
        ctx.move_to(x, y)
        ctx.show_text("Select Regatta")
        ctx.set_font_size(16)
        y += 4
        if self.menupos > len(self.races):
            # Nichts auswählen
            self.menupos = 0
            self.raceid = None
        i = 0
        for r in self.races:
            i += 1
            if r == self.raceid:
                r = f"\xbb {r} \xab"
            self.draw_text_boxed(ctx, x, y, 180, 20, r, (self.menupos == i))
            y += 20
        if i == 0:
            ctx.move_to(x, y + 20)
            ctx.show_text("[ none ]")

    def draw_info(self, ctx):
        """
        Nachricht der Wettfahrtleitung
        """
        ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
        ctx.set_font_size(24)
        ctx.move_to(4, 42)
        ctx.show_text("Message from race officers")

        ctx.set_font_size(16)
        if not self.app.track.hero_orgstatus or self.app.track.hero_orgstatus['message'] == '':
            ctx.move_to(8, 72)
            ctx.show_text("[ empty ]")
        else:
            lines = self.app.track.hero_orgstatus['message'].splitlines()
            y = 72
            for l in lines:
                ctx.move_to(8, y)
                ctx.show_text(l)
                y += 18

    def draw(self, ctx):
        if self.mode == 'N':
            if self.ttype in ('LOCAL', 'SDCARD'):
                self.draw_local(ctx)
            elif self.ttype == 'HERO':
                self.draw_hero(ctx)
            else:
                self.draw_none(ctx)
        elif self.mode == 'C':
            self.draw_config(ctx)
        else:
            self.draw_info(ctx)
        if self.query_active:
            self.draw_query(ctx, "A B O R T   R A C E", "Are you sure to abort the current race? This cannot be undone. Are you sure?")
