using System.Collections;
using System.Collections.Generic;

using System.Text;



using Godot;

namespace Rokojori
{
  using LexerType = JSONLexerEventType;

  public class JSONParser
  {
    List<JSONData> stack = new List<JSONData>();
      
    JSONArray currentArray = null;
    JSONObject currentObject = null;
    JSONData current = null;
    bool currentIsArray = false;
    JSONData root = null;

    string identifier = null;
    string source = null;
    bool hasError = false;
    JSONObject errorReport = null;
    
    void Reset()
    {
      stack = new List<JSONData>();
        
      currentArray = null;
      currentObject = null;
      current = null;
      currentIsArray = false;
      root = null;

      identifier = null;
      source = null;
    }

    void ProcessJSONData( JSONData jsonData )
    {
      if ( current == null )
      {
        root = jsonData;
      }
      else if ( currentIsArray )
      {
        currentArray.Push( jsonData );              
      }
      else 
      {
        currentObject.Set( identifier, jsonData );
      }
    }

    public static bool SHOW_DEBUG_INFO = false;

    void OnParse( LexerType type, int offset, int length )
    {
      if ( hasError ) { return; }

      if ( SHOW_DEBUG_INFO )
      {
        RJLog.Log(type, offset, length );

        if ( offset < source.Length && length > 0 && ( offset + length ) < source.Length )
        {
          RJLog.Log("'" + source.Substring( offset, length ) + "'" );
        }

      }
      
      switch ( type )
      {
        case LexerType.NUMBER:
        {
          var stringValue = source.Substring( offset, length );
          //double numberValue = 0;
          double numberValue = RegexUtility.ParseDouble( stringValue );
          //System.Double.TryParse( stringValue, out numberValue );
          ProcessJSONData( new JSONValue( numberValue ) );
        }
        break;

        case LexerType.STRING:
        {          
          var stringValue = new StringBuilder();
          JSONStringConverter.Read( stringValue, source, offset, length );
          ProcessJSONData( new JSONValue( stringValue.ToString() ) );
        }
        break;

        case LexerType.IDENTIFIER:
        {
          var stringValue = new StringBuilder();
          JSONStringConverter.Read( stringValue, source, offset, length );
          identifier = stringValue.ToString();
        }
        break;

        case LexerType.NULL:
        case LexerType.TRUE:
        case LexerType.FALSE:
        {
          JSONData jsonData = new JSONValue();
          
          if ( LexerType.NULL != type )
          { jsonData = new JSONValue( LexerType.TRUE == type ); }
         
          ProcessJSONData( jsonData );
        }
        break;

        case LexerType.ARRAY_START:
        case LexerType.OBJECT_START:          
        {
          var isArrayStart = LexerType.ARRAY_START == type;

          JSONData jsonData = null;
          if ( isArrayStart ){ jsonData = new JSONArray(); }
          else { jsonData = new JSONObject(); }

          ProcessJSONData( jsonData );

          current = jsonData;

          if ( isArrayStart )
          {
            currentArray = (JSONArray) jsonData;
            currentObject = null;
          }
          else
          {
            currentObject = (JSONObject) jsonData;
            currentArray = null;
          }

          stack.Add( current );
          currentIsArray = isArrayStart;
        } 
        break;

        case LexerType.ARRAY_END:
        case LexerType.OBJECT_END:
        {
          Lists.PopLast( stack );
          current = Lists.GetLast( stack );
          
          if ( current is JSONArray )
          {
            currentArray = (JSONArray) current; 
            currentObject = null;
            currentIsArray = true;
          }
          else if ( current is JSONObject )
          {
            currentArray = null;
            currentObject = (JSONObject) current;
            currentIsArray = false;
          }

        }
        break;

        case LexerType.ARRAY_SEPERATOR:
        case LexerType.IDENTIFIER_SEPERATOR:
        {

        }
        break;

        case LexerType.DONE_SUCCESS:
        {
            
        }
        break;


        // ERRORS:
        default:
        {
          hasError = true;
          CreateErrorReport( type, offset, length );
        }
        break;
      }
    }

    void CreateErrorReport( LexerType type, int offset, int length )
    {
      var linesMapper = new TextLinesMapper(); 
      linesMapper.Map( source );
      errorReport = new JSONObject();
      
      var end = offset + Mathf.Max( length, 0 );  
      var linesInfo = linesMapper.GetTextEditorInfo( offset, end );
      var snippet = linesMapper.GetTextEditorSnippet( source, offset, end );

      errorReport.Set( "errorType", type + "" );
      errorReport.Set( "linesInfo", linesInfo );
      errorReport.Set( "snippet", snippet );

    }

    public JSONData Parse( string source )
    {
      Reset();

      this.source = source;

      var lexer = new JSONLexer();
      lexer.Lex( source, this.OnParse );

      if ( hasError )
      {
        RJLog.Log( errorReport.Stringify() );
        return null;
      }

      return root;

    }
  }
}