// ============================================================
// LCD_U8g2_GM12864_64x128.h
// Pilote LCD GM12864I  128x64 pixels — ST7567 I2C via U8g2
// Cible : Arduino UNO R3  (AVR ATmega328P — 2048 bytes RAM)
//
// Controleur : ST7567  ENH_DG128064I
// Bus        : I2C software (SW_I2C)  SDA=A4  SCL=A5
// Adresse    : 0x3F * 2 = 0x7E
// Horloge    : 200 kHz
// Contraste  : 255
// Police     : profont10_tf  (5 x 8 px — 24 caracteres par ligne max)
//
// Mise en page 128x64 — 24 chars/ligne :
//
//  ┌────────────────────────┐
//  │  --- Station Meteo --- │  ligne 0  y= 9   (2 espaces debut)
//  │                        │  ligne 1  y=17   (libre)
//  │Vent:045kmh |Pluie: 12mm│  ligne 2  y=25   | au char 12
//  │Temp:+25C   |Hum.: 65%  │  ligne 3  y=33   | au char 12
//  │Pres:1013hPa            │  ligne 4  y=41
//  │N  NE  E SE  S SO  O  NO│  ligne 5  y=49   regle direction
//  │      ██                │  ligne 6  y=57   carre girouette 10x4px
//  └────────────────────────┘
//
// Separateur | fixe au caractere 12 sur les lignes 2 et 3
//
// Formats snprintf — valeurs calee a droite avec padding espace :
//   Vent  : %3u   "  0" "  9" " 96" "106"   jamais de debordement
//   Pluie : %3u   "  0" "  9" " 12" "100"
//   Temp  : %+3d  " +0" " -5" "+25" "+99"   signe toujours present
//   Hum   : %3u   "  0" "  9" " 65" "100"
//   Pres  : %4u   "1013"                     hPa = pr+900
//
// Anti-valeur-fantome : largeur FIXE — les espaces de padding
//   ecrasent les chiffres precedents plus larges.
//   ex: 106 → 96 : "106" → " 96"  (le '1' est ecrase par ' ')
//
// Carre girouette 10x4px sous la direction active :
//   Position X pixel (marge cadre 3px + char_index * 5px) :
//   N= 3  NE=18  E=33  SE=43  S=58  SO=68  O=83  NO=98
//
// Rafraichissement : lcdAfficherMeteo() a appeler toutes les 1000ms
//   Test dans loop() :
//   if (millis() - _lastMillisLCD >= 1000) {
//       _lastMillisLCD = millis();
//       lcdAfficherMeteo(...);
//   }
//
// Fonctions exportees :
//   lcdInit()              — init U8g2 + ecran demarrage 2s
//   lireGirouette(pin)     — lecture ADC + decodage 8 positions
//   lcdAfficherMeteo()     — affichage complet firstPage/nextPage
// ============================================================

#ifndef LCD_U8G2_GM12864_64X128_H
#define LCD_U8G2_GM12864_64X128_H

#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>

// ── Parametres LCD ────────────────────────────────────────────
#define LCD_I2C_ADDR   (0x3F * 2)    // 0x7E
#define LCD_BUS_CLOCK  200000         // 200 kHz
#define LCD_CONTRASTE  255

// ── Constructeur mode 1 page (_1_) ───────────────────────────
// _1_ = 128 bytes RAM  — OBLIGATOIRE sur UNO R3 (2048 bytes RAM total)
// _F_ = 1024 bytes RAM — INTERDIT sur R3 (overflow stack)
// Mode _1_ impose firstPage()/nextPage() — sendBuffer() INTERDIT
U8G2_ST7567_ENH_DG128064I_1_SW_I2C u8g2(
    U8G2_R0, SCL, SDA, U8X8_PIN_NONE
);

// ── Macro position Y baseline par numero de ligne ─────────────
// Police profont10 : 8px hauteur
// Cadre arrondi 2px + marge 2px + baseline +7px
// Ligne 0:y=9  L1:y=17  L2:y=25  L3:y=33  L4:y=41  L5:y=49  L6:y=57
#define LCD_Y(n)  (2 + 8*(n) + 7)

// ── Buffer ligne partage — 25 chars (24 + \0) ────────────────
// Reutilise par toutes les lignes — pas d allocation dynamique
static char _buf[25];

// ── Chrono rafraichissement LCD — accessible depuis loop() ────
// Test : if (millis() - _lastMillisLCD >= 1000) { ... }
static unsigned long _lastMillisLCD = 0;

