/*
    Grafikroutinen
    (C) Christian Julius 11.2015

    Definitionen:

    Hochdruckgebiet: 1013 - 1040 mb
    Tiefdruckgebiet: 990 - 1000 mbar
    Orkantief      : 950 - 970 mBar

*/

/* ------------ Standard  Include Files --------------- */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/* ------------ Special   Include Files --------------- */
#include "tm_stm32f4_ili9341.h"
#include "tm_stm32f4_rtc.h"

/* ------------ Projekt   Include Files --------------- */
#include "config.h"
#include "graphik.h"
#include "rtc_user.h"

/* ------------ Typen Deklarationen-------------------- */
/* ------------ Defintionen --------------------------- */

/* LCD settings */
#define ILI9341_WIDTH       240
#define ILI9341_HEIGHT      320

#define XPOS_MAX            320
#define YPOS_MAX            240

// Grafikdefinitionen fuer Geometrien von Objekten
#define GRID_XMAX           300
#define GRID_XOFFS          0                           // X 0/0-Position des Koordinatensystems
#define GRID_YOFFS          122                         // Y 0/0-Position des Koordinatensystems
#define GRID_HOEHE          (YPOS_MAX - GRID_YOFFS- 32) // Hoehe des Koordinatensystems [px]
#define GRID_LINES          8
#define GRID_BLOCK_ABSTAND  2                           // Abstand zwischen den Balken [px]
#define GRID_BLOCK_BREITE   ((GRID_XMAX - HISTORY_MAX_IDX * GRID_BLOCK_ABSTAND) / HISTORY_MAX_IDX)   // Breite eines Balkens
#define GRID_FACTOR         1.0                        // Hoehenfaktor für 3/4 Fuellung bei Maximalwert

// Defintionen für Temperatur Skala
#define TEMP_XSIZE          210                         // Laenge der Skala
#define TEMP_YSIZE          20                          // Breite der Skala
#define TEMP_X0             2                           // Linke abs. X Koordinate der Skala
#define TEMP_Y0             24                          // Linke abs. Y Koordinate der Skala
#define TEMP_X1             (TEMP_X0 + TEMP_XSIZE)      // Rechte abs. X Koordinate der Skala
#define TEMP_Y1             (TEMP_Y0 + TEMP_YSIZE)      // Rechte abs. Y Koordinate der Skala
#define TEMP_COLOR          ILI9341_COLOR_BLUE

// Defintionen für Barometer Skala
#define PRES_XSIZE          210                         // Laenge der Skala
#define PRES_YSIZE          20                          // Breite der Skala
#define PRES_X0             2                           // Linke abs. X Koordinate der Skala
#define PRES_Y0             70                          // Linke abs. Y Koordinate der Skala
#define PRES_X1             (PRES_X0 + PRES_XSIZE)      // Rechte abs. X Koordinate der Skala
#define PRES_Y1             (PRES_Y0 + PRES_YSIZE)      // Rechte abs. Y Koordinate der Skala
#define PRES_COLOR          ILI9341_COLOR_MAGENTA

// Andere Einstellungen
#define PRESSURE_NORMAL_LEVEL                           // Normaldruck einzeichnen
#define PRESSURE_DOT_ON_BAR                             // Punkt über aktuellem Wert
#define PRESSURE_DRAW_AVG                               // Mittelwertlinie
#define PRESSURE_24H_LINES

/* ------------ Private Global Variablen ---------------------- */

// Zembretti Vorhersage Texte
const char const forecast[26][42] = {    "Bestaendiges, sonniges Wetter.",
                                         "Schoenes, sonniges Wetter.",
                                         "Das Wetter wird schoen.",
                                         "Sonnig, Tendenz eher unbestaendiger.",
                                         "Sonnig, eventuell Schauer.",
                                         "Heiter, Besserung zu erwarten.",
                                         "Heiter, anfangs evtl. Schauer.",
                                         "Heiter, spaeter dann aber Regen.",
                                         "Anfangs Schauer, dann Besserung.",
                                         "Wechselhaft mit Schauern.",
                                         "Heiter, vereinzelt Regen.",
                                         "Unbestaendig, spaeter Aufklarung",
                                         "Unbestaendig, evtl. Besserung.",
                                         "Schauer mit heiteren Phasen.",
                                         "Schauer, es wird unbestaendiger.",
                                         "Wechselhaft mit etwas Regen.",
                                         "Unbestaendig mit kurzen sonnigen Phasen.",
                                         "Unbestaendig, spaeter Regen.",
                                         "Unbestaendig mit etwas Regen....",
                                         "Sehr wechselhaft, teils sonnig.",
                                         "Teils Regen, wird schlechter....",
                                         "Teils Regen, sehr unbestaendig..",
                                         "Dauerregen, sehr unbestaendig.",
                                         "Regen, sehr unbestaendig.",
                                         "Stuermisch, evtl. Besserung.",
                                         "Stuermisch mit sehr viel Regen."
};



