#ifndef VGS_PROCTOOLS_POSITIONER_H
#define VGS_PROCTOOLS_POSITIONER_H

#include "boost/bind.hpp"
#include <algorithm>
#include <functional>
#include <iterator>
#include <map>
#include <vector>
#include "logger.h" //XXX
#include "format.h" //XXX

namespace proctools
{

	template<typename Group, typename What>
	class positioner
	{
	public:
		struct item_info
		{
			Group groupId;
			What identifier;
			bool selectable;
			int index;

			item_info(const Group& gid, const What& id, bool sel, int index_):
					groupId( gid ), identifier( id ), selectable( sel ), index( index_ ) {}

			bool operator<(const item_info& rhs) const
			{
				if (groupId == rhs.groupId)
					return identifier < rhs.identifier;
				return groupId < rhs.groupId;
			}

		private:
			friend class positioner;
			item_info(void): index(-1) {}
		};

		const item_info& operator[](int index);
		int get_nearest(const item_info& key);

		void add(const Group& groupId, const What& identifier, bool selectable = false);
		void clear(void) { m_items.clear(); m_indices.clear(); }

	private:
		typedef std::vector<item_info> item_list;
		typedef std::map<item_info, int> index_map;
		
		item_list m_items;
		index_map m_indices;
	};

	template<typename Group, typename What>
	const typename positioner<Group, What>::item_info& 
	positioner<Group, What>::operator[](int index)
	{
		static item_info dummy;
		if (index != -1)
			return m_items[index];
		return dummy;
	}

	template<typename Group, typename What>
	int positioner<Group, What>::get_nearest(const item_info& key)
	{
		// dummy item (nothing was selected)
		if (key.index == -1) {
			typename item_list::iterator item = find_if(m_items.begin(), m_items.end(),
					boost::bind( &item_info::selectable, _1 ));
			return item != m_items.end() ? item->index : -1;
		}
			
		// first, check if item itself still exists
		typename index_map::iterator index = m_indices.find(key);
		if (index != m_indices.end())
			return index->second;

		// second, if list shorter than index, return last item of group
		if (static_cast<unsigned int>(key.index) >= m_items.size()) {
			typename item_list::reverse_iterator item = find_if(m_items.rbegin(), m_items.rend(), 
					boost::bind( std::equal_to<Group>(), 
						boost::bind( &item_info::groupId, _1 ), 
						key.groupId ));
			if (item != m_items.rend())
				return item->index;
		}

		// third, nearest item from old index (backwards)
		std::reverse_iterator<typename item_list::iterator> item( m_items.begin() + key.index );
		std::reverse_iterator<typename item_list::iterator> next;
		if ((next = find_if(item, m_items.rend(), 
				boost::bind( std::equal_to<Group>(), 
					boost::bind( &item_info::groupId, _1 ), 
					key.groupId )))
				!= m_items.rend())
			return next->index;

		// desperation
		return -1;
	}

	template<typename Group, typename What>
	void positioner<Group, What>::add(const Group& groupId, const What& identifier, bool selectable)
	{
		int index = m_items.size();
		logger::info(format("add {0}/{1} at index {2}") % (int)groupId % identifier % index);
		item_info newItem( groupId, identifier, selectable, index );
		m_indices[newItem] = index;
		typename index_map::iterator ix = m_indices.find(newItem);
		logger::info(format("found just added? {0}") % (ix != m_indices.end()));
		m_items.push_back(newItem);
	}

}

#endif // VGS_PROCTOOLS_POSITIONER_H
