#ifndef BU_JSON_H
#define BU_JSON_H

#include "bu/hash.h"
#include "bu/list.h"
#include "bu/array.h"
#include "bu/string.h"
#include "bu/utfstring.h"

namespace Bu
{
    class Stream;
    class Formatter;
    typedef Bu::List<Bu::UtfString> UtfStringList;

    class Json
    {
    private:
        class ParseState
        {
        public:
            ParseState( Bu::Stream &sInput ) :
                c( 0 ), sInput( sInput ), iLine( 1 ), iChar( 0 )
            {
            }

            void error( const Bu::String &sTxt );

            Bu::UtfChar c;
            Bu::Stream &sInput;
            int iLine;
            int iChar;
        };
        Json( ParseState &ps );
        typedef Bu::Hash<Bu::UtfString, Json *> JsonHash;
        typedef Bu::Array<Json *> JsonList;

    public:
        typedef JsonList::iterator iterator;
        typedef JsonList::const_iterator const_iterator;
        enum Type
        {
            Invalid,
            Object,
            Array,
            String,
            Number,
            Boolean,
            Null
        };

    public:
        Json();
        Json( const Bu::UtfString &sValue );
        Json( const Bu::String &sValue );
        Json( const char *sValue );
        Json( double dValue );
        Json( bool bValue );
        Json( Type eType );
        Json( Bu::Stream &sInput );
        Json( const Json &rSrc );
        virtual ~Json();

        Type getType() const;
        Bu::UtfString getString() const;
        double getNumber() const;
        bool getBoolean() const;
        bool isNull() const;
        Json &operator[]( const Bu::UtfString &sKey ) const;
        Json &operator[]( int iIndex ) const;
        int getSize() const;
        Bu::UtfStringList getKeys() const;
        iterator begin();
        const_iterator begin() const;
        iterator end();
        const_iterator end() const;

        bool has( const Bu::String &sKey ) const;
        Json &insert( const Bu::String &sKey, Bu::Json *pObj );
        Json &insert( const Bu::String &sKey, const Bu::Json &rObj );
        Json &insert( const Bu::String &sKey, const Bu::String &sValue );
        Json &insert( const Bu::String &sKey, const char *sValue );
        Json &insert( const Bu::String &sKey, double dValue );
        Json &insert( const Bu::String &sKey, bool bValue );
        Json &insertObject( const Bu::String &sKey );
        Json &insertArray( const Bu::String &sKey );
        Json &insertNull( const Bu::String &sKey );
        Json &append( Bu::Json *pObj );
        Json &append( const Bu::String &sValue );
        Json &append( const char *sValue );
        Json &append( double dValue );
        Json &append( bool bValue );
        Json &appendObject();
        Json &appendArray();
        Json &appendNull();

        void parse( Bu::Stream &sInput );
        void parse( const Bu::String &sInput );
        void reset();

        void write( Bu::Stream &sOutput ) const;
        void writeStable( Bu::Stream &sOutput ) const;
        Bu::String toString() const;
        Bu::String toStringStable() const;

        Bu::Json &operator=( const Bu::Json &rSrc );
        bool operator==( const Bu::String &rRhs );

    private:
        void parse( ParseState &ps );
        void parseString( ParseState &ps, Bu::UtfString &sOut );
        void parseString( ParseState &ps );
        void parseObject( ParseState &ps );
        void parseArray( ParseState &ps );
        void parseNumber( ParseState &ps );
        void parseLiteral( ParseState &ps );
        bool readChar( ParseState &ps );
        void readChar( ParseState &ps, const char *sSection );
        bool isWs( Bu::UtfChar c );
        void skipWs( ParseState &ps );
        void writeStr( const Bu::UtfString &sStr, Bu::Stream &sOutput ) const;

    private:
        Type eType;
        union DatUnion
        {
            DatUnion() : pObject( NULL ) { }
            DatUnion( const Bu::String &sValue ) :
                pString( new Bu::UtfString( sValue ) ) { }
            DatUnion( const Bu::UtfString &sValue ) :
                pString( new Bu::UtfString( sValue ) ) { }
            DatUnion( const char *sValue ) :
                pString( new Bu::UtfString( sValue ) ) { }
            DatUnion( double dValue ) : dNumber( dValue ) { }
            DatUnion( bool bValue ) : bBoolean( bValue ) { }
            JsonHash *pObject;
            JsonList *pArray;
            Bu::UtfString *pString;
            double dNumber;
            bool bBoolean;
        } uDat;
    };

    Bu::Formatter &operator<<( Bu::Formatter &f, const Bu::Json &j );
}

#endif
