/*********************************************************************************************************************
 *
 *   ██████╗ ███╗   ███╗██████╗ ██╗     ██╗███╗   ██╗███████╗
 *  ██╔════╝ ████╗ ████║██╔══██╗██║     ██║████╗  ██║██╔════╝
 *  ██║      ██╔████╔██║██║  ██║██║     ██║██╔██╗ ██║█████╗
 *  ██║      ██║╚██╔╝██║██║  ██║██║     ██║██║╚██╗██║██╔══╝
 *  ╚██████╗ ██║ ╚═╝ ██║██████╔╝███████╗██║██║ ╚████║███████╗
 *   ╚═════╝ ╚═╝     ╚═╝╚═════╝ ╚══════╝╚═╝╚═╝  ╚═══╝╚══════╝
 *
 *  CmdLine — Documentation d'utilisation
 *  Version v20 — UNO R4 Minima + Ethernet W5500
 *  Fichiers : CmdLineTelnet_UNOR4.h  /  CmdLineTelnet_UNOR4.cpp
 *
 *********************************************************************************************************************/

/*====================================================================================================================
 *  FICHIERS DE LA LIBRAIRIE
 *====================================================================================================================
 *
 *
 *  CmdLine.h        Classe CmdLine, constantes paramétrables (#define),
 *                   structure CanalState, déclarations publiques/privées.
 *
 *  CmdLine.cpp      Implémentation complète de toutes les méthodes.
 *
 *====================================================================================================================
 *  PLATEFORMES SUPPORTÉES
 *====================================================================================================================
 *
 *  Plateforme               Connexion réseau    Librairie requise
 *  ──────────────────────   ─────────────────   ────────────────────────────
 *  Arduino UNO R4 Minima    Telnet TCP           Ethernet@2.0.2
 *  + Shield Ethernet W5500  port 23
 *
 *====================================================================================================================
 *  CONSTANTES PARAMÉTRABLES (dans le sketch .ino avant #include)
 *====================================================================================================================
 *
 *  Toutes les constantes peuvent être redéfinies dans le sketch avant l'include :
 *
 *  #define CMDLINE_BUFFER_SIZE      128   Taille max ligne de commande (octets)
 *                                         RAM = BUFFER × HISTORY × 2 canaux
 *
 *  #define CMDLINE_HISTORY_SIZE      10   Profondeur historique (navigation ↑ ↓)
 *                                         ex: 10 × 128 × 2 = 2560 octets RAM
 *
 *  #define ESC_TIMEOUT_MS            40   Délai (ms) après ESC seul avant abandon
 *
 *  #define SEQ_TIMEOUT_MS           200   Délai (ms) pour compléter ESC[x
 *
 *  #define CMDLINE_CASE_INSENSITIVE   1   1 = insensible à la casse
 *                                         PING = Ping = ping
 *                                         SaveConf, LEDon... préservés dans cmd_t
 *                                         0 = strcmp strict
 *
 *  #define CMDLINE_ECHO_DEFAULT       1   1 = écho activé au démarrage (PuTTY)
 *                                         0 = écho désactivé (MobaXterm)
 *                                         Modifiable : cmdLine.setEcho(true/false)
 *
 *====================================================================================================================
 *  FONCTIONNALITÉS
 *====================================================================================================================
 *
 *  SAISIE INTERACTIVE
 *    • Écho des caractères tapés (configurable)
 *    • Backspace / DEL : supprime le caractère à gauche du curseur
 *    • ← → : déplace le curseur — insertion au milieu de la ligne
 *
 *  HISTORIQUE DES COMMANDES
 *    • ↑ ↓ : navigation dans les commandes précédentes
 *    • Taille configurable : CMDLINE_HISTORY_SIZE
 *    • La ligne en cours est sauvegardée avant le 1er ↑
 *    • Doublons consécutifs ignorés
 *
 *  COMPLÉTION TAB
 *    • 1er TAB, 0 match    → BEL (bip)
 *    • 1er TAB, 1 match    → complétion immédiate + espace
 *    • 1er TAB, N matchs   → liste + charge la 1ère + cycle Re-TAB
 *    • Re-TAB              → défile les correspondances en cycle
 *
 *  INSENSIBILITÉ À LA CASSE (CMDLINE_CASE_INSENSITIVE=1)
 *    • PING = Ping = ping → toujours reconnu
 *    • Noms cmd_t avec majuscules préservés : SaveConf, LEDon, serveurWEB
 *    • L'argument n'est JAMAIS converti
 *    • La complétion TAB respecte la casse originale des noms
 *
 *  ÉCHO CONFIGURABLE
 *    • cmdLine.setEcho(true)  → PuTTY Telnet, Serial (écho distant)
 *    • cmdLine.setEcho(false) → MobaXterm, terminal local echo ON
 *    • cmdLine.getEcho()      → lire l'état courant
 *
 *  SORTIE SIMULTANÉE SERIAL + RÉSEAU
 *    • cmdLine.print(...)         → remplace Serial.print(...)
 *    • cmdLine.println(...)       → remplace Serial.println(...)
 *    • cmdLine.print(val, DEC)    → remplace Serial.print(val, DEC/HEX/OCT/BIN)
 *    • cmdLine.println(val, HEX)  → idem avec saut de ligne
 *    • cmdLine.write(buf, len)    → remplace Serial.write(buf, len)
 *    • Supporte : int, float, long, String, char[], F("..."), bool...
 *
 *====================================================================================================================
 *  COMPATIBILITÉ TERMINAUX
 *====================================================================================================================
 *
 *  Terminal       ENTER          Backspace    Écho local    Action requise
 *  ─────────────  ─────────────  ───────────  ────────────  ──────────────────────────────
 *  PuTTY Telnet   CR+LF          0x08/0x7F    NON           Rien (défaut)
 *                 ou CR+NUL                   (géré par     Décocher "Return key sends
 *                 (selon config)               la lib)       Telnet New Line" si besoin
 *  MobaXterm      CR+LF          0x7F         OUI           cmdLine.setEcho(false)
 *  Tera Term      CR+LF          0x7F         NON           Rien
 *  telnet Linux   CR+LF          0x7F         NON           Rien
 *  PuTTY Serial   CR             0x08         NON           Flow control → None
 *  ESP32 BT       selon app BT   selon app    selon app     cmdLine.setEcho(false)
 *                                                           si doublement
 *
 *====================================================================================================================
 *  IMPLANTATION DANS LE SKETCH .ino
 *====================================================================================================================
 *
 *  ── DÉCLARATIONS (avant setup) ────────────────────────────────────────────────
 *
 *  // Optionnel : surcharger les constantes
 *  // #define CMDLINE_BUFFER_SIZE     256
 *  // #define CMDLINE_HISTORY_SIZE     20
 *  // #define CMDLINE_ECHO_DEFAULT      0   // désactivé par défaut (MobaXterm)
 *
 *  #include <CmdLine.h>
 *
 *  CmdLine       cmdLine(Serial);
 *  CmdLineServer serverTelnet(23);     // EthernetServer ou WiFiServer selon plateforme
 *  CmdLineClient clientTelnet;         // EthernetClient ou WiFiClient
 *
 *  #define countof(a) (sizeof(a)/sizeof(a[0]))
 *
 *  // ── Fonctions de commande ────────────────────────────────────────────────
 *  // Signature obligatoire : void nomFonction(const char *arg)
 *
 *  void cmdHelp   (const char *arg) { cmdLine.println(F("Commandes: help ping save")); }
 *  void cmdPing   (const char *arg) { cmdLine.print(F("pong : ")); cmdLine.println(arg); }
 *  void cmdSaveConf(const char *arg){ cmdLine.println(F("Configuration sauvegardee")); }
 *  void cmdLEDon  (const char *arg) { digitalWrite(LED_BUILTIN, HIGH); }
 *  void cmdLEDoff (const char *arg) { digitalWrite(LED_BUILTIN, LOW);  }
 *
 *  // ── Table des commandes ──────────────────────────────────────────────────
 *  // Les noms peuvent avoir des majuscules (SaveConf, LEDon...)
 *  // Accessibles aussi en minuscules si CMDLINE_CASE_INSENSITIVE=1
 *
 *  const cmd_t commands[] = {
 *      { "help",     cmdHelp     },
 *      { "ping",     cmdPing     },
 *      { "SaveConf", cmdSaveConf },   // "saveconf", "SAVECONF"... → reconnu
 *      { "LEDon",    cmdLEDon    },   // "ledon", "LEDON"... → reconnu
 *      { "LEDoff",   cmdLEDoff   },
 *  };
 *
 *  ── SETUP ─────────────────────────────────────────────────────────────────────
 *
 *  void setup() {
 *      Serial.begin(115200);
 *      while (!Serial);
 *
 *      // UNO R4 :
 *      Ethernet.begin(mac, ip);
 *      serverTelnet.begin();
 *
 *
 *      Serial.println(F("## CmdLine START ##"));
 *      Serial.println(F("   Taper help pour les commandes"));
 *      cmdLine.begin(commands, countof(commands));
 *  }
 *
 *  ── LOOP ──────────────────────────────────────────────────────────────────────
 *
 *  void loop() {
 *      // ── Canal Serial ──────────────────────────────────────────────────────
 *      cmdLine.update();
 *
 *      // ── Canal réseau (Telnet) ──────────────────────────
 *      clientTelnet = serverTelnet.available();
 *      cmdLine.updateNetwork(clientTelnet);
 *
 *      // ── Gestion connexion Telnet (première connexion) ─────────────────────
 *      if (clientTelnet && clientTelnet.connected() && premiereConnexion) {
 *          clientTelnet.println(F("Bienvenue sur CmdLine !"));
 *          clientTelnet.println(F("Taper help pour la liste des commandes."));
 *          clientTelnet.print(F("> "));
 *          premiereConnexion = false;
 *
 *          // Ajuster l'écho selon le terminal :
 *          // cmdLine.setEcho(false);  // MobaXterm
 *          // cmdLine.setEcho(true);   // PuTTY (défaut)
 *      }
 *
 *      // ── Autres services ────────────────────────────────────────────────────
 *      // ftpServer.handle();
 *      // webServer.handleClient();
 *  }
 *
 *====================================================================================================================
 *  MÉTHODES DE LA CLASSE CmdLine
 *====================================================================================================================
 *
 *  void begin(const cmd_t *commands, size_t num)
 *       Initialise la lib. Appeler dans setup() après Serial.begin() et réseau.
 *       Affiche "> " sur Serial.
 *
 *  void update()
 *       Traite les octets Serial disponibles. Appeler dans loop() à chaque cycle.
 *
 *  void updateNetwork(CmdLineClient &client)
 *       Traite les octets réseau disponibles. Appeler dans loop() à chaque cycle.
 *       client = serverTelnet.available()  ou  objet BluetoothSerial
 *
 *  void setEcho(bool enable)
 *       Active/désactive l'écho des caractères imprimables.
 *       true  → PuTTY Telnet, Serial
 *       false → MobaXterm, terminal avec local echo ON
 *
 *  bool getEcho()
 *       Retourne l'état courant de l'écho.
 *
 *  void print(...)
 *  void println(...)
 *       Sortie simultanée Serial + canal réseau.
 *       Remplacent Serial.print() dans les fonctions de commande.
 *       Types supportés : int, float, long, String, char[], bool, F("..."), ...
 *
 *  void print(long n, int base)
 *  void print(unsigned long n, int base)
 *  void println(long n, int base)
 *  void println(unsigned long n, int base)
 *       Sortie avec base numérique — remplace Serial.print(val, DEC/HEX/OCT/BIN).
 *       Exemple : cmdLine.print(val, DEC);   cmdLine.print(reg, HEX);
 *       Les types courts (int, byte, uint8_t) se convertissent vers long implicitement.
 *
 *  size_t write(const uint8_t *buf, size_t len)
 *       Ecriture binaire brute simultanee Serial + Telnet (si connecte).
 *       Exemple : cmdLine.write((uint8_t*)buf, idx);
 *       Utile pour envoyer des sequences ANSI ou des buffers prepares.
 *       Retourne le nombre d'octets ecrits sur Serial.
 *
 *====================================================================================================================
 *  HISTORIQUE DES VERSIONS
 *====================================================================================================================
 *
 *
 *  v20  Ajout write(const uint8_t *buf, size_t len) : ecriture binaire brute simultanee
 *       Serial + Telnet. Permet cmdLine.write((uint8_t*)buf, idx).
 *       Ajout surcharges print(long, int) / print(unsigned long, int) et leurs println().
 *       Permet cmdLine.print(val, DEC) / cmdLine.print(reg, HEX) etc.
 *       Organisation .cpp : sections 22 et 23 ajoutees en fin de fichier.
 *       Aucun code ni commentaire existant modifie.
 *
 *  v19  Correction bug casse — commandes non reconnues si nom avec majuscules.
 *       Ajout cmdStrcmpLower() et cmdStrncmpLower() (helpers statiques).
 *       tabComplete() et processCommand() utilisent ces helpers.
 *       La table cmd_t n'est jamais modifiée, casse originale préservée.
 *       IMPORTANT : les helpers doivent être définis AVANT tabComplete() dans .cpp.
 *
 *  v18  setEcho(bool) / getEcho() : contrôle de l'écho à l'exécution.
 *       CMDLINE_ECHO_DEFAULT : valeur par défaut de l'écho.
 *       HISTORY_SIZE → CMDLINE_HISTORY_SIZE (cohérence nommage).
 *
 *  v17  Documentation complète .h et .cpp (en-têtes, IN/OUT, machines d'état).
 *       CMDLINE_CASE_INSENSITIVE : insensibilité à la casse des commandes.
 *       Conversion uniquement sur la commande, argument préservé.
 *
 *  v16  Réécriture majeure basée sur Terminal.cpp (rweather/arduino-projects, MIT).
 *       Machine d'état Telnet 9 états persistante entre appels loop().
 *       Timeout ESC 40ms (valeur Terminal.cpp).
 *       Backspace = "\b \b" (universel PuTTY + MobaXterm).
 *       WILL ECHO réactif RFC 857 (répond au 1er WILL client).
 *       handleKey() commun Serial + Telnet.
 *       Deux canaux indépendants (CanalState séparé pour Serial et Telnet).
 *
 *  v15  Tentatives de correction écho PuTTY — non fonctionnel.
 *
 *  v14  Sketch de diagnostic dump hexadécimal des octets Telnet reçus.
 *       Mesures réelles PuTTY (^M coché/décoché) et MobaXterm.
 *       Identification des vraies séquences : ENTER=CR+LF/CR+NUL, flèches=ESC[x.
 *
 *  v7   Version de base Serial + Telnet pour UNO R4.
 *       Buffer simple, pas d'historique, pas de TAB, pas de flèches.
 *       Filtrage basique des caractères de contrôle.
 *
 *********************************************************************************************************************/


