#ifndef GRAPHBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define GRAPHBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66

#if defined(_MSC_VER) || (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
#pragma once
#endif

#include "yaml-cpp/mark.h"
#include <string>

namespace YAML
{
  class Parser;
  
  // GraphBuilderInterface
  // . Abstraction of node creation
  // . pParentNode is always NULL or the return value of one of the NewXXX()
  //   functions.
  class GraphBuilderInterface
  {
  public:
    // Create and return a new node with a null value.
    virtual void *NewNull(const Mark& mark, void *pParentNode) = 0;
    
    // Create and return a new node with the given tag and value.
    virtual void *NewScalar(const Mark& mark, const std::string& tag, void *pParentNode, const std::string& value) = 0;
    
    // Create and return a new sequence node
    virtual void *NewSequence(const Mark& mark, const std::string& tag, void *pParentNode) = 0;
    // Add pNode to pSequence.  pNode was created with one of the NewXxx()
    // functions and pSequence with NewSequence().
    virtual void AppendToSequence(void *pSequence, void *pNode) = 0;
    // Note that no moew entries will be added to pSequence
    virtual void SequenceComplete(void *pSequence) {(void)pSequence;}
    
    // Create and return a new map node
    virtual void *NewMap(const Mark& mark, const std::string& tag, void *pParentNode) = 0;
    // Add the pKeyNode => pValueNode mapping to pMap.  pKeyNode and pValueNode
    // were created with one of the NewXxx() methods and pMap with NewMap().
    virtual void AssignInMap(void *pMap, void *pKeyNode, void *pValueNode) = 0;
    // Note that no more assignments will be made in pMap
    virtual void MapComplete(void *pMap) {(void)pMap;}
    
    // Return the node that should be used in place of an alias referencing
    // pNode (pNode by default)
    virtual void *AnchorReference(const Mark& mark, void *pNode) {(void)mark; return pNode;}
  };
  
  // Typesafe wrapper for GraphBuilderInterface.  Assumes that Impl defines
  // Node, Sequence, and Map types.  Sequence and Map must derive from Node
  // (unless Node is defined as void).  Impl must also implement function with
  // all of the same names as the virtual functions in GraphBuilderInterface
  // -- including the ones with default implementations -- but with the
  // prototypes changed to accept an explicit Node*, Sequence*, or Map* where
  // appropriate.
  template <class Impl>
  class GraphBuilder : public GraphBuilderInterface
  {
  public:
    typedef typename Impl::Node Node;
    typedef typename Impl::Sequence Sequence;
    typedef typename Impl::Map Map;
    
    GraphBuilder(Impl& impl) : m_impl(impl)
    {
      Map* pMap = NULL;
      Sequence* pSeq = NULL;
      Node* pNode = NULL;
      
      // Type consistency checks
      pNode = pMap;
      pNode = pSeq;
    }
    
    GraphBuilderInterface& AsBuilderInterface() {return *this;}
    
    virtual void *NewNull(const Mark& mark, void* pParentNode) {
      return CheckType<Node>(m_impl.NewNull(mark, AsNode(pParentNode)));
    }
    
    virtual void *NewScalar(const Mark& mark, const std::string& tag, void *pParentNode, const std::string& value) {
      return CheckType<Node>(m_impl.NewScalar(mark, tag, AsNode(pParentNode), value));
    }
    
    virtual void *NewSequence(const Mark& mark, const std::string& tag, void *pParentNode) {
      return CheckType<Sequence>(m_impl.NewSequence(mark, tag, AsNode(pParentNode)));
    }
    virtual void AppendToSequence(void *pSequence, void *pNode) {
      m_impl.AppendToSequence(AsSequence(pSequence), AsNode(pNode));
    }
    virtual void SequenceComplete(void *pSequence) {
      m_impl.SequenceComplete(AsSequence(pSequence));
    }
    
    virtual void *NewMap(const Mark& mark, const std::string& tag, void *pParentNode) {
      return CheckType<Map>(m_impl.NewMap(mark, tag, AsNode(pParentNode)));
    }
    virtual void AssignInMap(void *pMap, void *pKeyNode, void *pValueNode) {
      m_impl.AssignInMap(AsMap(pMap), AsNode(pKeyNode), AsNode(pValueNode));
    }
    virtual void MapComplete(void *pMap) {
      m_impl.MapComplete(AsMap(pMap));
    }
    
    virtual void *AnchorReference(const Mark& mark, void *pNode) {
      return CheckType<Node>(m_impl.AnchorReference(mark, AsNode(pNode)));
    }
  
  private:
    Impl& m_impl;
    
    // Static check for pointer to T
    template <class T, class U>
    static T* CheckType(U* p) {return p;}
    
    static Node *AsNode(void *pNode) {return static_cast<Node*>(pNode);}
    static Sequence *AsSequence(void *pSeq) {return static_cast<Sequence*>(pSeq);}
    static Map *AsMap(void *pMap) {return static_cast<Map*>(pMap);}
  };
  
  void *BuildGraphOfNextDocument(Parser& parser, GraphBuilderInterface& graphBuilder);
  
  template <class Impl>
  typename Impl::Node *BuildGraphOfNextDocument(Parser& parser, Impl& impl)
  {
    GraphBuilder<Impl> graphBuilder(impl);
    return static_cast<typename Impl::Node *>(BuildGraphOfNextDocument(
      parser, graphBuilder
    ));
  }
}

#endif // GRAPHBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