// ============================================================
// TABLEAU GIROUETTE — 8 positions analogiques mesurées
// ============================================================

// Valeurs ADC 0..1023 pour chaque direction (mesurées sur le capteur)
//   Index :  0    1    2    3    4    5    6    7    8
//   Dir.  :  -    N   NE    E   SE    S   SO    O   NO
static const int _tableauGirouette[9] = {
      0,   // 0 : sentinelle (non utilise)
    786,   // 1 : N
    454,   // 2 : NE
     86,   // 3 : E
    181,   // 4 : SE
    284,   // 5 : S
    627,   // 6 : SO
    945,   // 7 : O
    886    // 8 : NO
};

// Tolerance de reconnaissance ±3 unites ADC
#define GIROUETTE_TOLERANCE  3

// ── Position X pixel du carre sous chaque etiquette ──────────
// Ligne 5 = "N  NE  E SE  S SO  O  NO"
//            0  3   6 8   11 13  16 19   <- index char debut de chaque label
// x_pixel = 3 (marge cadre) + index_char * 5 (profont10 = 5px/char)
//   N=3  NE=18  E=33  SE=43  S=58  SO=68  O=83  NO=98
// Carre 10px large couvre 2 chars (10px = 2*5px)
static const uint8_t _posXDir[9] = {
      0,   // 0 : non utilise
      3,   // 1 : N   x=3+0*5=3
     18,   // 2 : NE  x=3+3*5=18
     33,   // 3 : E   x=3+6*5=33
     43,   // 4 : SE  x=3+8*5=43
     58,   // 5 : S   x=3+11*5=58
     68,   // 6 : SO  x=3+13*5=68
     83,   // 7 : O   x=3+16*5=83
     98    // 8 : NO  x=3+19*5=98
};

// ============================================================
// lireGirouette(pin)
//-----------------------------------------------------------------------------
// Entree  : uint8_t pin    broche analogique (ex: A1)
// Sortie  : uint8_t        index direction 1..8 si position reconnue
//                          0 si valeur transitoire entre deux positions
// Var mod : aucune
// Remarque: Si retourne 0 → garder la derniere valeur valide dans loop()
//
// Algorithme :
//   val = analogRead(pin)  (0..1023)
//   Pour i=1..8 : si |_tableauGirouette[i] - val| < GIROUETTE_TOLERANCE
//                 → retourner i
//   Retourner 0  (aucune position reconnue — valeur transitoire)
// ============================================================
uint8_t lireGirouette(uint8_t pin) {
    int val = analogRead(pin);
    for (int i = 1; i < 9; i++) {
        int delta = _tableauGirouette[i] - val;
        // delta positif ou negatif — test sans valeur absolue (evite abs())
        if (delta > -GIROUETTE_TOLERANCE && delta < GIROUETTE_TOLERANCE) {
            return (uint8_t)i;
        }
    }
    return 0;   // valeur transitoire — l appelant garde la derniere valeur valide
}

// ============================================================
// lcdInit()
//-----------------------------------------------------------------------------
// Entree  : rien
// Sortie  : rien
// Var mod : _lastMillisLCD remis a 0
// Remarque: mode _1_ — firstPage/nextPage UNIQUEMENT
//           sendBuffer() INTERDIT en mode _1_
//           Affiche ecran de demarrage 2s ("  --- Station Meteo ---")
//
// Sequence init :
//   setI2CAddress(0x7E) → setBusClock(200kHz) → begin()
//   setContrast(255) → setFont(profont10) → setFontDirection(0)
//   firstPage / drawRFrame + drawStr / nextPage
//   delay(2000)
// ============================================================
void lcdInit(void) {

    _lastMillisLCD = 0;

    u8g2.setI2CAddress(LCD_I2C_ADDR);
    u8g2.setBusClock(LCD_BUS_CLOCK);
    u8g2.begin();
    u8g2.setContrast(LCD_CONTRASTE);
    u8g2.setFont(u8g2_font_profont10_tf);
    u8g2.setFontDirection(0);

    // ── Ecran de demarrage — 2 secondes ──────────────────────
    // "  --- Station Meteo ---" = 2 espaces + 21 chars = 23 chars
    u8g2.firstPage();
    do {
        u8g2.drawRFrame(0, 0, 128, 64, 4);
        u8g2.drawStr(3, LCD_Y(0), "  --- Station Meteo ---");
        u8g2.drawStr(3, LCD_Y(2), "    Demarrage...      ");
    } while (u8g2.nextPage());

    delay(2000);
}