Declaration
// ---------->  Libraire Commande Line CmdLine CLI -- serial / Telnet                 // Version Lib modifie
#include <CmdLineTelnet_UNOR4.h> 
CmdLine cmdLine(Serial);
#define countof(commands) (sizeof(commands)/sizeof(commands[0]))

setup
// Start Serial Commande Line (CmdLine)
  Serial.println(F("## Telnet Commande Line : OK !"));
  Serial.println(F("     Putty ou Kitty configurer en Telnet port 23")); 
  Serial.println(F("     Si besoin : Dans PUTTY le menu Telnet decocher"));
  Serial.println(F("     Return key sends Telnet New Line instead of ^M"));    
  Serial.println(F("## Serial Commande Line (CmdLine) START : OK !"));
  Serial.println(F("     Putty ou Kitty configurer en serial : Flow control selectionner None"));   
  Serial.println();
  Serial.println(F("     Taper help pour voir les commandes disponibles"));
  Serial.println(F("     Taper print pour voir la configuration réseau"));
  Serial.println(F("\033[1;32;40m## -= IoTOS =- System OK : Prise IP --> Start Loop()  \033[0;39;49m"));
  if ( conf.login_cmdTelnetSwitch == 1 ) 
     { Serial.print  (F("## Activer ou desactiver Login CmdLine ou Telnet - Etat : "));                Serial.println(conf.login_cmdTelnetSwitch == 0 ? "OFF" : "ON");   
       Serial.println(F("     Entrer le code de connexion entrer la commande : login <motDePasse>"));
       Serial.println(F("     pour acceder a la ligne de commande"));
       Serial.print  (F("     Le mot de passe acctuel de l'acces au Telnet est : "));                  Serial.println(conf.telnetPassword);  
       previousMillisTelnet = millis();
       nbrEssaiLoginTelnet = 5; 
       Serial.println();    
     }
  cmdLine.begin(commands, countof(commands));

