#include <map>
#include <vector>
#include <string>
#include <iostream>
#include <cstring>
#include <utility>
#include <functional>
 
namespace json {
 
class value;
 
typedef std::vector<value> array;
typedef std::map<std::string, value> object;
typedef std::string string;
 
enum class type
{
    null, // it is not realy a type but a value
    boolean,
    integer,
    number,
    string,
    object,
    array
};
 
class value
{
public:
    value();
    ~value();
 
    value(double number);
    value(int integer);
    value(bool b);
    value(char const* str);
    value(json::string const& str);
    value(json::array const& arr);
    value(json::object const& obj);
 
    value(json::string&& str);
    value(json::array&& arr);
    value(json::object&& obj);
 
    value(value const& val);
    value(value&& val);
 
    // assignment operators
    value& operator=(double number);
    value& operator=(int integer);
    value& operator=(bool b);
    value& operator=(json::string const& str);
    value& operator=(json::array const& arr);
    value& operator=(json::object const& obj);
 
    value& operator=(json::string&& str);
    value& operator=(json::array&& arr);
    value& operator=(json::object&& obj);
 
    value& operator=(value const& val);
    value& operator=(value&& val);
 
    void clear();
 
public:
    json::type type;
    union
    {
        int integer;
        double number;
        bool boolean;
        json::string* str;
        json::array* arr;
        json::object* obj;
    };
};
 
}
 
 
namespace json {
 
 
value::value() : type(json::type::null)
{
    std::cerr << "default constructor null" << '\n';
}
 
value::~value()
{
    std::cerr << "destructor" << '\n';
    clear();
}
 
value::value(double number) : type(json::type::number), number(number)
{
    std::cerr << "copy constructor number" << '\n';
}
 
value::value(int integer) : type(json::type::integer), integer(integer)
{
    std::cerr << "copy constructor integer" << '\n';
}
 
value::value(bool b) : type(json::type::boolean), boolean(b)
{
    std::cerr << "copy constructor boolean" << '\n';
}
 
value::value(char const* str) : value(json::string(str))
{
}
 
value::value(json::string const& str) : type(json::type::string), str(new json::string(str))
{
    std::cerr << "copy constructor string" << '\n';
}
 
value::value(json::array const& arr) : type(json::type::array), arr(new json::array(arr))
{
    std::cerr << "copy constructor array" << '\n';
}
 
value::value(json::object const& obj) : type(json::type::object), obj(new json::object(obj))
{
    std::cerr << "copy constructor object" << '\n';
}
 
value::value(value const& val)
{
    std::cerr << "copy constructor value" << '\n';
    switch (val.type)
    {
    case json::type::array:
        {
            arr = new json::array(*val.arr);
            type = json::type::array;
        }
        break;
    case json::type::string:
        {
            str = new json::string(*val.str);
            type = json::type::string;
        }
        break;
    case json::type::object:
        {
            obj = new json::object(*val.obj);
            type = json::type::object;
        }
        break;
    default:
        std::memcpy(this, &val, sizeof(val));
    }
}
 
value::value(json::string && str)
    : type(json::type::string)
    , str(new json::string(std::move(str)))
{
    std::cerr << "move constructor string" << '\n';
}
 
value::value(json::array && arr)
    : type(json::type::array)
    , arr(new json::array(std::move(arr)))
{
    std::cerr << "move constructor array" << '\n';
}
 
value::value(json::object && obj)
    : type(json::type::object)
    , obj(new json::object(std::move(obj)))
{
    std::cerr << "move constructor object" << '\n';
}
 
value::value(value && val)
{
    std::cerr << "move constructor value" << '\n';
    std::memcpy(this, &val, sizeof(val));
    val.type = json::type::null;
}
 
value& value::operator=(double number)
{
    std::cerr << "assignment number" << '\n';
    clear();
    type = json::type::number;
    this->number = number;
    return *this;
}
value& value::operator=(int integer)
{
    std::cerr << "assignment integer" << '\n';
    clear();
    type = json::type::integer;
    this->integer = integer;
    return *this;
}
 
value& value::operator=(bool b)
{
    std::cerr << "assignment bool" << '\n';
    clear();
    type = json::type::integer;
    this->integer = integer;
    return *this;
}
 
value& value::operator=(json::string const& str)
{
    std::cerr << "assignment string" << '\n';
    clear();
    this->str = new json::string(str);
    type = json::type::string;
    return *this;
}
 
value& value::operator=(json::array const& arr)
{
    std::cerr << "assignment array" << '\n';
    clear();
    this->arr = new json::array(arr);
    type = json::type::array;
    return *this;
}
 
value& value::operator=(json::object const& obj)
{
    std::cerr << "assignment object" << '\n';
    clear();
    this->obj = new json::object(obj);
    type = json::type::object;
    return *this;
}
 
value& value::operator=(value const& val)
{
    std::cerr << "assignment value" << '\n';
    clear();
    switch (val.type)
    {
    case json::type::array:
        {
            arr = new json::array(*val.arr);
            type = json::type::array;
        }
        break;
    case json::type::string:
        {
            str = new json::string(*val.str);
            type = json::type::string;
        }
        break;
    case json::type::object:
        {
            obj = new json::object(*val.obj);
            type = json::type::object;
        }
        break;
    default:
        std::memcpy(this, &val, sizeof(val));
    }
    return *this;
}
 
value& value::operator=(json::string&& str)
{
    std::cerr << "move assignment string" << '\n';
    clear();
    this->str = new json::string(std::move(str));
    type = json::type::string;
    return *this;
}
 
value& value::operator=(json::array&& arr)
{
    std::cerr << "move assignment array" << '\n';
    clear();
    this->arr = new json::array(std::move(arr));
    type = json::type::array;
    return *this;
}
 
value& value::operator=(json::object&& obj)
{
    std::cerr << "move assignment object" << '\n';
    clear();
    this->obj = new json::object(std::move(obj));
    type = json::type::object;
    return *this;
}
 
value& value::operator=(value && val)
{
    std::cerr << "move assignment value" << '\n';
    clear();
    std::memcpy(this, &val, sizeof(val));
    val.type = json::type::null;
    return *this;
}
 
void value::clear()
{
    switch (type)
    {
    case json::type::array:
        {
            delete arr;
        }
        break;
    case json::type::string:
        {
            delete str;
        }
        break;
    case json::type::object:
        {
            delete obj;
        }
        break;
    }
    type = json::type::null;
}
 
 
}
 
int main(int argc, char* argv [])
{
    json::string s = "HelloWorld";
    json::value v_str = std::move(s);
    json::value v_arr = std::move(std::vector<json::value>(1, v_str));
 
    return 0;
}