#define MAX_LINE 200
#define GCODE_LINE_END ';'

#define CONTEXT_NAME_INI 0
#define CONTEXT_NAME_END 1
#define CONTEXT_COMMENT_INI 2
#define CONTEXT_COMMENT_END 3
#define CONTEXT_VALUE_INI 4
#define CONTEXT_VALUE_END 5
#define CONTEXT_LINE_INI 6
#define CONTEXT_LINE_END 7
#define CONTEXT_ERROR 8

#define CT_NAME 0
#define CT_VALUE 1
#define CT_COMMENT 2
#define CT_LINE_END 3

class PROGRAM
{
  public:

    PROGRAM();
    void eat(char un_char);
    bool is_number_like( char un_char);
    bool is_command( char un_char);
    bool trigger_num_to_letter(char last, char un_char);
    bool trigger_letter_to_num(char last, char un_char);


    char line_buffer[MAX_LINE];
    char command_buffer[MAX_LINE];
    void process_command(char * the_command);
    void do_context_change(int new_context);
    void do_context_publish();

    void do_context_end();
    void do_name_end();
    void do_value_end();
    void do_line_end();

    void do_comment_end();

    void do_command_end();

    char buffer[MAX_LINE];
    void buffer_reset();
    void buffer_append(char c);
    int buffer_pivot;

    char last_char;
    int pivot;
    int pivot_command;
    int index;
    int context ;
}  ;

PROGRAM::PROGRAM()
{
  context = CT_NAME;
  pivot = 0;
  pivot_command = 0 ;
  last_char = '\0';
  index = 0;
}


void PROGRAM::eat(char un_char)
{

  // Serial.print(un_char);
  //  else  if (last_char == GCODE_LINE_END )
  //   {
  //    Serial.println("");
  //   }

  if (un_char == GCODE_LINE_END )
  {
    do_context_publish();
    do_context_change(CT_LINE_END);
    do_context_publish();
    do_context_change(CT_NAME);
  } else  if (un_char == '(' )
  {
    do_context_publish();
    do_context_change(CT_COMMENT);
    buffer_append(un_char);
  } else  if (un_char == ')' )
  {
    buffer_append(un_char);
    do_context_publish();
    do_context_change(CT_NAME);
  } else if (context == CT_NAME && !is_number_like(last_char) && is_number_like(un_char))
  {
    do_context_publish();
    do_context_change(CT_VALUE);
    buffer_append(un_char);
  } else if (context == CT_VALUE && is_number_like(last_char) && !is_number_like(un_char))
  {
    do_context_publish();
    do_context_change(CT_NAME);
    buffer_append(un_char);
  } else {
    buffer_append(un_char);
  }

  // Serial.print(un_char);
  last_char = un_char;
}