loop
//#####  ----->  Update cmdLine library                                                                   // ---> Execute Librairie CmdLineTelnet_UNOR4.h
  cmdLine.update();
  clientTelnet = serverTelnet.available();                                                                // mise à jour du client global
  cmdLine.updateTelnet(clientTelnet);                                                                     



//#####  ----->  Connexion Telnet
if (clientTelnet && clientTelnet.connected() && premiereConnexionTelnet) {
          cmdLine.println( F("*****  Telnet Client connecte !!"));
          clientTelnet.println(F("\033[1;32;40m"));
          clientTelnet.println(F(" #############################################################"));
          clientTelnet.println(F(" ###  Prise IP  . . . . . . . . . . . .  Connexion Telnet  ###"));
          clientTelnet.println(F(" ###  Arduino UNO R4 - Ethernet 2                          ###"));
          clientTelnet.println(F(" #############################################################"));       
          clientTelnet.println(F("\033[0;39;49m")); // Serial color defaut 
          cmdLine.println();             
          clientTelnet.println(F(" Si besoin : Dans PUTTY le menu Telnet decocher"));
          clientTelnet.println(F(" Return key sends Telnet New Line instead of ^M"));
          clientTelnet.println(F(" - Serial Commande Line (cmdLine).. START  : OK !"));
          clientTelnet.println(F("   Taper help pour voir les commandes disponibles"));
          clientTelnet.println(F("   Taper print pour voir la configuration réseau"));
          clientTelnet.println();           clientTelnet.println();
          if ( conf.login_cmdTelnetSwitch == 1 )                                                // Fonction LOGIN TELNET
             { 
                 clientTelnet.print  (F("\033[1;31;40m"));                                      // Rouge sur fond noir 
                 clientTelnet.println(F(" Acces Refuse !!"));
                 clientTelnet.println(F("    Entrer la commande :"));
                 clientTelnet.println(F("    login xxxxxxx  pour acceder a la ligne de commande"));
                 clientTelnet.println(F("    En cas de perte du mot de passe :"));
                 clientTelnet.println(F("    Connecter vous en Serial apres un RESET"));       
                 clientTelnet.println(F("\033[0;39;49m"));                                      // Serial color defaut 
             } 
 
          clientTelnet.println(); cmdLine.print( F("> "));
          //clientTelnet.flush();  // clear input buffer, else you get strange characters 
          premiereConnexionTelnet = 0; 
          timeoutDeconnexionTelnet = millis();
  }
  if ( (millis() - timeoutDeconnexionTelnet ) >= telnetTimeDuree )  premiereConnexionTelnet = 1;         // timeout deconnexion Telnet

 //#####  ----->  Serial Connexion
  etatActuelConnexionSerial = Serial;
  if (etatActuelConnexionSerial && !etatPrecedentConnexionSerial) {
          Serial.println(F("\033[1;32;40m"));
          Serial.println(F(" #############################################################"));
          Serial.println(F(" ###  Prise IP  . . . . . . . . . . . .  Connexion Serial  ###"));
          Serial.println(F(" ###  Arduino UNO R4 - Ethernet 2                          ###"));
          Serial.println(F(" #############################################################"));       
          Serial.println(F("\033[0;39;49m")); // Serial color defaut 
          Serial.println();             
          Serial.println(F(" Si besoin : Dans PUTTY le menu "));
          Serial.println(F(" Connexion --> Serial decocher : Flow control : None"));
          Serial.println(F(" - Serial Commande Line (cmdLine).. START  : OK !"));
          Serial.println(F("   Taper help pour voir les commandes disponibles"));
          Serial.println(F("   Taper print pour voir la configuration réseau"));
          //premiereConnexionSerial = 0; 
           Serial.println();
           if ( conf.login_cmdTelnetSwitch == 1 )                                                // Fonction LOGIN TELNET
             { 
                 Serial.print  (F("\033[1;31;40m"));                                             // Rouge sur fond noir 
                 Serial.println(F(" Acces Refuse !!"));
                 Serial.println(F("    Entrer la commande :"));
                 Serial.println(F("    login xxxxxxx  pour acceder a la ligne de commande"));
                 Serial.println(F("    En cas de perte du mot de passe :"));
                 Serial.println(F("    Connecter vous en Serial apres un RESET"));       
                 Serial.println(F("\033[0;39;49m"));                                             // Serial color defaut 
             }
           Serial.println();
           Serial.print(F("> "));
  } 
  etatPrecedentConnexionSerial = etatActuelConnexionSerial;
//------------------------------------------------------------------
Attention Declartation Erreur Exemple #define countof(commands) (sizeof(commands)sizeof(commands[0]))