//===-- JSONGenerator.h ----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef __JSONGenerator_h_
#define __JSONGenerator_h_

// C Includes
// C++ Includes

#include <iomanip>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

//----------------------------------------------------------------------
/// @class JSONGenerator JSONGenerator.h
/// @brief A class which can construct structured data for the sole purpose
/// of printing it in JSON format.
///
/// A stripped down version of lldb's StructuredData objects which are much
/// general purpose.  This variant is intended only for assembling information
/// and printing it as a JSON string.
//----------------------------------------------------------------------

class JSONGenerator
{
public:

    class Object;
    class Array;
    class Integer;
    class Float;
    class Boolean;
    class String;
    class Dictionary;
    class Generic;

    typedef std::shared_ptr<Object> ObjectSP;
    typedef std::shared_ptr<Array> ArraySP;
    typedef std::shared_ptr<Integer> IntegerSP;
    typedef std::shared_ptr<Float> FloatSP;
    typedef std::shared_ptr<Boolean> BooleanSP;
    typedef std::shared_ptr<String> StringSP;
    typedef std::shared_ptr<Dictionary> DictionarySP;
    typedef std::shared_ptr<Generic> GenericSP;

    enum class Type
    {
        eTypeInvalid = -1,
        eTypeNull = 0,
        eTypeGeneric,
        eTypeArray,
        eTypeInteger,
        eTypeFloat,
        eTypeBoolean,
        eTypeString,
        eTypeDictionary
    };

    class Object :
        public std::enable_shared_from_this<Object>
    {
    public:

        Object (Type t = Type::eTypeInvalid) :
            m_type (t)
        {
        }

        virtual ~Object ()
        {
        }

        virtual bool
        IsValid() const
        {
            return true;
        }

        virtual void
        Clear ()
        {
            m_type = Type::eTypeInvalid;
        }

        Type
        GetType () const
        {
            return m_type;
        }

        void
        SetType (Type t)
        {
            m_type = t;
        }

        Array *
        GetAsArray ()
        {
            if (m_type == Type::eTypeArray)
                return (Array *)this;
            return NULL;
        }

        Dictionary *
        GetAsDictionary ()
        {
            if (m_type == Type::eTypeDictionary)
                return (Dictionary *)this;
            return NULL;
        }

        Integer *
        GetAsInteger ()
        {
            if (m_type == Type::eTypeInteger)
                return (Integer *)this;
            return NULL;
        }

        Float *
        GetAsFloat ()
        {
            if (m_type == Type::eTypeFloat)
                return (Float *)this;
            return NULL;
        }

        Boolean *
        GetAsBoolean ()
        {
            if (m_type == Type::eTypeBoolean)
                return (Boolean *)this;
            return NULL;
        }

        String *
        GetAsString ()
        {
            if (m_type == Type::eTypeString)
                return (String *)this;
            return NULL;
        }

        Generic *
        GetAsGeneric()
        {
            if (m_type == Type::eTypeGeneric)
                return (Generic *)this;
            return NULL;
        }

        virtual void
        Dump (std::ostream &s) const = 0; 

    private:
        Type m_type;
    };

    class Array : public Object
    {
    public:
        Array () :
            Object (Type::eTypeArray)
        {
        }

        virtual
        ~Array()
        {
        }

        void
        AddItem(ObjectSP item)
        {
            m_items.push_back(item);
        }

        void Dump(std::ostream &s) const override
        {
            s << "[";
            const size_t arrsize = m_items.size();
            for (size_t i = 0; i < arrsize; ++i)
            {
                m_items[i]->Dump(s);
                if (i + 1 < arrsize)
                    s << ",";
            }
            s << "]";
        }

    protected:
        typedef std::vector<ObjectSP> collection;
        collection m_items;
    };


    class Integer  : public Object
    {
    public:
        Integer (uint64_t value = 0) :
            Object (Type::eTypeInteger),
            m_value (value)
        {
        }

        virtual ~Integer()
        {
        }

        void
        SetValue (uint64_t value)
        {
            m_value = value;
        }

        void Dump(std::ostream &s) const override
        {
            s << m_value;
        }

    protected:
        uint64_t m_value;
    };

    class Float  : public Object
    {
    public:
        Float (double d = 0.0) :
            Object (Type::eTypeFloat),
            m_value (d)
        {
        }

        virtual ~Float()
        {
        }