bool PROGRAM::is_number_like(char un_char)
{
  bool resu = false;
  resu = resu || (un_char == '0') ;
  resu = resu || (un_char == '1') ;
  resu = resu || (un_char == '2') ;
  resu = resu || (un_char == '3') ;
  resu = resu || (un_char == '4') ;
  resu = resu || (un_char == '5') ;
  resu = resu || (un_char == '6') ;
  resu = resu || (un_char == '7') ;
  resu = resu || (un_char == '8') ;
  resu = resu || (un_char == '9') ;
  resu = resu || (un_char == '.') ;
  return resu;
}
bool PROGRAM::is_command(char un_char)
{
  bool resu = false;
  resu = resu || (un_char == 'G') ;
  resu = resu || (un_char == 'X') ;
  resu = resu || (un_char == 'Y') ;
  resu = resu || (un_char == 'Z') ;
  resu = resu || (un_char == 'A') ;
  resu = resu || (un_char == 'M') ;
  resu = resu || (un_char == 'F') ;
  resu = resu || (un_char == 'R') ;
  resu = resu || (un_char == 'P') ;
  resu = resu || (un_char == 'Q') ;
  resu = resu || (un_char == 'U') ;
  resu = resu || (un_char == 'W') ;
  resu = resu || (un_char == 'N') ;
  resu = resu || (un_char == 'S') ;
  resu = resu || (un_char == 'O') ;
  resu = resu || (un_char == 'T') ;
  return resu;
}
bool PROGRAM::trigger_num_to_letter(char last, char un_char)
{
  // return !(un_char == GCODE_LINE_END) && is_number_like(last) && ! is_number_like(un_char);
  return  is_number_like(last) && ! is_number_like(un_char);
}
bool PROGRAM::trigger_letter_to_num(char last, char un_char)
{
  return !(un_char == GCODE_LINE_END) && !is_number_like(last) &&  is_number_like(un_char);
}
void PROGRAM::process_command(char  * the_command)
{

  Serial.print("++");
  Serial.print(the_command);
  Serial.print("++");

  char name_buff[MAX_LINE];
  char value_buff[MAX_LINE];
  int name_pivot = 0;
  int value_pivot = 0;
  char last_cmd_char = '\0';
  char cmd_state = 'W';

  for (int i = 0; i < strlen(the_command); i++ ) {
    char un_char = the_command[i];

    bool trigger = trigger_letter_to_num(last_cmd_char, un_char );
    /*
        Serial.print("c: ");
        Serial.print(un_char);
        Serial.print(" t:");
        Serial.print(trigger);
        Serial.print(" s:");
        Serial.println(cmd_state); */


    if (cmd_state == 'W' && !trigger)
    {
      name_buff[name_pivot] = un_char;
      name_buff[name_pivot + 1] = '\0';
      name_pivot ++;
    } else if (cmd_state == 'W' && trigger)
    {
      Serial.print("CMD:");
      Serial.print(name_buff);
      cmd_state = 'V';
      value_buff[value_pivot] = un_char;
      value_buff[value_pivot + 1] = '\0';
      value_pivot ++;
    } else if (cmd_state == 'V')
    {
      value_buff[value_pivot] = un_char;
      value_buff[value_pivot + 1] = '\0';
      value_pivot ++;
      // tone(8, 262, 50);
      // delay(50);
      // noTone(50);
      // delay(50);
    }

    last_cmd_char = un_char;
  }

  Serial.print(" Val:");
  Serial.println(value_buff);
  // return is_number_like(last_char) && ! is_number_like(un_char);
}

void PROGRAM::do_context_change(int new_context)
{
  // do_context_publish();
  // Serial.print("|");
  context = new_context ;
  // Serial.print(buffer);
  // buffer_reset();
}
void PROGRAM::do_context_publish()
{
  if (context == CT_LINE_END )
  {
    do_line_end();
  } else if (context == CT_NAME)
  {
    do_name_end();

  } else if (context == CT_VALUE)
  {
    do_value_end();
  } else if (context == CT_COMMENT)
  {
    do_comment_end();
  }

  // Serial.print(buffer);
  buffer_reset();
}



void PROGRAM::do_context_end()
{


  if  (context == CONTEXT_COMMENT_INI)
  {
    do_comment_end();
  } else if (context == CONTEXT_COMMENT_END)
  {
    do_comment_end();

  } else if (context == CONTEXT_NAME_INI)
  {
    do_name_end();

  }
  else if (context == CONTEXT_NAME_END)
  {
    do_name_end();

  }
  else if (context == CONTEXT_VALUE_INI)
  {
    do_value_end();
  }
  else if (context == CONTEXT_VALUE_END)
  {
    do_value_end();

  } else if (context == CONTEXT_LINE_END)
  {
    do_line_end();
  }

  buffer_reset();
}
void PROGRAM::do_line_end()
{
  Serial.println("End line");
}
void PROGRAM::do_name_end()
{
  if ( strlen(buffer) > 0 )
  {
    Serial.print("name:");
    Serial.print(buffer);
    Serial.print(" ");
  }

}
void PROGRAM::do_value_end()
{
  Serial.print("value:");
  Serial.println(buffer);
}
void PROGRAM::do_command_end()
{
  Serial.println("End command");
}
void PROGRAM::do_comment_end()
{
  Serial.print("comment:");
  Serial.println(buffer);
}
void PROGRAM::buffer_reset()
{
  buffer_pivot = 0;
  buffer[buffer_pivot] = '\0';
}
void PROGRAM::buffer_append(char c)
{
  if (buffer_pivot < MAX_LINE)
  {
    if ( is_number_like(c) || is_command(c) ||  context == CT_COMMENT  )
    {
      buffer[buffer_pivot] = c;
      buffer[buffer_pivot + 1] = '\0';
      buffer_pivot++;

    }
  }
}

PROGRAM p;

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

void loop() {
  while (Serial.available() > 0)
  {
    p.eat(Serial.read());
  }

}

/*
T0101  (the simple comment);
G0 X1 Y4;
G0 X2 Y56.7;
N10 G0 X3 Y6;
G71 U1.5 R0.5;
G71 U0.5 W0.5 P10 G20 F 0.18;;
*/