// ============================================================
// lcdAfficherMeteo(vV, posGirouette, pl, t, hum, pr)
//-----------------------------------------------------------------------------
// Entree  : uint8_t vV            vitesse vent    km/h   0..255
//           uint8_t posGirouette  index girouette  1..8  (0=inconnu)
//           uint8_t pl            pluviometre      mm    0..255
//           int8_t  t             temperature      degC  -128..+127
//           uint8_t hum           humidite         %RH   0..100
//           uint8_t pr            pression encodee       hPa-900
//
// Sortie  : affichage LCD complet — une passe firstPage/nextPage
// Var mod : _buf[25] buffer statique partage
//           _comptCoeur compteur defilement "..."
//
// Mise en page identique au recepteur (LCD_GM12864_64x128.h case 1) :
//
//  ┌────────────────────────┐   colonne index 0..23
//  │  --- Station Meteo --- │  L0  titre fixe
//  │ ...                    │  L1  points "..." defilants
//  │Vent:  0kmh |Pluie:  0mm│  L2  | a x=63 (char 12 * 5px + 3px marge)
//  │Temp: +0C   |Hum. :  0pc│  L3  | a x=63   "pc" = "%"
//  │Pres:1013 hPa           │  L4  espace avant hPa
//  │N  NE  E SE  S SO  O  NO│  L5  regle fixe
//  │      ▓▓                │  L6  carre 6x8px
//  └────────────────────────┘
//
// Formats — identiques recepteur :
//   L2 : "Vent:%3ukmh |Pluie:%3umm"    | a x=63
//   L3 : "Temp:%+3dC   |Hum. :%3upc"   | a x=63  "pc"="%"
//   L4 : "Pres:%4u hPa"
//   %3u  → "  0" "  9" " 96" "100"
//   %+3d → " +0" " -5" "+25" "+99"
//
// Carre girouette : drawBox(x, LCD_Y(5)+2, 6, 8)
//   La frame est redessinee a chaque firstPage → pas d effacement necessaire
//   Positions X (marge 3px + char_index * 5px) :
//   N= 3  NE=18  E=33  SE=43  S=58  SO=68  O=83  NO=98
//
// "..." defilants : _comptCoeur 0..21 par pas de 1
//   avancent de 5px (1 char) a chaque appel
//   reset automatique car frame redessinee (pas de drawBox effacement)
// ============================================================

static uint8_t _comptCoeur = 0;   // compteur defilement points L1

