#include <cctype>
#include <vector>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <iostream>
#include "StackTrace.h"
#include "Utilities.h"
#include "Types.h"
#include "Visitor.h"
#include "XMLVisitor.h"
#include "JSONVisitor.h"
#include "Property.h"


struct Person
{
  Boolean        m_bool;
  Byte           m_uint8;
  Char           m_int8;
  Unsigned32     m_uint32;
  Unsigned64     m_uint64;
  Integer32      m_int32;
  Integer64      m_int64;
  Float32        m_flt32;
  Float64        m_flt64;

  Integer32      id;
  String         name;
  Array<String>  email;
  Array<String>  phoneNumbers;

  // This can do both the serializing and de-serializing
  // This might be good to have generated, but possibly being more explicit could be better too
  // Also note the members could be private and this will still work
  template <class V>
  void Visit(V& v)
  {
    v.Enter("person");
    v.Visit("id",id);
    v.Visit("name",name);
    v.Visit("email",email);
    v.Visit("numbers",phoneNumbers);
    v.Exit("person");
  }
};


/*
struct Employer
{
  String  name;
  
};
*/



DECLARE_STRUCT(BaseStruct)
DECLARE_MEMBER(int,  m_int,  9)
DECLARE_MEMBER(bool, m_bool, false)
END_STRUCT()

DECLARE_STRUCT(BaseStruct2)
DECLARE_MEMBER(int,  m_int)
DECLARE_MEMBER(bool, m_bool)
END_STRUCT()

DECLARE_STRUCT(TestStruct)
DECLARE_MEMBER(BaseStruct,  m_base)
DECLARE_MEMBER(Integer32,   m_int,   100)
DECLARE_MEMBER(Float32,     m_flt,   9.0)
DECLARE_MEMBER(BaseStruct2, m_other)
DECLARE_MEMBER(Boolean,     m_bool,  false)
END_STRUCT()

DECLARE_STRUCT(Person2)
DECLARE_MEMBER(Integer32,     id)
DECLARE_MEMBER(String,        name)
DECLARE_MEMBER(Array<String>, email)
DECLARE_MEMBER(Array<String>, phoneNumbers)
END_STRUCT()

struct PlainBaseStruct {
  int m_int = 9;
  bool m_bool = false;
};

struct PlainBaseStruct2 {
  int m_int;
  bool m_bool;
};

struct PlainTestStruct {
  PlainBaseStruct  m_base;
  int              m_int = 100;
  float            m_flt = 9.0;
  PlainBaseStruct2 m_other;
  bool             m_bool = false;
};


static_assert(sizeof(BaseStruct) == sizeof(BaseStruct2), "blah 1");
static_assert(sizeof(PlainBaseStruct) == sizeof(PlainBaseStruct2), "blah 2");
static_assert(sizeof(PlainBaseStruct) == sizeof(BaseStruct), "blah 3");
static_assert(sizeof(PlainTestStruct) == sizeof(TestStruct), "blah 4");


#include <iostream>
#include <memory>
#include <utility>
#include <array>
   
struct A {
  A(int&& n) { std::cout << "rvalue overload, n=" << n << "\n"; }
  A(int& n)  { std::cout << "lvalue overload, n=" << n << "\n"; }
  A(const char*& n)    { std::cout << "lvalue str overload, n=" << n << "\n"; }
  A(const char*&& n)  { std::cout << "rvalue str overload, n=" << n << "\n"; }
};

template<class T, class U>
std::unique_ptr<T> make_unique1(U&& u)
{
  return std::unique_ptr<T>(new T(std::forward<U>(u)));
}


#include <cstdio>
#include <iostream>
#include <sstream>
#include <cxxabi.h>



class Z { public: Z() { PrintStackTrace(); } };
class B { public: B() { Z z; } };
class C { public: C() { B b; } };
class D { public: D() { C c; } };

