/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef JSON_OBJECT_H_

#define JSON_OBJECT_H_

#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AString.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>

namespace android {

struct JSONArray;
struct JSONCompound;
struct JSONObject;

struct JSONValue {
    enum FieldType {
        TYPE_STRING,
        TYPE_INT32,
        TYPE_FLOAT,
        TYPE_BOOLEAN,
        TYPE_NULL,
        TYPE_OBJECT,
        TYPE_ARRAY,
    };

    // Returns the number of bytes consumed or an error.
    static ssize_t Parse(const char *data, size_t size, JSONValue *out);

    JSONValue();
    JSONValue(const JSONValue &);
    JSONValue &operator=(const JSONValue &);
    ~JSONValue();

    FieldType type() const;
    bool getInt32(int32_t *value) const;
    bool getFloat(float *value) const;
    bool getString(AString *value) const;
    bool getBoolean(bool *value) const;
    bool getObject(sp<JSONObject> *value) const;
    bool getArray(sp<JSONArray> *value) const;

    void setInt32(int32_t value);
    void setFloat(float value);
    void setString(const AString &value);
    void setBoolean(bool value);
    void setObject(const sp<JSONObject> &obj);
    void setArray(const sp<JSONArray> &array);
    void unset();  // i.e. setNull()

    AString toString(size_t depth = 0, bool indentFirstLine = true) const;

private:
    FieldType mType;

    union {
        int32_t mInt32;
        float mFloat;
        AString *mString;
        bool mBoolean;
        JSONCompound *mObjectOrArray;
    } mValue;
};

struct JSONCompound : public RefBase {
    static sp<JSONCompound> Parse(const char *data, size_t size);

    AString toString(size_t depth = 0, bool indentFirstLine = true) const;

    virtual bool isObject() const = 0;

protected:
    virtual ~JSONCompound() {}

    virtual AString internalToString(size_t depth) const = 0;

    JSONCompound() {}

private:
    friend struct JSONValue;

    DISALLOW_EVIL_CONSTRUCTORS(JSONCompound);
};

template<class KEY>
struct JSONBase : public JSONCompound {
    JSONBase() {}

#define PREAMBLE()                              \
    JSONValue value;                            \
    if (!getValue(key, &value)) {               \
        return false;                           \
    }

    bool getFieldType(KEY key, JSONValue::FieldType *type) const {
        PREAMBLE()
        *type = value.type();
        return true;
    }

    bool getInt32(KEY key, int32_t *out) const {
        PREAMBLE()
        return value.getInt32(out);
    }

    bool getFloat(KEY key, float *out) const {
        PREAMBLE()
        return value.getFloat(out);
    }

    bool getString(KEY key, AString *out) const {
        PREAMBLE()
        return value.getString(out);
    }

    bool getBoolean(KEY key, bool *out) const {
        PREAMBLE()
        return value.getBoolean(out);
    }

    bool getObject(KEY key, sp<JSONObject> *obj) const {
        PREAMBLE()
        return value.getObject(obj);
    }

    bool getArray(KEY key, sp<JSONArray> *obj) const {
        PREAMBLE()
        return value.getArray(obj);
    }

#undef PREAMBLE

protected:
    virtual ~JSONBase() {}

    virtual bool getValue(KEY key, JSONValue *value) const = 0;

private:
    DISALLOW_EVIL_CONSTRUCTORS(JSONBase);
};

struct JSONObject : public JSONBase<const char *> {
    JSONObject();

    virtual bool isObject() const;
    void setValue(const char *key, const JSONValue &value);

    void setInt32(const char *key, int32_t in) {
        JSONValue val;
        val.setInt32(in);
        setValue(key, val);
    }

    void setFloat(const char *key, float in) {
        JSONValue val;
        val.setFloat(in);
        setValue(key, val);
    }

    void setString(const char *key, AString in) {
        JSONValue val;
        val.setString(in);
        setValue(key, val);
    }

    void setBoolean(const char *key, bool in) {
        JSONValue val;
        val.setBoolean(in);
        setValue(key, val);
    }

    void setObject(const char *key, const sp<JSONObject> &obj) {
        JSONValue val;
        val.setObject(obj);
        setValue(key, val);
    }

    void setArray(const char *key, const sp<JSONArray> &obj) {
        JSONValue val;
        val.setArray(obj);
        setValue(key, val);
    }

protected:
    virtual ~JSONObject();

    virtual bool getValue(const char *key, JSONValue *value) const;
    virtual AString internalToString(size_t depth) const;

private:
    KeyedVector<AString, JSONValue> mValues;

    DISALLOW_EVIL_CONSTRUCTORS(JSONObject);
};

struct JSONArray : public JSONBase<size_t> {
    JSONArray();

    virtual bool isObject() const;
    size_t size() const;
    void addValue(const JSONValue &value);

    void addInt32(int32_t in) {
        JSONValue val;
        val.setInt32(in);
        addValue(val);
    }

    void addFloat(float in) {
        JSONValue val;
        val.setFloat(in);
        addValue(val);
    }

    void addString(AString in) {
        JSONValue val;
        val.setString(in);
        addValue(val);
    }

    void addBoolean(bool in) {
        JSONValue val;
        val.setBoolean(in);
        addValue(val);
    }

    void addObject(const sp<JSONObject> &obj) {
        JSONValue val;
        val.setObject(obj);
        addValue(val);
    }

    void addArray(const sp<JSONArray> &obj) {
        JSONValue val;
        val.setArray(obj);
        addValue(val);
    }

protected:
    virtual ~JSONArray();

    virtual bool getValue(size_t key, JSONValue *value) const;
    virtual AString internalToString(size_t depth) const;


private:
    Vector<JSONValue> mValues;

    DISALLOW_EVIL_CONSTRUCTORS(JSONArray);
};

}  // namespace android

#endif  // JSON_OBJECT_H_