#include "ansi_parser.h"

using std::string;
using std::vector;


ANSIParser::ANSIParser()
  : state(STATE_PLAIN), current_parameter(-1)
{}

ANSIParser::~ANSIParser()
{}

void ANSIParser::Parse(const std::string& data)
{
  size_t len = data.length();
  size_t i = 0;
  
  size_t left = (this->state == STATE_PLAIN) ? i : len;
  size_t right = len;
  
  while (i < len) {
    char ch = data.at(i);
    
    switch (this->state) {
      case STATE_PLAIN:
        {
          size_t nextESC = data.find_first_of('\x1B', i);
          if (nextESC == std::string::npos) {
            i = len;
          } else {
            right = i = nextESC;
            this->state = STATE_BEGIN;
          }
        }
        break;
      
      case STATE_BEGIN:
        if (ch == '[') {
          current_parameter = 0;
          this->state = STATE_PARAMETERS;
        } else {
          this->state = STATE_PLAIN;
        }
        break;
      
      case STATE_PARAMETERS:
        if ('0' <= ch && ch <= '9') {
          current_parameter = (current_parameter * 10) + (ch - '0');
        } else {
          this->parameters.push_back(current_parameter);
          current_parameter = -1;
          
          this->state = STATE_PARAMETER_END;
          continue; // skip the i += 1
        }
        break;
      
      case STATE_PARAMETER_END:
        if (ch == ';') {
          current_parameter = 0;
          this->state = STATE_PARAMETERS;
        } else {
          this->state = STATE_INTERMEDIATE;
          continue; // skip the i += 1
        }
        break;
      
      case STATE_INTERMEDIATE:
        if (' ' <= ch && ch <= '/') {
          this->intermediate.append(1, ch);
        } else {
          this->state = STATE_END;
          continue; // skip the i += 1
        }
        break;
      
      case STATE_END:
        if ('@' <= ch && ch <= '~') {
          if (left < right) {
            this->OnText(data.substr(left, right-left));
          }
          this->OnCommand(ch, this->parameters, this->intermediate);
          left = i + 1;
        }
        
        this->state = STATE_PLAIN;
        this->Reset();
        
        right = len;
        break;
    }
    
    i += 1;
  }
  
  if (left < right) {
    this->OnText(data.substr(left, right-left));
  }
}

void ANSIParser::Reset()
{
  this->current_parameter = -1;
  this->parameters.clear();
  this->intermediate.clear();
}