#include "json.h"
#include <iomanip>
#include <sstream>
#include <iostream>
#include <cmath>
#include <cfloat>
#include <set>
#include <cstdlib>
#include <math.h>
#include <stdlib.h>

namespace json
{

Value Value::null;
//constructors
Value::Value()
{
    type = Type::nullValue;
    value.objectValue = NULL;
}

Value::Value(Type type)
{
    this->type = type;
    if (type == Type::stringValue)
        value.stringValue = new std::string();
    if (type == Type::arrayValue)
        value.arrayValue = new std::vector<Value>();
    if (type == Type::objectValue)
        value.objectValue = new std::map<std::string, Value>();
}

Value::Value(int value)
{
    type = Type::intValue;
    this->value.intValue = value;
}

Value::Value(float value)
{
    type = Type::floatValue;
    this->value.floatValue = value;
}

Value::Value(bool value)
{
    type = Type::boolValue;
    this->value.boolValue = value;
}

Value::Value(const std::string &value)
{
    type = Type::stringValue;
    this->value.stringValue = new std::string();
    this->value.stringValue->assign(value);
}
Value::Value(const char* value)
{
    type = Type::stringValue;
    this->value.stringValue = new std::string();
    this->value.stringValue->assign(value);
}

Value::Value(const Value& other) : Value(other.type)
{
    if (type == Type::objectValue)
        *this->value.objectValue = *other.value.objectValue;
    else if (type == Type::arrayValue)
        *this->value.arrayValue = *other.value.arrayValue;
    else if (type == Type::stringValue)
        this->value.stringValue->assign(*other.value.stringValue);
    else
        this->value = other.value;
}

void Value::operator=(const Value& other)
{
    if (type != other.type)
    {
        if (type == Type::stringValue)
            delete value.stringValue;
        if (type == Type::arrayValue)
            delete value.arrayValue;
        if (type == Type::objectValue)
            delete value.objectValue;
        this->type = other.type;
        if (type == Type::stringValue)
            value.stringValue = new std::string();
        if (type == Type::arrayValue)
            value.arrayValue = new std::vector<Value>();
        if (type == Type::objectValue)
            value.objectValue = new std::map<std::string, Value>();
    }

    if (type == Type::objectValue)
        *this->value.objectValue = *other.value.objectValue;
    else if (type == Type::arrayValue)
        *this->value.arrayValue = *other.value.arrayValue;
    else if (type == Type::stringValue)
        this->value.stringValue->assign(*other.value.stringValue);
    else
        this->value = other.value;
}

Value::~Value()
{
    if (type == Type::stringValue)
        delete value.stringValue;
    else if (type == Type::arrayValue)
        delete value.arrayValue;
    else if (type == Type::objectValue)
        delete value.objectValue;
}

size_t Value::size() const
{
    assert(type == Type::arrayValue || type == Type::objectValue);
    if (type == Type::arrayValue)
        return value.arrayValue->size();
    else if (type == Type::objectValue)
        return value.objectValue->size();
    throw "Unsupported";
}

void Value::push_back(const Value& value)
{
    assert(type == Type::arrayValue || type == Type::nullValue);
    if (type == Type::nullValue)
    {
        type = Type::arrayValue;
        this->value.arrayValue = new std::vector<Value>();
    }
    this->value.arrayValue->push_back(value);
}


Value& Value::operator[](const std::string &key)
{
    assert(type == Type::objectValue);
    return (*value.objectValue)[key];
}
Value& Value::operator[](const std::string &key) const
{
    assert(type == Type::objectValue);
    return (*value.objectValue)[key];
}

Value& Value::operator[](const char* key)
{
    assert(type == Type::objectValue || type == Type::nullValue);
    if (type == Type::nullValue)
    {
        type = Type::objectValue;
        value.objectValue = new std::map<std::string, Value>();
    }
    return (*value.objectValue)[std::string(key)];
}

Value& Value::operator[](const char* key) const
{
    assert(type == Type::objectValue);
    return (*value.objectValue)[std::string(key)];
}

Value& Value::operator[](size_t index)
{
    assert(type == Type::arrayValue);
    return (*value.arrayValue)[index];
}
Value& Value::operator[](size_t index) const
{
    assert(type == Type::arrayValue);
    return (*value.arrayValue)[index];
}
Value& Value::operator[](int index)
{
    assert(type == Type::arrayValue);
    return (*value.arrayValue)[index];
}
Value& Value::operator[](int index) const
{
    assert(type == Type::arrayValue);
    return (*value.arrayValue)[index];
}

void Value::erase(size_t index)
{
    throw "Cannot cast";
}














Value::Iterator Value::end() const
{
    if (type == Type::objectValue)
        return Iterator(value.objectValue->end());
    else if (type == Type::arrayValue)
        return Iterator(value.arrayValue->end());
    throw "oops";
}

Value::Iterator Value::begin() const
{
    if (type == Type::objectValue)
        return Iterator(value.objectValue->begin());
    else if (type == Type::arrayValue)
        return Iterator(value.arrayValue->begin());
    throw "oops";
}

///iterator stuff

Value Value::Iterator::operator*()
{
    if (type == Type::objectValue)
        return this->objectIterator->second;
    else  if (type == Type::arrayValue)
        return *arrayIterator;
    throw "Oops";
}

bool Value::Iterator::operator!=(const Iterator &other)
{
    if (type == Type::objectValue)
        return this->objectIterator != other.objectIterator;
    else  if (type == Type::arrayValue)
        return this->arrayIterator != other.arrayIterator;
    throw "Oops";
}

void Value::Iterator::operator++()
{
    if (type == Type::objectValue)
        this->objectIterator++;
    else  if (type == Type::arrayValue)
        this->arrayIterator++;
}
void Value::Iterator::operator++(int)
{
    if (type == Type::objectValue)
        this->objectIterator++;
    else  if (type == Type::arrayValue)
        this->arrayIterator++;
}

Value::Iterator::Iterator(const std::vector<Value>::iterator& arrayIterator)
{
    type = Type::arrayValue;
    this->arrayIterator = arrayIterator;
}

Value::Iterator::Iterator(const std::map<std::string, Value>::iterator& objectIterator)
{
    type = Type::objectValue;
    this->objectIterator = objectIterator;
}

std::string Value::Iterator::key()
{
    assert(type == Type::objectValue);
    return this->objectIterator->first;
}
Value& Value::Iterator::value()
{
    assert(type == Type::objectValue);
    return this->objectIterator->second;
}







static void ltrim(std::istream& stream);
static void eatComment(std::istream& stream);
static Value eatString(std::istream& stream);
static Value eatObject(std::istream& stream);
static Value eatArray(std::istream& stream);
static Value eatNumeric(std::istream& stream, char firstChar);
static Value eatBool(std::istream& stream);
static Value eatNull(std::istream& stream);
static Value eatValue(std::istream& stream);

//reading
static void ltrim(std::istream& stream)
{
    char c = stream.peek();
    while (c == ' ' || c == '\t' || c == '\n' || c == '\r')
    {
        stream.get();
        c = stream.peek();
    }
};


static Value eatString(std::istream& stream)
{
    std::string value = "";
    bool escaped = false;
    while (!stream.eof())
    {
        char c = stream.get();
        if (c == '\\' && !escaped)
            escaped = !escaped;
        else if (c == '\"' && !escaped)
            return Value(value);
        else
        {
            value += c;
            escaped = false;
        }
    }
    return Value(value);
};




static Value eatObject(std::istream& stream)
{
    Value obj(Type::objectValue);
    while (!stream.eof())
    {
        ltrim(stream);
        char token = stream.get();
        if (token == '}')
            break; //empty object
        if (token == '/')
        {
            eatComment(stream);
            token = stream.get();
        }

        assert(token == '"');
        Value key = eatString(stream);
        ltrim(stream);
        token = stream.get();
        assert(token == ':');
        ltrim(stream);
        Value val = eatValue(stream);
        obj[key.asString()] = val;
        ltrim(stream);

        token = stream.get();
        if (token == '}')
            break;
        assert(token == ',');
    }
    return obj;
};
static Value eatArray(std::istream& stream)
{
    Value obj(Type::arrayValue);
    while (!stream.eof())
    {
        ltrim(stream);
        if (stream.peek() == ']')
        {
            stream.get();
            break;
        }
        obj.push_back(eatValue(stream));
        ltrim(stream);
        char token = stream.get();
        if (token == '/')
        {
            eatComment(stream);
            token = stream.get();
        }
        if (token == ']')
            break;
        assert(token == ',');
    }
    return obj;
};
static Value eatNumeric(std::istream& stream, char firstChar)
{
    std::string numeric(1, firstChar);
    while (!stream.eof())
    {
        char token = stream.peek();
        if ((token >= '0' && token <= '9') || token == '.' || token == '-')
            numeric += stream.get();
        else
            break;
    }
    if (numeric.find('.') == std::string::npos)
        return Value(atoi(numeric.c_str()));
    else
        return Value((float)atof(numeric.c_str()));
};

static Value eatBool(std::istream& stream)
{
    char token = stream.get();
    if (token == 'a') //fAlse
    {
        stream.get(); //l
        stream.get(); //s
        stream.get(); //e
        return false;
    }
    else if (token == 'r') //tRue
    {
        stream.get(); // u
        stream.get(); // e
        return true;
    }
    return Value(Type::nullValue);
};
static Value eatNull(std::istream& stream)
{
    return Value(Type::nullValue);
};

//precondition: / is already eaten
static void eatComment(std::istream& stream)
{
    char token = stream.get();
    assert(token == '/' || token == '*');
    if (token == '*')
    {
        char last = token;
        while ((last != '*' || token != '/') && !stream.eof())
        {
            last = token;
            token = stream.get();
        }
    }
    else if (token == '/')
        while (token != '\n' && !stream.eof())
            token = stream.get();
    ltrim(stream);
}


static Value eatValue(std::istream& stream)
{
    ltrim(stream);
    char token = stream.get();
    if (token == '{')
        return eatObject(stream);
    if (token == '[')
        return eatArray(stream);
    if ((token >= '0' && token <= '9') || token == '.' || token == '-')
        return eatNumeric(stream, token);
    if (token == '"')
        return eatString(stream);
    if (token == 't' || token == 'f')
        return eatBool(stream);
    if (token == 'n')
        return eatNull(stream);
    if (token == '/')
    {
        eatComment(stream);
        return eatValue(stream);
    }
    throw "Unable to parse json";
};


Value readJson(const std::string &data)
{
    std::stringstream stream;
    stream << data;
    printf("%s", "test");

    return eatValue(stream);
}

Value readJson(std::istream &stream)
{
    return eatValue(stream);
}



std::ostream& indent(std::ostream& stream, int level)
{
    for (int i = 0; i < level; i++)
        stream << '\t';
    return stream;
}

std::ostream& Value::prettyPrint(std::ostream& stream, json::Value& printConfig, int level) const
{
    stream << std::fixed << std::setprecision(6);
    switch (type)
    {
        case Type::intValue:
            stream << value.intValue;
            break;
        case Type::floatValue:
            assert(!isnan(value.floatValue));
            //assert(isnormal(value.floatValue));
            if (value.floatValue >= 0)
                stream << " ";
            stream << value.floatValue;
            break;
        case Type::boolValue:
            stream << (value.boolValue ? "true" : "false");
            break;
        case Type::stringValue:
            stream << "\"" << *value.stringValue << "\""; //TODO: escape \'s
            break;
        case Type::arrayValue:
        {
            stream << "[";
            int wrap = 99999;
            if (value.arrayValue->size() > 10)
                wrap = 1;
            if (value.arrayValue->at(0).isArray() || value.arrayValue->at(0).isObject())
                wrap = 1;
            else
                wrap = 3;

            std::string seperator = " ";

            if (!printConfig.isNull() && printConfig.isMember("wrap"))
                wrap = printConfig["wrap"];
            if (!printConfig.isNull() && printConfig.isMember("seperator"))
                seperator = printConfig["seperator"].asString();


            int index = 0;

            if ((long)size() > wrap)
            {
                stream << "\n";
                indent(stream, level + 1);
            }
            for (auto v : *this)
            {
                if (index > 0)
                {
                    stream << "," << seperator;
                    if (index % wrap == 0)
                    {
                        stream << "\n";
                        indent(stream, level + 1);
                    }
                }

                json::Value childPrintConfig = json::Value::null;
                if (!printConfig.isNull())
                {
                    if (printConfig.isMember("elements"))
                        childPrintConfig = printConfig["elements"];
                    else if (printConfig.isMember("recursive") && printConfig["recursive"].asBool() == true)
                        childPrintConfig = printConfig;
                }

                v.prettyPrint(stream, childPrintConfig, level + 1);
                index++;
            }
            if ((long)size() > wrap)
            {
                stream << "\n";
                indent(stream, level);
            }
            stream << "]";
            break;
        }
        case Type::objectValue:
        {
            stream << "{\n";
            int wrap = 99999;
            if (value.arrayValue->size() > 10)
                wrap = 1;
            if (value.arrayValue->at(0).isArray() || value.arrayValue->at(0).isObject())
                wrap = 1;
            else
                wrap = 3;

            std::string seperator = " ";

            if (!printConfig.isNull() && printConfig.isMember("wrap"))
                wrap = printConfig["wrap"];
            if (!printConfig.isNull() && printConfig.isMember("seperator"))
                seperator = printConfig["seperator"].asString();


            int index = 0;
            indent(stream, level + 1);



            //for (auto v : *value.objectValue)
            auto printEl = [&](const std::pair<const std::string&, const Value&> v)
            {
                if (index > 0)
                {
                    stream << "," << seperator;
                    if (index % wrap == 0)
                    {
                        stream << "\n";
                        indent(stream, level + 1);
                    }
                }
                stream << "\"" << v.first << "\" : ";
                if (
                        (v.second.isArray() || v.second.isObject()) &&
                        (printConfig.isNull() ||
                         (
                                 printConfig.isMember(v.first) &&
                                 printConfig[v.first].isObject() &&
                                 printConfig[v.first].isMember("wrap") &&
                                 printConfig[v.first]["wrap"].asInt() < (int)v.second.size())
                        )
                        )
                {
                    stream << "\n";
                    indent(stream, level + 1);
                }

                json::Value childPrintConfig = json::Value::null;
                if (!printConfig.isNull())
                {
                    if (printConfig.isMember(v.first))
                        childPrintConfig = printConfig[v.first];
                    else if (printConfig.isMember("recursive") && printConfig["recursive"].asBool() == true)
                        childPrintConfig = printConfig;
                }

                v.second.prettyPrint(stream, childPrintConfig, level + 1);;
                index++;
            };



            std::set<std::string> printed;
            if (!printConfig.isNull())
            {
                if (printConfig.isMember("sort"))
                {
                    for (std::string el : printConfig["sort"])
                    {
                        if (isMember(el))
                        {
                            printEl(std::pair<const std::string&, const Value&>(el, (*value.objectValue)[el]));
                            printed.insert(el);
                        }
                    }
                }
            }

            for (auto v : *value.objectValue)
                if (printed.find(v.first) == printed.end())
                    printEl(v);

            stream << "\n";
            indent(stream, level);
            stream << "}";
            break;
        }
        case Type::nullValue:
            stream << "null";
            break;
    }
    return stream;
}


std::ostream & operator<<(std::ostream &stream, const Value& value)
{
    stream << std::fixed<< std::setprecision(6);
    switch (value.type)
    {
        case Type::intValue:
            stream << value.value.intValue;
            break;
        case Type::floatValue:
            assert(!isnan(value.value.floatValue));
            //assert(isnormal(value.value.floatValue));
            stream << value.value.floatValue;
            break;
        case Type::boolValue:
            stream << (value.value.boolValue ? "true" : "false");
            break;
        case Type::stringValue:
            stream << "\"" << *value.value.stringValue << "\""; //TODO: escape \'s
            break;
        case Type::arrayValue:
        {
            stream << "[";
            bool first = true;
            for (auto v : value)
            {
                if (!first)
                    stream << ", ";
                stream << v;
                first = false;
            }
            stream << "]";
            break;
        }
        case Type::objectValue:
        {
            stream << "{";
            bool first = true;
            for (auto v : *value.value.objectValue)
            {
                if (!first)
                    stream << ", ";
                stream << "\"" << v.first << "\" : " << v.second << std::endl;
                first = false;
            }
            stream << "}";
            break;
        }
        case Type::nullValue:
            stream << "null";
            break;
    }
    return stream;
}
}