///////////////////////////////////////////////////
// Druckt die Zeit und Datum ins Display
///////////////////////////////////////////////////

void gfx_DrawDateTime(TM_RTC_Time_t const *Zeit)
{
    static uint8_t oldtime;
    static const char Tag[8][3] = {"So","Mo","Di","Mi","Do","Fr","Sa","--"};
    char buf[80];
    uint8_t index;

    // Nur bei Veränderung ausführen
    if (Zeit->seconds == oldtime)
        return;
    oldtime = Zeit->seconds;

    // Evtl. Fehler abfangen
    index = (Zeit->day < 1 || Zeit->day > 7) ? 7 : (Zeit->day-1);
    /* Format time */
    sprintf(buf, "%02u:%02u:%02u Uhr am %s,%02u.%02u.%04u", Zeit->hours,Zeit->minutes,Zeit->seconds,Tag[index], Zeit->date,Zeit->month,Zeit->year + 2000);

    TM_ILI9341_Puts(TransX(0),TransY(239), buf,&TM_Font_11x18,ILI9341_COLOR_WHITE, ILI9341_COLOR_BLACK);

}


///////////////////////////////////////////////////
// Balkendiagramme einzeichnen
///////////////////////////////////////////////////

/* Berechnete Werte von gfx_UpdateData() sind Grundlage dieser Funktion
   Wird diese nicht vorher aufgerufen werden die alten Werte dargestellt
*/

