/*
 * ReadInput.h - some methods for reading input and picking out two numbers sep by a comma
 * readInput for commands is copied largely from Paul's SystemControl method
 * readLEDcmd is based on SystemConfig/ConfigParam/ReadFromCLI + SystemControl/ReadInput
 * Irene Hu
 * 
 * May 2, 2023
 * 
*/

#ifndef _READINPUT
#define _READINPUT

#define CMD_CHAR_SINGLE '!'
#define CMD_CHAR_CONT '&'
#define CMD_TIMEOUT_MS 5000
#define CMD_BUFFER_SIZE 128

//#define ECHO

#include <Arduino.h>
//#include <string.h> // strtok_r

int readLEDcmd(Stream * in, int * LEDnum, int * duration_ms, int * cmdNum, int numLEDs = 8)
// parses one line ending with a \r
// looks for a cmd !LEDnum,duration_ms, sets cmdNum to 1
// 5/30/23 also looks for &LEDnum,duration_ms, sets cmdNum to 2 (this is to enable commands for continuous mode)
// returns 1 if everything worked; 0 if nothing on input stream or invalid stream; -1 if no cmd char; -2 if format doesn't match; -3 if timeout
// borrows from ReadFromCLI, but modified to only run if bytes available and starts w/ cmd char (for syncing/ID); also to remove some nests
{
    char buffer[CMD_BUFFER_SIZE];
    int bufferIndex = 0;

    if (in == NULL || in->available() <= 0)  return 0; // this entire function only runs if there's input

    char c = in->read();
    // look for command char; this will help start every parse from a new line
    // vs if something messes up partway through a line
    // 5/30/23 now also used for identifying different commands
    #ifdef ECHO
    in->write(c);
    #endif
    if (c == CMD_CHAR_SINGLE)  {
        *cmdNum = 1;
    } else if (c == CMD_CHAR_CONT) {
        *cmdNum = 2;
    } else { // not a valid command
        //#ifdef ECHO
        //in->write("\n\r");
        //#endif
        return -1;
    }

    unsigned long startTimer = millis();

    while (startTimer <= millis() && millis() - startTimer < CMD_TIMEOUT_MS) {

        // Wait on user input
        if (in->available()) {
            // Read the next char and reset timer          
            char c = in->read();
            startTimer = millis();

            if (c == '\r') { // parse string
                #ifdef ECHO
                in->write("\n\r");
                #endif
                if (bufferIndex < CMD_BUFFER_SIZE) {
                    buffer[bufferIndex] = '\0';
                    int result = 0;
                    int LEDVal, durationVal;
                    result = sscanf(buffer, "%d,%d", &LEDVal,&durationVal);
                    if (result == 2 && LEDVal >= 0 && LEDVal < numLEDs && durationVal > 0) {
                        *LEDnum = LEDVal;
                        *duration_ms = durationVal;
                        return 1;
                    }
                    else {
                        //in->printf("Result: %d; LEDval: %d; duration: %d/n/r",result,LEDVal,durationVal);
                        //in->println(buffer); // for debugging - it's failing to parse input
                        return -2;
                    }
                }
            }

            if (c == '\b') {
                bufferIndex -= 1;
                if (bufferIndex < 0) {
                    bufferIndex = 0;
                }
                else if ( bufferIndex >= 0) {
                    #ifdef ECHO
                    in->write("\b \b");
                    #endif
                }
            }

            else {
                if (bufferIndex < CMD_BUFFER_SIZE) {
                    buffer[bufferIndex++] = c;
                    #ifdef ECHO
                    in->write(c); // echo back
                    #endif
                }
            }
        } // if in->available()
    } // end timeout loop

    return -3;
} 

/*
void readInput(Stream *in) {

    char cmdBuffer[CMD_BUFFER_SIZE];
    //int LED, ontime_ms;
    
    if (in != NULL && in->available() > 0) {
        char c = in->read();

        // still look for command char; this will help start every parse from a new line
        // vs if something messes up partway through a line
        if (c == CMD_CHAR) {

            // in->write(c);

            unsigned long startTimer = millis();
            int index = 0;
            while (startTimer <= millis() && millis() - startTimer < CMD_TIMEOUT_MS) {

                // Break if we have exceed the buffer size
                if (index >= CMD_BUFFER_SIZE)
                    break;

                // Wait on user input
                if (in->available()) {
                    // Read the next char and reset timer          
                    c = in->read();
                    startTimer = millis();
                }
                else {
                    continue;
                }

                // Exit command loop on repeat command char
                if (c == CMD_CHAR) {
                    break;
                }                
                
                if (c == '\r') {
                    // Command ended try to 
                    if (index <= CMD_BUFFER_SIZE)
                        cmdBuffer[index++] = '\0';
                    else
                        cmdBuffer[CMD_BUFFER_SIZE-1] = '\0';
                    
                    // Parse Command menus
                    char * rest;
                    char * cmd = strtok_r(cmdBuffer,",",&rest);

                    // pick out first number
                    //LED = std::stoi(cmd);

                    // CFG (configuration commands)
                    if (cmd != NULL && strncmp_ci(cmd,CFG, 3) == 0) {
                        if (rest != NULL) {
                            cfg.parseConfigCommand(rest, in);
                        }
                        else {
                            char timeString[64];
                            getTimeString(timeString);
                            cfg.printConfig(in, timeString);

                        }                            
                    }

                    // Reset the buffer and print out the prompt
                    if (c == '\n')
                        in->write('\r');
                    else
                        in->write("\r\n");

                    in->write(PROMPT);

                    index = 0;                       
                    startTimer = millis();
                    continue;
                }
                
                // Handle backspace
                if (c == '\b') {
                    index -= 1;
                    if (index < 0) {
                        index = 0;
                    }
                    else if ( index >= 0 && cfg.getInt(LOCALECHO)) {
                        in->write("\b \b");
                    }
                }
                else {
                    cmdBuffer[index++] = c;
                    if (cfg.getInt(LOCALECHO))
                        in->write(c);
                }
            }
        }
    }
}

// from Utils, but maybe cleaner in this file
int strncmp_ci(const char * input, const char * command, int n) {
    size_t maxLength = n;
    
    // Coerce to shortest string
    if (strlen(input) < maxLength)
        maxLength = strlen(input);
        
    if (strlen(command) < maxLength)
        maxLength = strlen(command);

    for (unsigned int i=0; i < maxLength; i++) {
        if (tolower(input[i]) < tolower(command[i]))
            return -1;
        if (tolower(input[i]) > tolower(command[i]))
            return 1;
    }

    // string match up to n chars
    return 0;
}*/
    
#endif