        void
        SetValue (double value)
        {
            m_value = value;
        }

        void Dump(std::ostream &s) const override
        {
            s << m_value;
        }

    protected:
        double m_value;
    };

    class Boolean  : public Object
    {
    public:
        Boolean (bool b = false) :
            Object (Type::eTypeBoolean),
            m_value (b)
        {
        }

        virtual ~Boolean()
        {
        }

        void
        SetValue (bool value)
        {
            m_value = value;
        }

        void Dump(std::ostream &s) const override
        {
            if (m_value == true)
                s << "true";
            else
                s << "false";
        }

    protected:
        bool m_value;
    };



    class String  : public Object
    {
    public:
        String () :
            Object (Type::eTypeString),
            m_value ()
        {
        }

        String (const std::string &s) :
            Object (Type::eTypeString),
            m_value (s)
        {
        }

        String (const std::string &&s) :
            Object (Type::eTypeString),
            m_value (s)
        {
        }

        void
        SetValue (const std::string &string)
        {
            m_value = string;
        }

        void Dump(std::ostream &s) const override
        {
            std::string quoted;
            const size_t strsize = m_value.size();
            for (size_t i = 0; i < strsize ; ++i)
            {
                char ch = m_value[i];
                if (ch == '"')
                    quoted.push_back ('\\');
                quoted.push_back (ch);
            }
            s << '"' << quoted.c_str() << '"';
        }

    protected:
        std::string m_value;
    };

    class Dictionary : public Object
    {
    public:
        Dictionary () :
            Object (Type::eTypeDictionary),
            m_dict ()
        {
        }

        virtual ~Dictionary()
        {
        }

        void
        AddItem (std::string key, ObjectSP value)
        {
            m_dict.push_back(Pair(key, value));
        }

        void
        AddIntegerItem (std::string key, uint64_t value)
        {
            AddItem (key, ObjectSP (new Integer(value)));
        }

        void
        AddFloatItem (std::string key, double value)
        {
            AddItem (key, ObjectSP (new Float(value)));
        }

        void
        AddStringItem (std::string key, std::string value)
        {
            AddItem (key, ObjectSP (new String(std::move(value))));
        }

        void
        AddBytesAsHexASCIIString (std::string key, const uint8_t *src, size_t src_len)
        {
            if (src && src_len)
            {
                std::ostringstream strm;
                for (size_t i = 0; i < src_len; i++)
                    strm << std::setfill('0') << std::hex << std::right << std::setw(2) << ((uint32_t)(src[i]));
                AddItem (key, ObjectSP (new String(std::move(strm.str()))));
            }
            else
            {
                AddItem (key, ObjectSP (new String()));
            }
        }

        void
        AddBooleanItem (std::string key, bool value)
        {
            AddItem (key, ObjectSP (new Boolean(value)));
        }

        void Dump(std::ostream &s) const override
        {
            bool have_printed_one_elem = false;
            s << "{";
            for (collection::const_iterator iter = m_dict.begin(); iter != m_dict.end(); ++iter)
            {
                if (have_printed_one_elem == false)
                {
                    have_printed_one_elem = true;
                }
                else
                {
                    s << ",";
                }
                s << "\"" << iter->first.c_str() << "\":";
                iter->second->Dump(s);
            }
            s << "}";
        }

    protected:
        // Keep the dictionary as a vector so the dictionary doesn't reorder itself when you dump it
        // We aren't accessing keys by name, so this won't affect performance
        typedef std::pair<std::string, ObjectSP> Pair;
        typedef std::vector<Pair> collection;
        collection m_dict;
    };

    class Null : public Object
    {
    public:
        Null () :
            Object (Type::eTypeNull)
        {
        }

        virtual ~Null()
        {
        }

        bool
        IsValid() const override
        {
            return false;
        }

        void Dump(std::ostream &s) const override
        {
            s << "null";
        }

    protected:
    };

    class Generic : public Object
    {
      public:
        explicit Generic(void *object = nullptr)
            : Object(Type::eTypeGeneric)
            , m_object(object)
        {
        }

        void
        SetValue(void *value)
        {
            m_object = value;
        }

        void *
        GetValue() const
        {
            return m_object;
        }

        bool
        IsValid() const override
        {
            return m_object != nullptr;
        }

        void Dump(std::ostream &s) const override;

      private:
        void *m_object;
    };

};  // class JSONGenerator



#endif  // __JSONGenerator_h_
