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

using System.Text;
using System.Threading.Tasks;



namespace Rokojori
{
  public class JSONStringConverter
  {
    static bool ContainsBackslash( string dataFromFile, int start, int length )
    {
      var end = start + length;
      for ( var i = start; i < end; i++ )
      {
        if ( dataFromFile[ i ] == '\\' ) { return true; }
      }

      return false;
    }

    public static string Read( string dataFromFile )
    {
      var output = new StringBuilder();
      Read( output, dataFromFile, 0, dataFromFile.Length );
      return output.ToString();
    }

    public static void Read( StringBuilder output, string dataFromFile, int start, int length )
    {
      ReadWithoutQuotes( output, dataFromFile, start + 1, length - 2 );
    }

    public static void Read( StringBuilder output, string dataFromFile )
    {
      Read( output, dataFromFile, 0, dataFromFile.Length );
    }

    public static void ReadWithoutQuotes( StringBuilder output, string dataFromFile, int start, int length ) 
    {
      if ( ! JSONStringConverter.ContainsBackslash( dataFromFile, start, length ) )
      {
        output.Append( dataFromFile.Substring( start, length ) );
        return;
      }


      var end = start + length;

      for ( var i = start ; i < end; i++ )
      {
        var rawCharacter = dataFromFile[ i ];

        if ( rawCharacter == '\\' )
        {
          if ( i == end - 1 ) { return; }

          var nextRawCharacter = dataFromFile[ i + 1 ];

          if ( nextRawCharacter == 'u' )
          {
            var hexStringStart = i + 2;            
            var hexString = dataFromFile.Substring( hexStringStart, 4 );
            var unicodeValue = ReadUnicodeFromHexString( hexString );

            output.Append( unicodeValue );

            i += 5;
          }
          else
          {
            var escapedCharacter = ReadSingleEscaped( nextRawCharacter );
            output.Append( escapedCharacter );

            i ++;
          }
          

        }
        else
        {
          output.Append( rawCharacter );
        }
      }

    }

    static char ReadSingleEscaped( char character )
    {
      switch ( character )
      {
        case 'b': return '\b';
        case 'f': return '\f';
        case 'n': return '\n';
        case 'r': return '\r';
        case 't': return '\t';

        default: return character;
      }     
    }

    static char ReadUnicodeFromHexString( string hexString )
    {
      return (char) int.Parse( hexString, System.Globalization.NumberStyles.HexNumber );
    }

    static readonly char[] ESCAPE_CHARACTERS = new char[]
    { 
      '\"', '\\', '/', '\b', '\f', '\n', '\r', '\t' 
    };

    static readonly string[] ESCAPING_SEQUENCES = new string[]
    {      
      "\\\"", "\\\\", "\\/", "\\b", "\\f", "\\n", "\\r", "\\t" 
    }; 

    public static string Write( string dataFromMemory )
    {
      var output = new StringBuilder();
      Write( output, dataFromMemory, 0, dataFromMemory.Length );
      return output.ToString();
    }

    public static void Write( StringBuilder output, string dataFromMemory, int start, int length ) 
    {
      output.Append( "\"" );
      WriteWithoutQuotes( output, dataFromMemory, start, length );
      output.Append( "\"" ); 
    }
    
    public static void Write( StringBuilder output, string dataFromMemory ) 
    {
      Write( output, dataFromMemory, 0, dataFromMemory.Length );
    }


    public static void WriteWithoutQuotes( StringBuilder output, string dataFromMemory, int start, int length  )
    {
      var end = start + length;
      var index = IndexOfEscapeCharacter( dataFromMemory, start, end );

      if ( index == -1 )
      {
        output.Append( dataFromMemory.Substring( start, length ) );
        return;
      }
           

      for ( var i = 0; i < end; i++ )
      {
        var intValue = ( int ) dataFromMemory[ i ];

        if ( intValue > 127 )
        {
          var escapedUnicodeValue = "\\u" + intValue.ToString( "x4" );
          output.Append( escapedUnicodeValue );

          continue;
        }

        var escapeIndex = GetEscapeIndex( dataFromMemory[ i ] );

        if ( escapeIndex != -1 )
        {
          output.Append( ESCAPING_SEQUENCES[ escapeIndex ] ); 
        }
        else
        {
          output.Append( dataFromMemory[ i ] );
        }
      } 

    } 

    static int IndexOfEscapeCharacter( string dataFromMemory, int start, int end )
    {
      for ( var i = start; i < end; i++ )
      {
        var intValue = ( int ) dataFromMemory[ i ];

        if ( intValue > 127 )
        {
          return i;
        }

        var escapeIndex = GetEscapeIndex( dataFromMemory[ i ] );

        if ( escapeIndex != -1 )
        {
          return i;
        }
      } 

      return -1;
    }

    static int GetEscapeIndex( char c )
    {
      for ( int i = 0; i < ESCAPE_CHARACTERS.Length; i++ )
      {
        if ( ESCAPE_CHARACTERS[ i ] == c )
        {
          return i;
        }
      }

      return -1;

    }

    
  }
}