void gfx_DrawHistory(Work_t const *W)
{
    // Erst ab einem definierten Abstand zwischen 2 Drücken Farbe ändern
     #define MAKE_COLOR_SMOOTH  0.25
     char buf[80];
     static uint16_t oldcolor = ILI9341_COLOR_GREEN;
     static float oldpressure;
     static uint16_t oldptr;
     int16_t bar_heigth;
     uint16_t avg,xpos;
     float   y_factor;                     // Muliplikator für Skalisierung der Hohe

    // Hat sich etwas seit dem letzten Aufruf verändert?
    if (HasChanged(W->P_Now,oldpressure) || HasChanged(W->ptr,oldptr))
    {
        oldpressure = W->P_Now;
        oldptr = W->ptr;
    } else
        return;

    // 1 mbar = y_factor Pixel bezogen auf die Grenzen, in denen sich der Druck absolut bewegte
    y_factor = (float)GRID_HOEHE / (W->P_MaxGraph  - W->P_MinGraph);

    // Loesche die alten Diagramme zunächst
    TM_ILI9341_DrawFilledRectangle(TransX(GRID_XOFFS),TransY(GRID_YOFFS),TransX(ILI9341_HEIGHT),TransY(GRID_YOFFS+GRID_HOEHE),ILI9341_COLOR_BLACK);

    // ---------- Waagerechtes Grid zeichnen ---------------
    //TM_ILI9341_DrawRectangle(TransX(0),TransY(GRID_YOFFS-1),TransX(ILI9341_HEIGHT),TransY(ILI9341_WIDTH-30),ILI9341_COLOR_GRAY);
    uint16_t abstand = GRID_HOEHE / GRID_LINES;
    for (uint16_t i = 0; i <= (GRID_LINES + 1); i++) {
        TM_ILI9341_DrawLine(TransX(0),TransY(GRID_YOFFS + (i * abstand)),
                            TransX(ILI9341_HEIGHT),TransY(GRID_YOFFS + (i * abstand)),
                            ILI9341_COLOR_GRAY);
    }

#ifdef PRESSURE_24H_LINES
    // 24 Stunden Linie einzeichnen
    #define HOUR_DISTANCE     12
    uint16_t loops = HISTORY_MAX_IDX / HOUR_DISTANCE;
    for (int16_t i = 1; i <= loops; i++) {
        uint16_t xpos2  = (GRID_XOFFS + GRID_BLOCK_BREITE) + (i * HOUR_DISTANCE) * (GRID_BLOCK_BREITE + GRID_BLOCK_ABSTAND);
        TM_ILI9341_DrawLine(TransX(xpos2),TransY(GRID_YOFFS),
                            TransX(xpos2),
                            TransY(GRID_YOFFS + GRID_HOEHE+2),
                            ILI9341_COLOR_GRAY);

    };

#endif

    // ---- Balkendiagramme zeichnen -------------
    uint16_t color;
    for (uint16_t i = 0; i <= W->ptr;i++)
    {
       // Steigende Druecke gruen, fallende Druecke rot faerben
       if (i > 0) {
            if ((W->History[i].Pressure > (W->History[i-1].Pressure + MAKE_COLOR_SMOOTH)))
                color = ILI9341_COLOR_GREEN;
            else if ((W->History[i].Pressure < (W->History[i-1].Pressure - MAKE_COLOR_SMOOTH)))
                color = ILI9341_COLOR_ORANGE;
            else
                color = oldcolor;

            oldcolor = color;
        }
        else
           color = ILI9341_COLOR_GREEN;

        oldcolor = color;

        if (i == W->ptr)
           color = ILI9341_COLOR_YELLOW;

        // Berechnung der Höhe einer Bar, basierend auf "P_MinGraph"

        bar_heigth = (uint16_t)((W->History[i].Pressure - W->P_MinGraph) * y_factor * GRID_FACTOR);
        xpos       = (GRID_XOFFS + GRID_BLOCK_BREITE) + i * (GRID_BLOCK_BREITE + GRID_BLOCK_ABSTAND);

        // Fehler begrenzen
        if (bar_heigth > GRID_HOEHE)
             bar_heigth = GRID_HOEHE;

        if (bar_heigth < 0)
            bar_heigth = 0;

        TM_ILI9341_DrawFilledRectangle(TransX(GRID_XOFFS + i * (GRID_BLOCK_BREITE + GRID_BLOCK_ABSTAND)),
                                              TransY(GRID_YOFFS),
                                              TransX(xpos),
                                              TransY(GRID_YOFFS + bar_heigth),
                                              color);
    }

#ifdef PRESSURE_DOT_ON_BAR
    // Punkt über dem letzten Wert zeichnen, wenn es passt
    bar_heigth = (uint16_t)((W->History[W->ptr].Pressure - W->P_MinGraph) * y_factor * GRID_FACTOR);
    xpos       = (GRID_XOFFS + GRID_BLOCK_BREITE) + W->ptr * (GRID_BLOCK_BREITE + GRID_BLOCK_ABSTAND);
    if (xpos > 3)
        TM_ILI9341_DrawFilledCircle(TransX(xpos-1),TransY(GRID_YOFFS + bar_heigth),3,ILI9341_COLOR_WHITE);

#endif

#ifdef PRESSURE_DRAW_AVG
    // ------ Mittelwert einzeichnen ---------
    avg = (uint16_t)((W->P_AvgInHistory - W->P_MinGraph) * y_factor * GRID_FACTOR);
    // Linie
    TM_ILI9341_DrawLine(TransX(0),TransY(GRID_YOFFS + avg),
                            TransX(ILI9341_HEIGHT),TransY(GRID_YOFFS + avg),
                            ILI9341_COLOR_YELLOW);

    // Wert auf Linie
    sprintf(buf,"%4.1f",W->P_AvgInHistory);

    // Wenn Beschriftung zu weit oben, dann zeichne unter der Linie
    uint16_t foo;
    if (avg < (GRID_HOEHE-15))
        foo = avg + 12;
    else
        foo = avg - 5;

    TM_ILI9341_Puts(TransX(PRES_X0+1),TransY(GRID_YOFFS + foo), buf,&TM_Font_7x10,ILI9341_COLOR_CYAN, ILI9341_COLOR_BLACK);
#endif

#ifdef PRESSURE_NORMAL_LEVEL
    // Normalwert einzeichnen
    if (W->P_MinGraph < (PRESSURE_NORMAL-1)) {
        uint16_t norm = (uint16_t)((PRESSURE_NORMAL - W->P_MinGraph) * y_factor * GRID_FACTOR);
        // Linie
        TM_ILI9341_DrawLine(TransX(0),TransY(GRID_YOFFS + norm),
                                TransX(ILI9341_HEIGHT),TransY(GRID_YOFFS + norm),
                                ILI9341_COLOR_MAGENTA);
    }
#endif

    // Bereichsgrenzen des dargestellten Druckes einzeichnen
    uint16_t bheigth = (uint16_t)((W->P_MaxInHistory - W->P_MinGraph) * y_factor * GRID_FACTOR);

    sprintf(buf,"%4u",W->P_MaxInHistory);
    TM_ILI9341_Puts(TransX(ILI9341_HEIGHT - 30),TransY(GRID_YOFFS+bheigth), buf,&TM_Font_7x10,ILI9341_COLOR_BLUE, ILI9341_COLOR_BLACK);

    sprintf(buf,"%4u",W->P_MinInHistory);
    TM_ILI9341_Puts(TransX(ILI9341_HEIGHT - 30),TransY(GRID_YOFFS+11), buf,&TM_Font_7x10,ILI9341_COLOR_RED, ILI9341_COLOR_BLACK);

    // Vorhersage Zambretti
    sprintf(buf, "%s",forecast[W->ZFN]);
    TM_ILI9341_Puts(TransX(0),TransY(GRID_YOFFS - 5), buf,&TM_Font_7x10,ILI9341_COLOR_WHITE, ILI9341_COLOR_BLACK);

}

