#pragma once

#include "marker.hpp"
#include "worklist.hpp"
#include <vector>
#include <list>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <map>

#include <boost/shared_ptr.hpp>

#include "debug_macro.hpp"

/** This dictionary records visited memory address in the process.*/
typedef chocolat::container::Marker<unsigned> AddressMarker;
typedef chocolat::container::WorkList<unsigned> AddressWorkList;

/** This object allocates address or id -> address or id. */
class AddressMap
{
public:
	typedef boost::shared_ptr<std::map<unsigned, unsigned> > shared_ptr;
	static shared_ptr create()
	{
		shared_ptr n(new std::map<unsigned, unsigned>);
		return n;
	}
};

/** This module contains build method of address vector. address book.*/
class AddressList
{
	AddressList()
	{
	}
public:
	typedef unsigned Word;
	typedef std::vector<Word> vector;
	typedef vector::iterator iterator;
	typedef boost::shared_ptr<vector> shared_ptr;
	
	static shared_ptr create()
	{
		return shared_ptr(new std::vector<Word>);
	}
	/** straighten the list.*/
	static void straighten(AddressList::shared_ptr list)
	{
		std::sort(list->begin(), list->end());
		list->erase(std::unique(list->begin(), list->end()),list->end());
	}
	/** predicate to check its in.*/
	class isInList
	{
		AddressList::shared_ptr m_list;
	public:
		isInList(const AddressList::shared_ptr& list)
				:m_list(list)
		{
		}
		bool operator()(unsigned address) const
		{
			auto i = std::find(m_list->begin(), m_list->end(), address);
			return i != m_list->end();
		}
	};
	/** remove all element from former list if is in the latter list.*/
	static void excludeListFrom(AddressList::shared_ptr target,
								const AddressList::shared_ptr& reference)
	{
		auto i = std::remove_if(target->begin(),
					   target->end(),
					   isInList(reference));
		target->erase(i, target->end());
	}
};
/** This utility provides search method for AddressList. */
class AddressListManipulater
{
	AddressList::shared_ptr m_list;
public:
	AddressListManipulater(AddressList::shared_ptr list)
			:m_list(list)
	{
	}
	/* list is assumed to be sorted.
	 * if cannot find, return 0;
	 */
	unsigned getNextFrom(unsigned address)const
	{
		for (auto i=m_list->begin(); i!=m_list->end(); ++i){
			if (*i > address) {
				return *i;
			}
		}
		return 0;
	}
	bool hasNextFrom(unsigned address)const
	{
		return (*m_list)[m_list->size()-1] > address;
	}
	
	template<typename T>
	unsigned getNextIf(unsigned address, const T& predicate)const
	{
		for (auto i=m_list->begin(); i!=m_list->end(); ++i){
			if (*i > address && predicate(*i)) {
				return *i;
			}
		}
		return 0;
	}
	void straighten()const
	{
		AddressList::straighten(m_list);
	}
	bool contains(unsigned address)const
	{
		AddressList::isInList predicate(m_list);
		return predicate(address);
	}
	std::string to_s()const
	{
		if (m_list->empty()){
			return "---- ";
		}
		std::string result;
		for (auto i=m_list->begin();i!=m_list->end(); ++i) {
			result += HexadecimalFormat(*i).to_s() + " ";
		}
		return result;
		
	}
};