void lcdAfficherMeteo(uint8_t vV,
                      uint8_t posGirouette,
                      uint8_t pl,
                      int8_t  t,
                      uint8_t hum,
                      uint8_t pr)
{
    // ── Calcul position "..." AVANT firstPage ─────────────────
    // OBLIGATOIRE : _comptCoeur ne doit PAS etre incremente
    // dans le do{}while — le mode _1_ fait plusieurs passes
    // et incrementerait _comptCoeur plusieurs fois par appel
    uint8_t xCoeur = _comptCoeur;   // snapshot pour cette frame
    _comptCoeur++;
    if (_comptCoeur >= 22) _comptCoeur = 0;

    // ── Preparation buffer ────────────────────────────────────
    snprintf(_buf, sizeof(_buf), "Vent:%3ukmh |Pluie:%3umm",
             (unsigned)vV, (unsigned)pl);
    // Stocke L2 dans _buf avant firstPage (police pas encore chargee)

    u8g2.firstPage();
    do {
        // setFont OBLIGATOIRE dans la boucle — mode _1_ reset le contexte
        u8g2.setFont(u8g2_font_profont10_tf);
        u8g2.setFontDirection(0);

        // ── Cadre arrondi ─────────────────────────────────────
        u8g2.drawRFrame(0, 0, 128, 64, 4);

        // ── Ligne 0 : titre fixe ──────────────────────────────
        u8g2.drawStr(3, LCD_Y(0), "  --- Station Meteo ---");

        // ── Ligne 1 : points "..." defilants ──────────────────
        // xCoeur calcule AVANT la boucle — pas d increment ici
        // 0..21 par pas de 1 — avance de 5px (1 char) a chaque appel
        u8g2.drawStr(3 + 5*xCoeur, LCD_Y(1), "...");

        // ── Ligne 2 : Vent | Pluie ────────────────────────────
        // "Vent:XXXkmh |Pluie:XXXmm" = 24ch — | au char 12
        //   gauche 12ch = 60px : "Vent:"(5) + val(3) + "kmh "(4)
        //   | a x=3+60=63px
        //   droite 11ch = 55px : "Pluie:"(6) + val(3) + "mm"(2)
        u8g2.drawStr(3, LCD_Y(2), _buf);   // deja calcule avant firstPage

        // ── Ligne 3 : Temp | Hum ──────────────────────────────
        // "Temp:XXXdC   |Hum. :XXXpc" = 24ch — | au char 12
        //   gauche 12ch = 60px : "Temp:"(5) + val(3) + "C   "(4)
        //   "Hum. :" avec espace apres point (identique recepteur)
        //   "pc" remplace "%" (identique recepteur)
        char _buf3[25];
        snprintf(_buf3, sizeof(_buf3), "Temp:%+3dC   |Hum. :%3upc",
                 (int)t, (unsigned)hum);
        u8g2.drawStr(3, LCD_Y(3), _buf3);

        // ── Ligne 4 : Pression ────────────────────────────────
        // "Pres:XXXX hPa" — espace entre valeur et hPa
        char _buf4[16];
        snprintf(_buf4, sizeof(_buf4), "Pres:%4u hPa",
                 (uint16_t)pr + 900);
        u8g2.drawStr(3, LCD_Y(4), _buf4);

        // ── Ligne 5 : regle direction ─────────────────────────
        u8g2.drawStr(3, LCD_Y(5), "N  NE  E SE  S SO  O  NO");

        // ── Ligne 6 : carre 6x8px ─────────────────────────────
        // Frame entiere redessinee → carre precedent efface auto
        // Positions X (marge 3px + char_index * 5px) :
        //   N= 3  NE=18  E=33  SE=43  S=58  SO=68  O=83  NO=98
        if (posGirouette >= 1 && posGirouette <= 8) {
            u8g2.drawBox(_posXDir[posGirouette],
                         LCD_Y(5) + 2,
                         6, 8);
        }

    } while (u8g2.nextPage());
}

#endif // LCD_U8G2_GM12864_64X128_H


/*
============================================================
MISE EN PAGE LCD 128x64 — profont10 (5x8px) — 24 chars/ligne
Marge cadre 3px gauche / 3px droite — 122px utiles = 24*5+2px

  ┌────────────────────────┐  char index 0          23
  │  --- Station Meteo --- │  L0  y= 9   2esp+21ch=23
  │                        │  L1  y=17   libre
  │Vent:045kmh |Pluie: 12mm│  L2  y=25   |@char12   24ch
  │Temp:+25C   |Hum.: 65%  │  L3  y=33   |@char12   24ch
  │Pres:1013hPa            │  L4  y=41   12ch
  │N  NE  E SE  S SO  O  NO│  L5  y=49   regle 24ch
  │      ██                │  L6  y=57   carre NE x=18
  └────────────────────────┘

POSITION CARRE GIROUETTE (drawBox 10x4px) :
  Dir  char_L5  x_pixel  carre [x..x+9]
  N       0       3      [ 3.. 12]
  NE      3      18      [18.. 27]
  E       6      33      [33.. 42]
  SE      8      43      [43.. 52]
  S      11      58      [58.. 67]
  SO     13      68      [68.. 77]
  O      16      83      [83.. 92]
  NO     19      98      [98..107]

FORMAT SNPRINTF ANTI-FANTOME (largeur fixe) :
  %3u  → "  0"  "  9"  " 96"  "100"   (vent, pluie, hum)
  %+3d → " +0"  " -5"  "+25"  "+99"   (temperature avec signe)
  %4u  → " 900" "1013" "1155"          (pression hPa)
  Padding espace ecrase chiffres precedents → pas de valeur fantome

RAFRAICHISSEMENT 1s — dans loop() du .ino :
  if (millis() - _lastMillisLCD >= 1000) {
      _lastMillisLCD = millis();
      lcdAfficherMeteo(vV, posGirouette, pl, t, hum, pr);
  }

MODE PAGE U8g2 :
  firstPage() / do { dessiner tout } while (nextPage())
  sendBuffer() INTERDIT en mode _1_
  Frame entiere redessinee a chaque appel → pas de residuel possible
============================================================
*/
