#include "JSON.h"

using namespace Framework;
using namespace JSON;

JSONValue::JSONValue()
{
    this->type = NULL_;
    ref = 1;
}

JSONValue::~JSONValue()
{}

JSONValue::JSONValue( JSONType type )
{
    this->type = type;
    ref = 1;
}

JSONType JSONValue::getType() const
{
    return type;
}

Text JSONValue::toString() const
{
    return Text( "null" );
}

JSONValue *JSONValue::getThis()
{
    ref++;
    return this;
}

JSONValue *JSONValue::release()
{
    if( !--ref )
        delete this;
    return 0;
}


JSONBool::JSONBool( bool b )
    : JSONValue( BOOLEAN )
{
    this->b = b;
}

bool JSONBool::getBool() const
{
    return b;
}

Text JSONBool::toString() const
{
    if( b )
        return Text( "true" );
    else
        return Text( "false" );
}


JSONNumber::JSONNumber( double num )
    : JSONValue( NUMBER )
{
    number = num;
}

double JSONNumber::getNumber() const
{
    return number;
}

Text JSONNumber::toString() const
{
    return Text( number );
}


JSONString::JSONString( Text string )
    : JSONValue( STRING )
{
    this->string = string;
}

Text JSONString::getString() const
{
    return string;
}

Text JSONString::toString() const
{
    return Text( Text( "\"" ) += string.getText() ) += "\"";
}


JSONArray::JSONArray()
    : JSONValue( ARRAY )
{
    array = new RCArray< JSONValue >();
}

JSONArray::JSONArray( Text string )
    : JSONValue( ARRAY )
{
    array = new RCArray< JSONValue >();
    string = Parser::removeWhitespace( string );
    if( string.getText()[ 0 ] == '[' && string.getText()[ string.getLength() - 1 ] == ']' )
    {
        string.remove( 0, 1 );
        string.remove( string.getLength() - 1, string.getLength() );
        while( string.getLength() )
        {
            int end = Parser::findObjectEndInArray( string );
            Text *objStr = string.getTeilText( 0, end );
            string.remove( 0, end + 1 );
            array->add( Parser::getValue( *objStr ) );
            objStr->release();
        }
    }
}

JSONArray::JSONArray( const JSONArray &arr )
    : JSONValue( ARRAY )
{
    array = arr.array->getThis();
}

JSONArray::~JSONArray()
{
    array->release();
}

JSONArray &JSONArray::operator=( const JSONArray &arr )
{
    array->release();
    array = arr.array->getThis();
    return *this;
}

void JSONArray::addValue( JSONValue *value )
{
    array->add( value );
}

JSONValue *JSONArray::getValue( int i ) const
{
    return array->get( i );
}

int JSONArray::getLength() const
{
    return array->getEintragAnzahl();
}

Text JSONArray::toString() const
{
    Text str = "[";
    for( auto i = array->getIterator(); i; i++ )
    {
        str += i->toString();
        if( i.hasNext() )
            str += ",";
    }
    str += "]";
    return str;
}


JSONObject::JSONObject()
    : JSONValue( OBJECT )
{
    fields = new Array< Text >();
    values = new RCArray< JSONValue >();
}

JSONObject::JSONObject( Text string )
    : JSONValue( OBJECT )
{
    fields = new Array< Text >();
    values = new RCArray< JSONValue >();
    string = Parser::removeWhitespace( string );
    if( string.getText()[ 0 ] == '{' && string.getText()[ string.getLength() - 1 ] == '}' )
    {
        string.remove( 0, 1 );
        string.remove( string.getLength() - 1, string.getLength() );
        while( string.getLength() )
        {
            int endField = Parser::findFieldEndInObject( string );
            Text *fieldName = string.getTeilText( 0, endField );
            string.remove( 0, endField + 1 );
            fieldName->remove( 0, 1 );
            fieldName->remove( fieldName->getLength() - 1, fieldName->getLength() );
            int endValue = Parser::findValueEndInObject( string );
            Text *value = string.getTeilText( 0, endValue );
            string.remove( 0, endValue + 1 );
            fields->add( Text( fieldName->getText() ) );
            values->add( Parser::getValue( *value ) );
            fieldName->release();
            value->release();
        }
    }
}

JSONObject::JSONObject( const JSONObject &obj )
    : JSONValue( OBJECT )
{
    fields = obj.fields->getThis();
    values = obj.values->getThis();
}

JSONObject::~JSONObject()
{
    fields->release();
    values->release();
}


JSONObject &JSONObject::operator=( const JSONObject &obj )
{
    fields->release();
    values->release();
    fields = obj.fields->getThis();
    values = obj.values->getThis();
    return *this;
}


bool JSONObject::addValue( Text field, JSONValue *value )
{
    if( hasValue( field ) )
        return 0;
    fields->add( field );
    values->add( value );
    return 1;
}

bool JSONObject::removeValue( Text field )
{
    for( int i = 0; i < fields->getEintragAnzahl(); i++ )
    {
        if( fields->get( i ).istGleich( field ) )
        {
            fields->remove( i );
            values->remove( i );
            return 1;
        }
    }
    return 0;
}

bool JSONObject::hasValue( Text field )
{
    for( int i = 0; i < fields->getEintragAnzahl(); i++ )
    {
        if( fields->get( i ).istGleich( field ) )
            return 1;
    }
    return 0;
}

