#include "qVar.h"
#include "qEval.h"

qVar variables;
qEval evaluator;

char* concat(const char* input) {
    if (input == NULL) return NULL; // Return if input is NULL

    char* result = (char*)malloc(sizeof(char) * 16); // Initial buffer size
    if (result == NULL) return NULL; // Return if memory allocation failed

    int resultSize = 16;
    int resultIndex = 0;
    bool inQuotes = false;
    bool escapeNext = false; // To handle escaped characters within quotes
    bool skipNextPlus = false; // To skip '+' right after quotes

    for (int i = 0; input[i] != '\0'; ++i) {
        char current = input[i];

        if (current == '\\' && inQuotes) { // Handle escape character only within quotes
            escapeNext = true;
            continue;
        }

        if (current == '"' && !escapeNext) { // Toggle quote state unless it's escaped
            inQuotes = !inQuotes;
            skipNextPlus = true; // Enable skipping '+' after ending quotes
            continue;
        }

        if (inQuotes) {
            if (escapeNext && current == '"') {
                result[resultIndex++] = current; // Include the escaped quote
            } else if (!escapeNext) {
                result[resultIndex++] = current; // Add current character to result
            }
            escapeNext = false; // Reset the escape flag after processing
        } else {
            // Skip '+' if it's the first character after a closing quote
            if (current == '+' && skipNextPlus) {
                skipNextPlus = false; // Reset flag after skipping
                continue;
            }
            skipNextPlus = false; // Reset flag for other characters

            if ((isdigit(current) || current == '-' || current == '.') && (resultIndex > 0 || !skipNextPlus)) {
                result[resultIndex++] = current;
            }
        }

        // Ensure enough buffer size, realloc if necessary
        if (resultIndex == resultSize - 1) {
            resultSize *= 2;
            char* temp = (char*)realloc(result, resultSize);
            if (temp == NULL) {
                free(result);
                return NULL;
            }
            result = temp;
        }
    }

    result[resultIndex] = '\0'; // Null-terminate the string
    return result;
}

void parse(const char *str) {
  bool inQuotes = false;
  int nestingLevel = 0;
  size_t startIndex = 0; // Start index of the current token
  int blockStart = 0;
  int blockEnd = 0;

  for (size_t i = 0; str[i] != '\0'; i++) {
    char ch = str[i];

    if (ch == '\"') {
      inQuotes = !inQuotes; // Toggle inQuotes flag
    } else if (ch == '{') {
      if (nestingLevel == 0) { blockStart = i - startIndex + 1; }
      nestingLevel++;
    } else if (ch == '}') {
      nestingLevel--;
      if (nestingLevel == 0) { blockEnd = i - startIndex; }
    }

    if ((ch == ';' || ch == '}') && nestingLevel == 0 && !inQuotes) {
      // Store the token
      const size_t tokenSize = i - startIndex + 2; // Include space for the delimiter if it's the detected delimiter
      char token[tokenSize];
      for (size_t j = 0; j < tokenSize - 1; j++) { // Exclude the delimiter if it's not the detected delimiter
        token[j] = str[startIndex + j];
      }
      if (ch == ';') { // Only add delimiter if it's the detected delimiter
        token[tokenSize - 2] = ';'; // Add the delimiter
      }
      token[tokenSize - 1] = '\0'; // Null terminator

      if (ch == '}' || ch == ';') { parseLine(token, blockStart, blockEnd); } // parse line without block

      // Update startIndex for the next token
      startIndex = i + 1;
      blockStart = 0; blockEnd = 0;
    }
  }
}

