#ifndef JSON_VISITOR_H
#define JSON_VISITOR_H


class JsonSerializerVisitor : public Serializer
{
public:
  JsonSerializerVisitor()
  {
    Reset();
  }

  void Enter(const char* name)
  {
    output += indent(depth++) + quoted(name) + " : {\n";
  }

  void Exit(const char* name)
  {
    output += "\n" + indent(--depth) + "}\n";
  }

  template <class T>
  void Visit(Array<T>& arr)
  {
    output += "[ ";
    for (int i = 0; i < arr.size(); i++)
    {
      Visit(arr.at(i));
      if ( i != arr.size() - 1 )
        output += " ,";
      output += " ";
    }
    output += "]";
  }

  void Visit(Integer32& var)
  {
    output += std::to_string(var);
  }

  void Visit(String& var)
  {
    output += quoted(var.c_str());
  }

  template <class T>
  void Visit(const char* name, T& var)
  {
    if (lastDepth == depth)
        output += " ,\n";
    lastDepth = depth;

    output += indent(depth) + quoted(name) + " : ";
    Visit(var);
  }

  template <class T>
  void Visit(T& var)
  {
    var.Visit(*this);
  }

  const std::string& Output()
  {
    output += "}\n";
    return output;
  }

  void Reset()
  {
    output = "{\n";
    depth = 1;
  }
private:
  int lastDepth = 0;
  int depth = 0;
  std::string output;
  
};


class JsonDeserializerVisitor : public VisitorBase
{
public:
  JsonDeserializerVisitor(std::string data)
  {
    Reset(data);
  }

  void Reset(const std::string& a_input)
  {
    input = a_input;
    String str;
    ParseString(str); // gets the '{' 
    assert(str == "{");
  }

  void ParseString(String& var)
  {
    stripWhiteSpace();
    int pos = offset;
    while (input.c_str()[pos])
    {
        if (isspace(input.c_str()[pos]))
        {
            var = input.substr(offset, pos-offset);
            offset = pos;
            return;
        }
        pos++;
    }
  }

  void Enter(const char* name)
  {
    String str;
    ParseString(str); // gets the name
    //printf("name: -%s-", quoted(name).c_str());
    //printf("str:  -%s-", str.c_str());
    assert(str == quoted(name));
    ParseString(str); // gets the ':'
    assert(str == ":");
    ParseString(str); // gets the '{'
    assert(str == "{");
    //printf("enter deserializing for -%s-, got -%s-\n", name, str.c_str());
  }

  void Exit(const char* name)
  {
    String str;
    ParseString(str); // gets the '}'
    assert(str == "}");
    //printf("exit deserializing for -%s-, got -%s-\n", name, str.c_str());
  }

  template <class T>
  void Visit(Array<T>& arr)
  {
    String str;
    ParseString(str); // gets the '['
    assert(str == "[");
    while (true)
    {
      T item;
      Visit(item);
      arr.push_back(item);

      String str;
      ParseString(str); // gets the ',' or ']' or '],'
      if (str != ",")
      {
        //printf("expecting ]");
        //printf("str:  -%s-", str.c_str());
        assert(str == "]");
        break;
      }
    }
  }

  void Visit(Integer32& var)
  {
    const char* ptr = input.c_str();
    char* endPtr;
    var = strtol(ptr + offset, &endPtr, 10);
    offset = endPtr - ptr;
  }

  void Visit(String& var)
  {
    // TODO
    stripWhiteSpace();
    int newLineOff = input.find("\"", offset + 1);
    if (newLineOff != std::string::npos)
        var = input.substr(offset + 1, newLineOff - offset - 1);
    offset = newLineOff + 1;
  }

  template <class T>
  void Visit(const char* name, T& var)
  {
    String str;
    ParseString(str); // gets the name
    //printf("name: -%s-", quoted(name).c_str());
    //printf("str:  -%s-", str.c_str());
    assert(str == quoted(name));
    ParseString(str); // gets the ':'
    assert(str == ":");
    Visit(var);

    int oldOffset = offset;
    ParseString(str); // gets the ',' if there
    if (str != ",")
        offset = oldOffset;
  }

  template <class T>
  void Visit(T& var)
  {
    var.Visit(*this);
  }

private:
  std::string input;
  int offset = 0;
  std::string quoted(const char* name)
  {
    return std::string("\"" + std::string(name) + "\"");
  }
  void stripWhiteSpace()
  {
    while (isspace(input.c_str()[offset]))
    {
        offset++;
    }
  }
};



#endif // JSON_VISITOR_H

