/*
 * SYSTÈME RX/TX CW ESP-NOW - STATION F4HQJ
 * Optimisé pour : Faible latence, Puissance Max, S-mètre calibré
 */

#include <esp_now.h>
#include <WiFi.h>
#include <esp_wifi.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// --- CONFIGURATION MATÉRIELLE ---
#define WIFI_CHANNEL 1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

const int PIN_BUTTON = 4;   // Manipulateur (Clé CW)
const int PIN_LED_TX = 17;  // LED Rouge (Émission)
const int PIN_LED_RX = 16;  // LED Verte (Réception)
const int PIN_AUDIO  = 25;  // Sortie Audio (HW-104)
const int TONE_FREQ  = 700; // Tonalité 700Hz

// Adresse de Broadcast (Envoi à tout le monde sur le canal)
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

// --- VARIABLES ---
volatile bool localState = false;
volatile bool remoteState = false;
bool lastButtonState = HIGH;
int rssiSignal = -100; 
bool screenUpdatePending = true;
uint32_t lastDisplayUpdate = 0;

typedef struct struct_message {
  bool state;
} struct_message;

struct_message myData;

// --- FONCTION DE MISE À JOUR PHYSIQUE (AUDIO & LED) ---
// Cette fonction est appelée immédiatement pour éviter le lag
void updatePhysicalOutputs() {
  if (localState || remoteState) {
    tone(PIN_AUDIO, TONE_FREQ);
  } else {
    noTone(PIN_AUDIO);
  }
  digitalWrite(PIN_LED_TX, localState);
  digitalWrite(PIN_LED_RX, remoteState);
}

// --- DESSIN DE L'INTERFACE OLED ---
void drawUI() {
  display.clearDisplay();
  
  // 1. ZONE JAUNE (Haut)
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("CW 2.4 Ghz ");
  display.setCursor(95, 0);
  display.print("CH:01");

  // 2. ZONE BLEUE (Bas)
  display.setCursor(0, 18);
  display.print("STATION: F4HQJ");
  display.setCursor(0, 28);
  display.print("PWR: MAX (20dBm)");

  // --- ÉCHELLE DU S-MÈTRE CORRIGÉE ---
  // Espacement réduit pour que tout tienne sur 128px
  display.setCursor(0, 40);
  display.print("S1  S3  S5  S7  S9+"); 
  
  // Calcul de la barre avec échelle ajustée
  // -90dBm = S1 (vide) / -45dBm = S9+ (plein)
  int barWidth = map(rssiSignal, -90, -45, 0, 128);
  barWidth = constrain(barWidth, 0, 128);
  
  // Cadre de la barre
  display.drawRect(0, 50, 128, 6, SSD1306_WHITE);
  // Remplissage
  display.fillRect(0, 50, barWidth, 6, SSD1306_WHITE);

  // État TX/RX
  display.setCursor(0, 57);
  if (localState) {
    display.print(">> TRANSMISSION [TX]");
  } else if (remoteState) {
    display.print("<< RECEPTION    [RX]");
  } else {
    display.print("   STATION EN VEILLE");
  }

  display.display();
}

// --- CALLBACK RÉCEPTION ESP-NOW ---
void OnDataRecv(const esp_now_recv_info *recv_info, const uint8_t *incomingData, int len) {
  // Capture du RSSI (Puissance du signal reçu)
  rssiSignal = recv_info->rx_ctrl->rssi;

  if (len == sizeof(myData)) {
    memcpy(&myData, incomingData, sizeof(myData));
    remoteState = myData.state;
    
    // Priorité absolue au son
    updatePhysicalOutputs();
    
    // On demande la mise à jour de l'écran (sera faite dans la loop)
    screenUpdatePending = true;
  }
}

void setup() {
  Serial.begin(115200);

  // Configuration des Pins
  pinMode(PIN_BUTTON, INPUT_PULLUP);
  pinMode(PIN_LED_TX, OUTPUT);
  pinMode(PIN_LED_RX, OUTPUT);
  pinMode(PIN_AUDIO, OUTPUT);

  // Initialisation OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    Serial.println("Erreur OLED");
  }
  display.clearDisplay();
  display.display();

  // Initialisation WiFi
  WiFi.mode(WIFI_STA);

  // ==========================================
  // RÉGLAGE PUISSANCE MAXIMUM
  // 78 correspond à environ 19.5 - 20 dBm
  // ==========================================
  esp_wifi_set_max_tx_power(78);

  // Forçage du canal Wi-Fi
  esp_wifi_set_promiscuous(true);
  esp_wifi_set_channel(WIFI_CHANNEL, WIFI_SECOND_CHAN_NONE);
  esp_wifi_set_promiscuous(false);

  // Initialisation ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Erreur ESP-NOW");
    return;
  }

  // Enregistrement du callback
  esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));

  // Configuration du Peer (Broadcast)
  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = WIFI_CHANNEL;
  peerInfo.encrypt = false;
  
  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Erreur Peer");
  }

  drawUI();
  Serial.println("F4HQJ : Prêt en puissance Max");
}

void loop() {
  bool currentButtonState = digitalRead(PIN_BUTTON);

  // 1. GESTION DU MANIPULATEUR (PRIORITÉ HAUTE)
  if (currentButtonState != lastButtonState) {
    localState = (currentButtonState == LOW); // LOW = Pressé
    
    // Envoi immédiat
    myData.state = localState;
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    
    // Retour sonore et LED immédiat (Sidetone)
    updatePhysicalOutputs();
    
    lastButtonState = currentButtonState;
    screenUpdatePending = true;
  }

  // 2. TIMEOUT SIGNAL (Remise à zéro du S-mètre après 2 sec de silence)
  static uint32_t lastActivity = 0;
  if (localState || remoteState) {
    lastActivity = millis();
  } else if (millis() - lastActivity > 2000 && rssiSignal > -100) {
    rssiSignal = -100;
    screenUpdatePending = true;
  }

  // 3. MISE À JOUR DE L'ÉCRAN (PRIORITÉ BASSE)
  // On ne rafraîchit l'OLED que s'il y a un changement ET pas plus de 10 fois par seconde
  // Cela évite que le bus I2C de l'écran ne bloque le processeur pendant la manipulation
  if (screenUpdatePending && (millis() - lastDisplayUpdate > 100)) {
    drawUI();
    screenUpdatePending = false;
    lastDisplayUpdate = millis();
  }
}