void parseKeyword(const char *line, int blockStart, int blockEnd) {
  // Extract keyword
  const char *start = line;
  while (*start == ' ') { start++; }
  const char *end = start;
  while (*end != '\0' && ((*end >= 'a' && *end <= 'z') || (*end >= 'A' && *end <= 'Z') || (*end >= '0' && *end <= '9') || *end == '.')) { end++; }
  size_t keywordLen = end - start;
  char keyword[keywordLen + 1]; // +1 for null terminator
  for (size_t i = 0; i < keywordLen; i++) {
    keyword[i] = start[i];
  }
  keyword[keywordLen] = '\0'; // Null-terminate the string

  // Extract block
  size_t blockLen = blockEnd - blockStart;
  char block[blockLen + 1];
  if (blockLen > 0) {
    for (size_t i = 0; i < blockLen; i++) {
      block[i] = line[blockStart + i];
    }
  }
  block[blockLen] = '\0'; // Null-terminate the string

  // Extract parameters

  const char *paramStart = strchr(line, '(');
  //const char *paramEnd = strchr(line, ')');
  const char * paramEnd; // Parameter end
  if ( blockStart != NULL ) { paramEnd = strrchrrBefore(line, ')', blockStart); }
  else { paramEnd = strrchr(line, ')'); }

  size_t len = 0;
  if (paramStart && paramEnd && paramStart < paramEnd) {
    len = paramEnd - paramStart - 1;
  }
  char parameters[len + 1]; // +1 for null terminator
  if (len > 0) {
    for (size_t i = 0; i < len; i++) {
      parameters[i] = paramStart[i + 1];
    }
  }
  parameters[len] = '\0'; // Null-terminate the string
  
  // Handle internal keywords
    if (strcmp(keyword, "if") == 0) {
    //parseIf(line, blockStart, blockEnd);
  } else if (strcmp(keyword, "else") == 0) {
    //parseElse(line, blockStart, blockEnd);
  } else if (strcmp(keyword, "while") == 0) {
    //parseWhile(line, blockStart, blockEnd);
  } else if (strcmp(keyword, "for") == 0) {
    //parseFor(line, blockStart, blockEnd);
  } else if (strcmp(keyword, "print") == 0) {
    keywordPrint(parameters);
  }
  //if ( strcmp(keyword, "print") == 0 ) { keywordPrint(parameters); }
  //Serial.println("[" + String(keyword) + "][" +String(parameters)+"]["+String(block)+"]");
}

const char *strrchrrBefore(const char *array, char target, int position) {
  const char *lastOccurrence = NULL;

  // Iterate backward from the specified position
  for (int i = position - 1; i >= 0; i--) {
    if (array[i] == target) {
      // Store the address of the last occurrence of the target character
      lastOccurrence = &array[i];
      return lastOccurrence; // Return the address of the last occurrence
    }
  }
  return NULL; // Return NULL if not found
}

// Function to split a character array based on a delimiter
// Delimiter is assumed to be a single character
// Ignores delimiters within quoted sections and escapes quotes within quotes
int split(char* input, char** output, char delimiter, int max_splits) {
  int split_count = 0;
  bool in_quotes = false;
  char* ptr = input;
  output[split_count++] = ptr; // First substring always starts from the beginning

  while (*ptr != '\0' && split_count < max_splits) {
    if (*ptr == '\"') {
      in_quotes = !in_quotes; // Toggle quoted section status
    }
    if (*ptr == delimiter && !in_quotes) {
      *ptr = '\0'; // Replace delimiter with null terminator
      output[split_count++] = ptr + 1; // Next substring starts after the delimiter
    }
    ptr++;
  }
  return split_count; // Return the number of splits
}

void keywordPrint ( const char * parameters ) {
  Serial.println(parameters);
}

void parseLine(const char *line, int blockStart, int blockEnd) {
  // A line should either start with a keyword or a variable assignment, we need to detect which we are dealing with here
  const char *equal = strchr(line, '=');
  const char *parenOpen = strchr(line, '(');
  const char *parenClose = strchr(line, ')');
  const char *quote = strchr(line, '"');

  // Variable assignment will meet these criteria
  // 1. Contains equal sign
  // 2. if parenthesis exist, equal sign preceeds it
  // 3. if quote exist, equal sign preceeds it
  if (equal && (!parenOpen || parenOpen > equal) && (!quote || quote > equal)) {
    parseAssignment(line);
  } else {
    // Try and parse a keyword
    parseKeyword(line, blockStart, blockEnd);
  }
}