JSONValue *JSONObject::getValue( Text field )
{
    for( int i = 0; i < fields->getEintragAnzahl(); i++ )
    {
        if( fields->get( i ).istGleich( field ) )
            return values->get( i );
    }
    return new JSONValue();
}

Iterator< Text > JSONObject::getFields()
{
    return fields->getIterator();
}

Iterator< JSONValue* > JSONObject::getValues()
{
    return values->getIterator();
}

int JSONObject::getFieldCount() const
{
    return fields->getEintragAnzahl();
}

Text JSONObject::toString() const
{
    Text str = "{";
    Iterator< Text > k = fields->getIterator();
    for( auto v = values->getIterator(); k && v; k++, v++ )
    {
        str += "\"";
        str += k._.getText();
        str += "\":";
        str += v->toString().getText();
        if( v.hasNext() )
            str += ",";
    }
    str += "}";
    return str;
}

int Parser::findObjectEndInArray( const char *str )
{
    return findValueEndInObject( str );
}

Text Parser::removeWhitespace( const char *str )
{
    int wsc = 0;
    int i = 0;
    bool esc = 0;
    bool strO = 0;
    for( ; str[ i ]; i++ )
    {
        switch( str[ i ] )
        {
        case '\\':
            if( strO )
                esc = !esc;
            else
                esc = 0;
            break;
        case '"':
            if( !esc )
                strO = !strO;
            esc = 0;
            break;
        case ' ':
        case '\n':
        case '\t':
        case '\r':
            if( !strO )
                wsc++;
            esc = 0;
            break;
        default:
            esc = 0;
            break;
        }
    }
    Text ret;
    ret.fillText( ' ', i - wsc );
    i = 0;
    esc = 0;
    strO = 0;
    int index = 0;
    for( ; str[ i ]; i++ )
    {
        switch( str[ i ] )
        {
        case '\\':
            if( strO )
                esc = !esc;
            else
                esc = 0;
            ret.getText()[ index++ ] = str[ i ];
            break;
        case '"':
            if( !esc )
                strO = !strO;
            esc = 0;
            ret.getText()[ index++ ] = str[ i ];
            break;
        case ' ':
        case '\n':
        case '\t':
        case '\r':
            if( strO )
                ret.getText()[ index++ ] = str[ i ];
            esc = 0;
            break;
        default:
            ret.getText()[ index++ ] = str[ i ];
            esc = 0;
            break;
        }
    }
    return ret;
}

JSONValue *Parser::getValue( const char *str )
{
    Text string = Parser::removeWhitespace( str );
    if( string.istGleich( "true" ) )
        return new JSONBool( 1 );
    if( string.istGleich( "false" ) )
        return new JSONBool( 0 );
    if( string.getText()[ 0 ] == '"' )
    {
        string.remove( 0, 1 );
        string.remove( string.getLength() - 1, string.getLength() );
        return new JSONString( string );
    }
    if( string.getText()[ 0 ] == '[' )
        return new JSONArray( string );
    if( string.getText()[ 0 ] == '{' )
        return new JSONObject( string );
    if( Text( (double)string ).istGleich( string.getText() ) )
        return new JSONNumber( string );
    return new JSONValue();
}

int Parser::findFieldEndInObject( const char *str )
{
    int i = 0;
    bool esc = 0;
    bool strO = 0;
    int objOc = 0;
    int arrayOc = 0;
    for( ; str[ i ]; i++ )
    {
        switch( str[ i ] )
        {
        case '\\':
            if( strO )
                esc = !esc;
            else
                esc = 0;
            break;
        case '"':
            if( !esc )
                strO = !strO;
            esc = 0;
            break;
        case '[':
            if( !strO )
                arrayOc++;
            esc = 0;
            break;
        case ']':
            if( !strO )
                arrayOc--;
            esc = 0;
            break;
        case '{':
            if( !strO )
                objOc++;
            esc = 0;
            break;
        case '}':
            if( !strO )
                objOc--;
            esc = 0;
            break;
        case ':':
            if( !strO && objOc == 0 && arrayOc == 0 )
                return i;
            esc = 0;
            break;
        default:
            esc = 0;
            break;
        }
    }
    return i;
}

int Parser::findValueEndInObject( const char *str )
{
    int i = 0;
    bool esc = 0;
    bool strO = 0;
    int objOc = 0;
    int arrayOc = 0;
    for( ; str[ i ]; i++ )
    {
        switch( str[ i ] )
        {
        case '\\':
            if( strO )
                esc = !esc;
            else
                esc = 0;
            break;
        case '"':
            if( !esc )
                strO = !strO;
            esc = 0;
            break;
        case '[':
            if( !strO )
                arrayOc++;
            esc = 0;
            break;
        case ']':
            if( !strO )
                arrayOc--;
            esc = 0;
            break;
        case '{':
            if( !strO )
                objOc++;
            esc = 0;
            break;
        case '}':
            if( !strO )
                objOc--;
            esc = 0;
            break;
        case ',':
            if( !strO && objOc == 0 && arrayOc == 0 )
                return i;
            esc = 0;
            break;
        default:
            esc = 0;
            break;
        }
    }
    return i;
}