///////////////////////////////////////////////////////////////////////
// Updated den Temperaturgraphen
// Eingabe: Temperatur
///////////////////////////////////////////////////////////////////////

void gfx_DrawTemperature(Work_t const *W) {

    static float old_temperature;
    char buf[] = "Temperatur [C]";
    uint16_t x_bar;
    uint16_t  xtol_max, xtol_min;

    if (!HasChanged(W->T_Now,old_temperature))
        return;
    old_temperature = W->T_Now;

    // Berechnung der Balkenlaenge
    x_bar = (W->T_Now - MIN_TEMP) * (TEMP_X1 - TEMP_X0) / (MAX_TEMP - MIN_TEMP) ;

    xtol_max = (W->T_MaxInHistory - W->T_Now) * (TEMP_X1 - TEMP_X0) / (MAX_TEMP - MIN_TEMP) ;
    xtol_min = (W->T_Now - W->T_MinInHistory) * (TEMP_X1 - TEMP_X0) / (MAX_TEMP - MIN_TEMP) ;

    // Lösche zunächst den alten Graphen + Beschriftung mit schwarz
    TM_ILI9341_DrawFilledRectangle(TransX(0),TransY(TEMP_Y0),ILI9341_HEIGHT,TransY(TEMP_Y1),ILI9341_COLOR_BLACK);
    // Zeichne den Rahmen
    TM_ILI9341_DrawRectangle(TransX(0),TransY(TEMP_Y0),TransX(TEMP_X1),TransY(TEMP_Y1),TEMP_COLOR);
    // Beschrifte den Rahmen
    TM_ILI9341_Puts(TransX(TEMP_X0),TransY(TEMP_Y0-5), buf,&TM_Font_11x18,ILI9341_COLOR_CYAN, ILI9341_COLOR_BLACK);
    // Trage den Balken ein
    TM_ILI9341_DrawFilledRectangle(TransX(TEMP_X0),TransY(TEMP_Y0+2),TransX(x_bar),TransY(TEMP_Y1-2),TEMP_COLOR);
    // Zeichne ein Rechteck mit der Schwankungsbreite um aktuelle Temperatur
    TM_ILI9341_DrawFilledRectangle(TransX(x_bar-xtol_min),TransY(TEMP_Y0+2),TransX(x_bar+xtol_max),TransY(TEMP_Y1-2),ILI9341_COLOR_ORANGE);
    // Markiere Vorderseite des Balkens gelb
    TM_ILI9341_DrawFilledRectangle(TransX(x_bar-1),TransY(TEMP_Y0),TransX(x_bar+1),TransY(TEMP_Y1),ILI9341_COLOR_YELLOW);
    // Trage Temperatur rechts vom Balken ein
    sprintf(buf,"%2.1f",W->T_Now);
    // Feld löschen und zeichnen
    TM_ILI9341_DrawFilledRectangle(TransX(TEMP_X1+7),TransY(TEMP_Y0-2),ILI9341_HEIGHT-10,TransY(TEMP_Y1),ILI9341_COLOR_BLACK);

    // Anzeigewert: Je nach Temperatur farbig machen
    uint16_t color2;
    if (W->T_Now <= 17.0)
        color2 = ILI9341_COLOR_BLUE;
    else if (W->T_Now >= 23.0)
        color2 = ILI9341_COLOR_ORANGE;
    else
        color2 = ILI9341_COLOR_GREEN;

    TM_ILI9341_Puts(TransX(TEMP_X1+12),TransY(TEMP_Y0+22), buf,&TM_Font_16x26,color2, ILI9341_COLOR_BLACK);

}

///////////////////////////////////////////////////////////////////////
// Updated den Druckgraphen
// Eingabe: Druck
///////////////////////////////////////////////////////////////////////

