// Modified from  @gcjr
// https://forum.arduino.cc/t/commands-over-serial/1320091/14

// multiword command processing
//   split separate word/values (i.e. tokens) in command line
//   search for cmd in command-table, cmds[], and execute if found
//   test with example command lines

char s [90];        // for sprintf()

// -----------------------------------------------------------------------------
// arrays of tokens (char *) and corresponging values if numeric
// a token in one word of text/numeric

#define MAX_TOKS   10
char * toks [MAX_TOKS];
int    vals [MAX_TOKS];
int    nTok;

char buf [90];      // char buffer when input is const char


#include <util/crc16.h>
unsigned int crc16a(unsigned char *message) {
   int i;
   unsigned int byte, crc;
   i = 0;
   while (message[i] != 0) {
     _crc16_update (crc,message[i++]);
   }
   return crc;
}

// -------------------------------------
// display the current arrays of tokens and values
void
tokDisp ()
{
    Serial.println ("  tokDisp:");
    for (int n = 0; n < nTok; n++)  {
        sprintf (s, " %6d %s, crc16:%x\n", vals [n], toks [n], crc16a(toks[n]));
        Serial.print (s);
    }
}

// -------------------------------------
// locate tokens in provide string, set ptrs in tokens
int
tokenize (
    char *str )
{
    nTok = 0;

    sprintf (s, "  tokenize: %s\n", str);
    Serial.print (s);

    strcpy (buf, str);  // copy possible const char array into char array

    // initially call strtok() with char array, subsequent calls are passed nul
    // continue processing until nul token found
    // attempt to translate all tokens into numberic value
    for (toks [nTok]  = strtok (buf, " "); toks [nTok]; )  {
        vals [nTok]   = atoi (toks [nTok]);
        toks [++nTok] = strtok (NULL, " ");
    }

    return nTok;
}

// -----------------------------------------------------------------------------
// test command functions used to demonstrate processing
void a (char *toks [], int n) { Serial.print (" a:\n"); tokDisp (); }
void b (char *toks [], int n) { Serial.print (" b:\n"); tokDisp (); }
void c (char *toks [], int n) { Serial.print (" c:\n"); tokDisp (); }
void d (char *toks [], int n) { Serial.print (" d:\n"); tokDisp (); }

// -------------------------------------
// command table - function ptr and command string
struct Cmd {
    void (*f) (char *tok [], int nTok);
    const char *name;
}
cmds [] = {             // table for above command functions
    { a, "tom" },
    { b, "dick" },
    { c, "harry" },
    { d, "martha" },
};
const int Ncmd = sizeof(cmds)/sizeof(Cmd);

// -------------------------------------
// tokenize command line and search for cmd, 1st token, in cmds []
void
processCmd (
    char *buf )
{
    sprintf (s, "\nprocessCmd: %s\n", buf);
    Serial.print (s);

    tokenize (buf);

    for (int n = 0; n < Ncmd; n++)  {
        // execute cmd when found and return
        if (! strcmp (cmds [n].name, toks [0]))  {
            cmds [n].f (toks, nTok);
            return;
        }
    }

    // only reached if cmd not found
    sprintf (s, " processCmd: unknown cmd %s\n", toks [0]);
    Serial.print (s);
}

// -----------------------------------------------------------------------------
// define test command lines
char *str [] = {
    (char *)"martha",
    (char *)"tom 0 1 2 3",
    (char *)"dick xyz 040",
    (char *)"harry 0 1 abc 2",
};
const int Nstr = 4;

void setup ()
{
    Serial.begin (9600);
    delay (200);

    // process each test cmd line
    for (int n = 0; n < Nstr; n++)
        processCmd (str [n]);
}

void loop () { }