#ifndef XML_VISITOR_H
#define XML_VISITOR_H


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

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

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

  template <class T>
  void Visit(const char* name, T& var)
  {
    Enter(name);
    Visit(var);
    Exit(name);
  }

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

  void Reset()
  {
    output = "<xml>\n";
    depth = 1;
  }

  template <typename T>
  typename std::enable_if< std::is_arithmetic<T>::value >::type
  Visit(T& var)
  {
    output += indent(depth) + std::to_string(var) + "\n";
  }

  template <class T>
  typename std::enable_if< !std::is_arithmetic<T>::value >::type
  Visit(T& var)
  {
    var.Visit(*this);
  }

  template <class T>
  void Visit(Array<T>& arr)
  {
    for (auto i : arr)
    {
      Visit("item", i);
    }
  }

  void Visit(String& var)
  {
    // TODO: limitiation is that the string can not have a new line
    output += indent(depth) + var + "\n";
  }

private:
  int depth = 0;
  std::string output;
};



class XMLDeserializerVisitor : public Deserializer
{
public:
  XMLDeserializerVisitor(std::string data)
  {
    Reset(data);
  }

  void Reset(const std::string& a_input)
  {
    input = a_input;
    String str;
    Visit(str); // deserialize the <xml> tag
  }

  void Enter(const char* name)
  {
    String str;
    Visit(str);
    //printf("enter deserializing for -%s-, got -%s-\n", name, str.c_str());
  }

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

  template <class T>
  void Visit(Array<T>& arr)
  {
    while (true)
    {
      int oldOffset = offset;
      String str;
      Visit(str);
      offset = oldOffset;
      if (str != "<item>")
        break;

      T item;
      Visit("item", item);
      arr.push_back(item);
    }
  }

  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)
  {
    stripWhiteSpace();
    int newLineOff = input.find("\n", offset);
    if (newLineOff != std::string::npos)
        var = input.substr(offset, newLineOff-offset);
    offset = newLineOff;
  }

  template <class T>
  void Visit(const char* name, T& var)
  {
    Enter(name);
    Visit(var);
    Exit(name);
  }

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

private:
  std::string input;
  int offset = 0;
  void stripWhiteSpace()
  {
    while (isspace(input.c_str()[offset]))
    {
        offset++;
    }
  }
};


#endif // XML_VISITOR_H