void gfx_DrawPressure(Work_t const *W) {

    static float oldpressure;
    char buf[32];
    uint16_t x_bar, xtol_min, xtol_max;
    uint16_t color2;

    if (!HasChanged(W->P_Now,oldpressure))
        return;
    oldpressure = W->P_Now;


    // Balkenlaenge berechnen
    x_bar = (W->P_Now - W->P_MinGraph) * (PRES_X1 - PRES_X0) / (W->P_MaxGraph - W->P_MinGraph) ;

    // Balken Toleranz berechnen über Aufzeichnungszeit
    xtol_max = (W->P_MaxInHistory - W->P_Now) * (PRES_X1 - PRES_X0) / (W->P_MaxGraph - W->P_MinGraph);
    xtol_min = (W->P_Now - W->P_MinInHistory) * (PRES_X1 - PRES_X0) / (W->P_MaxGraph - W->P_MinGraph);

    // Lösche alten Balken und trage den neuen ein
    TM_ILI9341_DrawFilledRectangle(TransX(0),TransY(PRES_Y0),ILI9341_HEIGHT,TransY(PRES_Y1),ILI9341_COLOR_BLACK);

    // Zeichne den Rahmen
    TM_ILI9341_DrawRectangle(TransX(0),TransY(PRES_Y0),TransX(PRES_X1),TransY(PRES_Y1),PRES_COLOR);

    // Beschrifte den Rahmen
    TM_ILI9341_Puts(TransX(PRES_X0),TransY(PRES_Y0-5),"Druck [mbar]",&TM_Font_11x18,ILI9341_COLOR_CYAN, ILI9341_COLOR_BLACK);

    // Grenzwerte auf Skala oben drucken
    sprintf(buf,"%u ",(uint16_t)W->P_MinGraph);
    TM_ILI9341_Puts(TransX(PRES_X0),TransY(PRES_Y0+PRES_YSIZE+12), buf,&TM_Font_7x10,ILI9341_COLOR_GREEN, ILI9341_COLOR_BLACK);
    sprintf(buf,"%u ",(uint16_t)W->P_MaxGraph);
    TM_ILI9341_Puts(TransX(PRES_X1-28),TransY(PRES_Y0+PRES_YSIZE+12), buf,&TM_Font_7x10,ILI9341_COLOR_GREEN, ILI9341_COLOR_BLACK);

    // Mittelwert des Druckes in die Mitte des Balkens drucken
    sprintf(buf,"%u avg ",(uint16_t)W->P_AvgInHistory);
    TM_ILI9341_Puts(TransX((PRES_X1-PRES_X0)/2-15),TransY(PRES_Y0+PRES_YSIZE+12), buf,&TM_Font_7x10,ILI9341_COLOR_YELLOW, ILI9341_COLOR_BLACK);

    // Balken zeichnen
    TM_ILI9341_DrawFilledRectangle(TransX(PRES_X0),TransY(PRES_Y0+2),TransX(x_bar),TransY(PRES_Y1-2),PRES_COLOR);

    // Toleranzband um aktuellen Wert zeichnen
    TM_ILI9341_DrawFilledRectangle(TransX(x_bar-xtol_min),TransY(PRES_Y0+2),TransX(x_bar+xtol_max),TransY(PRES_Y1-2),ILI9341_COLOR_ORANGE);

    // Vorderseite gelb markieren
    TM_ILI9341_DrawFilledRectangle(TransX(x_bar-1),TransY(PRES_Y0),TransX(x_bar+1),TransY(PRES_Y1),ILI9341_COLOR_YELLOW);

    // Trage Druck rechts vom Balken ein
    sprintf(buf,"%4.1f",W->P_Now);

    // Feld löschen und zeichnen
    TM_ILI9341_DrawFilledRectangle(TransX(PRES_X1+7),TransY(PRES_Y0-2),ILI9341_HEIGHT-10,TransY(PRES_Y1),ILI9341_COLOR_BLACK);

    // Druckwerte rechts neben Skala schreiben
    color2 = (W->P_Now > PRESSURE_NORMAL) ? ILI9341_COLOR_GREEN : ILI9341_COLOR_ORANGE;
    TM_ILI9341_Puts(TransX(PRES_X1+12),TransY(PRES_Y0+20), buf,&TM_Font_16x26,color2, ILI9341_COLOR_BLACK);


}


///////////////////////////////////////////////////////////////////////
// Stellt alle Angaben als Text im Display dar
// Eingabe: keine
///////////////////////////////////////////////////////////////////////

void gfx_DrawTextInfo()
{

}
