#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>

// -------------------- CONFIG ECRAN ST7789 --------------------
// Orientation : 0 = portrait, 1 = paysage
#define TFT_ROTATION 1   // change à 0 pour portrait

#define TFT_CS   10
#define TFT_DC    9
#define TFT_RST   8

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

// Résolution typique 170x320 (rotation gérée ensuite)
#define TFT_W 170
#define TFT_H 320

// -------------------- CONFIG CAPTEUR HALL --------------------
#define HALL_PIN A0
const float VREF = 5.0;
const int ADC_MAX = 1023;

// Calibration Hall
float hallOffset = 2.50;    // tension au repos (V)
float hallSens   = 0.07;    // V/mm

// Filtrage Hall
const int HALL_FILTER_SIZE = 10;
float hallBuffer[HALL_FILTER_SIZE];
int hallIndex = 0;

// -------------------- CONFIG MESURE TENSION HP --------------------
#define HP_VOLT_PIN A1
const float HP_DIV_RATIO = 10.0;

// Filtrage tension HP
const int HP_FILTER_SIZE = 10;
float hpBuffer[HP_FILTER_SIZE];
int hpIndex = 0;

// -------------------- BOUTON RESET --------------------
#define RESET_PIN 4

// -------------------- VARIABLES EXCURSION --------------------
float vMinHall = 5.0;
float vMaxHall = 0.0;

// -------------------- DETECTION Xmax --------------------
const int MAX_POINTS = 30;
float tensionTab[MAX_POINTS];
float excursionTab[MAX_POINTS];
int pointCount = 0;

bool refSlopeSet = false;
float refSlope   = 0.0;

bool  xmaxDetected = false;
float xmaxValue    = 0.0;

const float SLOPE_DROP_THRESHOLD = 0.8; // 80 % de la pente initiale

// -------------------- TEMPO AFFICHAGE --------------------
unsigned long lastDisplay = 0;
const unsigned long DISPLAY_INTERVAL = 80; // ms

// -------------------- ZONES GRAPHIQUE / BARRE --------------------
// En mode paysage (rotation=1), on considère ~320x170
// On dessine le graphique dans une zone centrale
#define GRAPH_X  10
#define GRAPH_Y  70
#define GRAPH_W  300
#define GRAPH_H  70

int graphIndex = 0;

// -------------------- FILTRAGE --------------------
float readFilteredHall() {
  int raw = analogRead(HALL_PIN);
  float v = raw * (VREF / ADC_MAX);

  hallBuffer[hallIndex] = v;
  hallIndex = (hallIndex + 1) % HALL_FILTER_SIZE;

  float sum = 0;
  for (int i = 0; i < HALL_FILTER_SIZE; i++) sum += hallBuffer[i];
  return sum / HALL_FILTER_SIZE;
}

float readFilteredHPVoltage() {
  int raw = analogRead(HP_VOLT_PIN);
  float vArduino = raw * (VREF / ADC_MAX);
  float vHP = vArduino * HP_DIV_RATIO;

  hpBuffer[hpIndex] = vHP;
  hpIndex = (hpIndex + 1) % HP_FILTER_SIZE;

  float sum = 0;
  for (int i = 0; i < HP_FILTER_SIZE; i++) sum += hpBuffer[i];
  return sum / HP_FILTER_SIZE;
}

// -------------------- DETECTION Xmax --------------------
void addPoint(float tension, float excursion) {
  if (pointCount < MAX_POINTS) {
    tensionTab[pointCount]   = tension;
    excursionTab[pointCount] = excursion;
    pointCount++;
  }
}

void computeRefSlope() {
  if (pointCount < 3) return;

  float t1 = tensionTab[0];
  float e1 = excursionTab[0];
  float t2 = tensionTab[pointCount / 2];
  float e2 = excursionTab[pointCount / 2];

  float dt = t2 - t1;
  if (dt <= 0) return;

  refSlope = (e2 - e1) / dt; // mm/V
  refSlopeSet = true;
}

void detectXmax() {
  if (!refSlopeSet || pointCount < 4 || xmaxDetected) return;

  int i1 = pointCount - 2;
  int i2 = pointCount - 1;

  float t1 = tensionTab[i1];
  float e1 = excursionTab[i1];
  float t2 = tensionTab[i2];
  float e2 = excursionTab[i2];

  float dt = t2 - t1;
  if (dt <= 0) return;

  float slope = (e2 - e1) / dt; // mm/V

  if (slope < refSlope * SLOPE_DROP_THRESHOLD) {
    xmaxDetected = true;
    xmaxValue    = e2;
  }
}

// -------------------- THEME COULEUR --------------------
uint16_t getColorForState(const char* etat) {
  if (strcmp(etat, "Lineaire") == 0)      return ST77XX_GREEN;
  if (strcmp(etat, "Non lineaire") == 0)  return ST77XX_YELLOW;
  if (strcmp(etat, "DANGER") == 0)        return ST77XX_RED;
  return ST77XX_WHITE;
}

// -------------------- GRAPHIQUE TEMPS REEL --------------------
void drawGraph(float excursionMM) {
  // Echelle simple : 5 px / mm, limité à la hauteur du graphe
  int y = GRAPH_Y + GRAPH_H - (int)(excursionMM * 5.0);
  if (y < GRAPH_Y) y = GRAPH_Y;
  if (y > GRAPH_Y + GRAPH_H - 1) y = GRAPH_Y + GRAPH_H - 1;

  // Efface la colonne précédente
  tft.drawFastVLine(GRAPH_X + graphIndex, GRAPH_Y, GRAPH_H, ST77XX_BLACK);
  // Trace le point
  tft.drawPixel(GRAPH_X + graphIndex, y, ST77XX_CYAN);

  graphIndex++;
  if (graphIndex >= GRAPH_W) graphIndex = 0;
}

// -------------------- BARRE % Xmax --------------------
void drawBar(float percent, const char* etat) {
  if (percent < 0)   percent = 0;
  if (percent > 200) percent = 200;

  int x = 10;
  int y = GRAPH_Y + GRAPH_H + 10;
  int w = 300;
  int h = 16;

  uint16_t col = getColorForState(etat);

  tft.drawRect(x, y, w, h, ST77XX_WHITE);

  int fillW = (int)(w * (percent / 100.0));
  if (fillW > w) fillW = w;

  if (fillW > 2) {
    tft.fillRect(x + 1, y + 1, fillW - 2, h - 2, col);
  }
}

// -------------------- UI PRINCIPALE --------------------
void drawUI(float vHP, float excursionMM, float slope, float percentXmax,
            float xmaxValueLocal, const char* etat) {

  tft.fillScreen(ST77XX_BLACK);

  uint16_t colEtat = getColorForState(etat);

  tft.setTextSize(2);
  tft.setTextColor(ST77XX_WHITE);

  // Ligne 1 : Tension HP
  tft.setCursor(10, 10);
  tft.print("VHP:");
  tft.print(vHP, 2);
  tft.print("V");

  // Ligne 2 : Excursion
  tft.setCursor(10, 30);
  tft.print("Exc:");
  tft.print(excursionMM, 2);
  tft.print("mm");

  // Ligne 3 : Pente
  tft.setCursor