void parseAssignment(const char *line) {
  // Implementation of parseAssignment
  // Extract target
  const char *start = line;
  while (*start == ' ') { start++; }
  const char *end = start;
  while (*end != '\0' && ((*end >= 'a' && *end <= 'z') || (*end >= 'A' && *end <= 'Z') || (*end >= '0' && *end <= '9') || *end == '.')) { end++; }
  size_t targetLen = end - start;
  char target[targetLen + 1]; // +1 for null terminator
  for (size_t i = 0; i < targetLen; i++) {
    target[i] = start[i];
  }
  target[targetLen] = '\0'; // Null-terminate the string

  // Extract assignment
  const char *assignmentStart = strchr(line, '=')+1;
  const char *assignmentEnd = strchr(line, ';');
  size_t len = 0;
  if (assignmentStart && assignmentEnd && assignmentStart < assignmentEnd) {
    len = assignmentEnd - assignmentStart - 1;
  }

  bool assignmentType = 0; // 0 is numeric, 1 is alphanumeric
  char assignment[len + 1]; // +1 for null terminator
  if (len > 0) {
    for (size_t i = 0; i < len; i++) {
      assignment[i] = assignmentStart[i + 1];
      if ( assignment[i] == '"' || assignment[i] == '\'' ) { assignmentType = 1; }
    }
  }
  assignment[len] = '\0'; // Null-terminate the string

  // char * assignmentr = variables.replaceVariablesWithValues(assignment);
  //Serial.println(assignmentr);

  if ( assignmentType == 0 ) { // perform numeric assignment
     char* result = evaluator.eval(assignment);

//    double r = evaluator.eval(assignment);
//    char* result = new char[20]; // assuming the maximum length of the result is 20 characters
//    dtostrf(r, 1, 2, result);


    //variables.stripTrailingZeros(result);
    variables.set(target, result);
    free(result);
  } else { // perform alphanumeric assignments
    char * result = concat(assignment);
    //stripUnescapedQuotes(assignment);
    variables.set(target, result);
    free(result);
  }
  //Serial.println(assignmentType);
  //Serial.println(assignment);
  //free(assignmentr);
}

// Function to strip all unescaped single and double quotes from a char array
void stripUnescapedQuotes(char* str) {
    int writeIndex = 0;  // Index to write to
    bool escaped = false;  // Flag to track whether the current character is escaped

    for (int readIndex = 0; str[readIndex] != '\0'; ++readIndex) {
        char current = str[readIndex];

        if (escaped) {
            // Previous character was a backslash
            escaped = false;  // Reset escaped status
            str[writeIndex++] = current;  // Always write the escaped character
        } else if (current == '\\') {
            // Current character is a backslash, possibly escaping the next character
            escaped = true;  // Set escaped flag for the next iteration
            str[writeIndex++] = current;  // Write the backslash to the output
        } else if (current == '\'' || current == '\"') {
            // It's an unescaped quote, skip it
            continue;
        } else {
            // Regular character, just copy
            str[writeIndex++] = current;
        }
    }
    str[writeIndex] = '\0';  // Null-terminate the modified string
}

void setup() {
  Serial.begin(500000);
  Serial.println("Heap: " + String(ESP.getFreeHeap()));
  const char testString[] = "var1 = \"this is a test\" + \" concatenation!\";"
                            "var2 = \"another test!\";"
                            "var2 = 21999;"
                            "var3 = 12.34*54123.5123;"
                            "function(test) { Serial.println(1); fun2() { test(); } }"
                            "if ( a > b ) { Serial.print(a); }"
                            "keyword_text ( \"this is some input\", 4, 20 );"
                            "Serial.print (\"This is a test, cool eh?\");"
                            "runMe { test; }"
                            "again(test,(test+test), 123);";
  unsigned long start = micros();
  parse(testString);
  Serial.println(String(micros() - start));
  Serial.println("Heap: " + String(ESP.getFreeHeap()));
  variables.list();
  //const char *t = variables.get("var3");
  std::string t = std::string(variables.get("var3"));
  Serial.println(t.c_str());
}

void loop() {
  // Nothing here
}