int main(int argc, char* argv[])
{
  auto p1 = make_unique1<A>(2); // rvalue
  int i = 1;
  auto p2 = make_unique1<A>(i); // lvalue
  auto p3 = make_unique1<A>("blah"); // rvalue
  const char *i2 = "foo";
  auto p4 = make_unique1<A>(i2); // lvalue


                typedef struct { static decltype(std::make_tuple()) tuple() { return std::make_tuple(); }
  } blah_name1; typedef struct { static decltype(std::tuple_cat(blah_name1::tuple(), std::make_tuple("x",     1))) tuple() { return std::tuple_cat(blah_name1::tuple(), std::make_tuple("x",      1)); }
  } blah_name2; typedef struct { static decltype(std::tuple_cat(blah_name2::tuple(), std::make_tuple("xb", true))) tuple() { return std::tuple_cat(blah_name2::tuple(), std::make_tuple("xb",  true)); }
  } blah_name3; typedef struct { static decltype(std::tuple_cat(blah_name3::tuple(), std::make_tuple("y",     2))) tuple() { return std::tuple_cat(blah_name3::tuple(), std::make_tuple("y",      2)); }
  } blah_name4; typedef struct { static decltype(std::tuple_cat(blah_name4::tuple(), std::make_tuple("x", false))) tuple() { return std::tuple_cat(blah_name4::tuple(), std::make_tuple("yb", false)); }
  } blah_name5; typedef struct { static decltype(std::tuple_cat(blah_name5::tuple(), std::make_tuple("z",     3))) tuple() { return std::tuple_cat(blah_name5::tuple(), std::make_tuple("z",      3)); }
  } blah_name6; typedef struct { static decltype(std::tuple_cat(blah_name6::tuple(), std::make_tuple("zb", true))) tuple() { return std::tuple_cat(blah_name6::tuple(), std::make_tuple("zb",  true)); }
  } last;

  std::cout << "mangled:   " <<                     typeid(last::tuple()).name()           << std::endl;
  std::cout << "demangled: " << abi::__cxa_demangle(typeid(last::tuple()).name(), 0, 0, 0) << std::endl;

  D stack;

  XMLSerializerVisitor serializer;
  printf("%s", serializer.Output().c_str());
  {
    TestStruct t;
    t.m_int = 1234;
    t.m_bool = true;
    XMLSerializerVisitor serializer;
    serializer.Visit(t);
    printf("%s", serializer.Output().c_str());
  }
  {
    TestStruct t;
    XMLSerializerVisitor serializer;
    serializer.Visit(t);
    printf("%s", serializer.Output().c_str());
  }



  std::string input;

  {  
    Person a;
    a.id = 10;
    a.name = "blah";
    a.email.push_back("em@ail.com");
    a.email.push_back("other@add.com");
    a.phoneNumbers.push_back("123555");

    XMLSerializerVisitor serializer;
    serializer.Visit(a);
    printf("%s", serializer.Output().c_str());
    input = serializer.Output();
  }
  {  
    Person2 a;
    a.id = 10;
    a.name = "blah";
    a.email.push_back("em@ail.com");
    a.email.push_back("other@add.com");
    a.phoneNumbers.push_back("123555");

    XMLSerializerVisitor serializer;
    serializer.Visit(a);
    printf("%s", serializer.Output().c_str());
    input = serializer.Output();
  }
  return 0;

  {
    Person2 b;

    XMLDeserializerVisitor deserializer(input);
    deserializer.Visit(b);

    {
      XMLSerializerVisitor serializer;
      serializer.Visit(b);
      printf("%s", serializer.Output().c_str());
    }
  }


  {  
    Person a;
    a.id = 10;
    a.name = "blah";
    a.email.push_back("em@ail.com");
    a.email.push_back("other@add.com");
    a.phoneNumbers.push_back("123555");

    JsonSerializerVisitor serializer;
    serializer.Visit(a);
    printf("%s", serializer.Output().c_str());
    input = serializer.Output();
  }

  {
    Person b;

    JsonDeserializerVisitor deserializer(input);
    deserializer.Visit(b);

    {
      JsonSerializerVisitor serializer;
      serializer.Visit(b);
      printf("%s", serializer.Output().c_str());
    }
  }

  {  
    Property p("hello world");
    Property i(100);
    Array<Property> vals;
    vals.push_back(p);
    vals.push_back(i);
    Property a(vals);

    XMLSerializerVisitor serializer;
    serializer.Visit(a);
    printf("%s", serializer.Output().c_str());
    input = serializer.Output();
  }

}




