#include "nodeevents.h"
#include "yaml-cpp/node/node.h"
#include "yaml-cpp/node/impl.h"
#include "yaml-cpp/eventhandler.h"
#include "yaml-cpp/mark.h"

namespace YAML
{
	void NodeEvents::AliasManager::RegisterReference(const detail::node& node)
	{
		m_anchorByIdentity.insert(std::make_pair(node.ref(), _CreateNewAnchor()));
	}
	
	anchor_t NodeEvents::AliasManager::LookupAnchor(const detail::node& node) const
	{
		AnchorByIdentity::const_iterator it = m_anchorByIdentity.find(node.ref());
		if(it == m_anchorByIdentity.end())
			return 0;
		return it->second;
	}

	NodeEvents::NodeEvents(const Node& node): m_pMemory(node.m_pMemory), m_root(*node.m_pNode)
	{
		Setup(m_root);
	}

	void NodeEvents::Setup(const detail::node& node)
	{
		int& refCount = m_refCount[node.ref()];
		refCount++;
		if(refCount > 1)
			return;
		
		if(node.type() == NodeType::Sequence) {
			for(detail::const_node_iterator it=node.begin();it!=node.end();++it)
				Setup(**it);
		} else if(node.type() == NodeType::Map) {
			for(detail::const_node_iterator it=node.begin();it!=node.end();++it) {
				Setup(*it->first);
				Setup(*it->second);
			}
		}
	}

	void NodeEvents::Emit(EventHandler& handler)
	{
		AliasManager am;
		
		handler.OnDocumentStart(Mark());
		Emit(m_root, handler, am);
		handler.OnDocumentEnd();
	}

	void NodeEvents::Emit(const detail::node& node, EventHandler& handler, AliasManager& am) const
	{
		anchor_t anchor = NullAnchor;
		if(IsAliased(node)) {
			anchor = am.LookupAnchor(node);
			if(anchor) {
				handler.OnAlias(Mark(), anchor);
				return;
			}
			
			am.RegisterReference(node);
			anchor = am.LookupAnchor(node);
		}
		
		switch(node.type()) {
			case NodeType::Undefined:
				break;
			case NodeType::Null:
				handler.OnNull(Mark(), anchor);
				break;
			case NodeType::Scalar:
				handler.OnScalar(Mark(), node.tag(), anchor, node.scalar());
				break;
			case NodeType::Sequence:
				handler.OnSequenceStart(Mark(), node.tag(), anchor);
				for(detail::const_node_iterator it=node.begin();it!=node.end();++it)
					Emit(**it, handler, am);
				handler.OnSequenceEnd();
				break;
			case NodeType::Map:
				handler.OnMapStart(Mark(), node.tag(), anchor);
				for(detail::const_node_iterator it=node.begin();it!=node.end();++it) {
					Emit(*it->first, handler, am);
					Emit(*it->second, handler, am);
				}
				handler.OnMapEnd();
				break;
		}
	}

	bool NodeEvents::IsAliased(const detail::node& node) const
	{
		RefCount::const_iterator it = m_refCount.find(node.ref());
		return it != m_refCount.end() && it->second > 1;
	}
}
