/*
    Copyright (C) 2011 Adam Wardyński, Tomasz Śniatowski, Paweł Kędzia,
    Adam Radziszewski, Bartosz Broda
    Part of the WCCL project

    This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.

    This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. 

    See the LICENSE, COPYING.LESSER and COPYING files for more details.
*/

#ifndef LIBWCCL_WCCLFILEOPSECTIONS_H
#define LIBWCCL_WCCLFILEOPSECTIONS_H

#include <libwccl/ops/opsequence.h>
#include <boost/unordered_map.hpp>
#include <boost/foreach.hpp>

namespace Wccl {

template<class T>
class WcclFileOpSections : boost::noncopyable
{
	BOOST_MPL_ASSERT( (boost::is_base_of<FunctionalOpSequence, T>) );
	BOOST_MPL_ASSERT_NOT( (boost::is_same<FunctionalOpSequence, T>) );
public:
	typedef typename T::op_t op_t;
	typedef typename boost::shared_ptr<op_t> op_ptr_t;
	typedef typename boost::shared_ptr<const op_t> op_ptr_c_t;
	typedef typename boost::shared_ptr<T> ptr_t;
	typedef typename boost::shared_ptr<const T> ptr_c_t;
	typedef typename std::vector<ptr_t> ptr_v_t;
	typedef typename boost::unordered_map<std::string, ptr_t> map_t;
	typedef typename T::name_op_v_t name_op_v_t;
	typedef typename T::name_op_v_c_t name_op_v_c_t;

protected:	
	
	bool has_section(const std::string& name) const;

	const ptr_v_t& sections();
	
	size_t size() const;
	bool empty() const;

	std::vector<std::string> section_names() const;

	T& get_section(const std::string& name);
	const T& get_section(const std::string& name) const;

	ptr_t get_section_ptr(const std::string& name);
	ptr_c_t get_section_ptr(const std::string& name) const;

	op_t& get_op(const std::string& name, size_t idx = 0);
	const op_t& get_op(const std::string& name, size_t idx = 0) const;

	op_ptr_t get_op_ptr(const std::string& name, size_t idx = 0);
	op_ptr_c_t get_op_ptr(const std::string& name, size_t idx = 0) const;

	name_op_v_t& add_name_op_pairs(name_op_v_t& pairs);
	name_op_v_c_t& add_name_op_pairs(name_op_v_c_t& pairs) const;

	name_op_v_t gen_name_op_pairs();
	name_op_v_c_t gen_name_op_pairs() const;
	
	WcclFileOpSections()
		: sections_(),
		  name_section_map_()
	{
	}

	void append(const ptr_t& section);

	ptr_v_t sections_;
	map_t name_section_map_;
};

} /* end ns Wccl */


//
// Implementation details
//
namespace Wccl {

template<class T> inline
bool WcclFileOpSections<T>::has_section(const std::string& name) const
{
	return name_section_map_.find(name) != name_section_map_.end();
}

template<class T> inline
const typename WcclFileOpSections<T>::ptr_v_t& WcclFileOpSections<T>::sections()
{
	return sections_;
}

template<class T> inline
bool WcclFileOpSections<T>::empty() const
{
	return sections_.empty();
}

template<class T> inline
size_t WcclFileOpSections<T>::size() const
{
	return sections_.size();
}

template<class T> inline
std::vector<std::string> WcclFileOpSections<T>::section_names() const
{
	std::vector<std::string> v;
	BOOST_FOREACH (const ptr_t& section, sections_) {
		v.push_back(section->name());
	}
	return v;
}

template<class T> inline
typename WcclFileOpSections<T>::ptr_t WcclFileOpSections<T>::get_section_ptr(const std::string& name)
{
	typename map_t::iterator i = name_section_map_.find(name);
	if (i == name_section_map_.end()) {
		throw InvalidArgument("name", "Section named \"" + name + "\" does not exist.");
	}
	return i->second;
}

template<class T> inline
typename WcclFileOpSections<T>::ptr_c_t WcclFileOpSections<T>::get_section_ptr(const std::string& name) const
{
	typename map_t::const_iterator i = name_section_map_.find(name);
	if (i == name_section_map_.end()) {
		throw InvalidArgument("name", "Section named \"" + name + "\" does not exist.");
	}
	return i->second;
}

template<class T> inline
T& WcclFileOpSections<T>::get_section(const std::string& name)
{
	return *get_section_ptr(name);
}

template<class T> inline
const T& WcclFileOpSections<T>::get_section(const std::string& name) const
{
	return *get_section_ptr(name);
}

template<class T> inline
typename T::name_op_v_t& WcclFileOpSections<T>::add_name_op_pairs(name_op_v_t& pairs)
{
	BOOST_FOREACH (const ptr_t& section, sections_) {
		section->add_name_op_pairs(pairs);
	}
	return pairs;
}

template<class T> inline
typename T::name_op_v_c_t& WcclFileOpSections<T>::add_name_op_pairs(name_op_v_c_t& pairs) const
{
	BOOST_FOREACH (const ptr_t& section, sections_) {
		section->add_name_op_pairs(pairs);
	}
	return pairs;
}

template<class T> inline
typename T::name_op_v_t WcclFileOpSections<T>::gen_name_op_pairs()
{
	name_op_v_t pairs;
	BOOST_FOREACH (const ptr_t& section, sections_) {
		section->add_name_op_pairs(pairs);
	}
	return pairs;
}

template<class T> inline
typename T::name_op_v_c_t WcclFileOpSections<T>::gen_name_op_pairs() const
{
	name_op_v_c_t pairs;
	BOOST_FOREACH (const ptr_t& section, sections_) {
		section->add_name_op_pairs(pairs);
	}
	return pairs;
}

template<class T> inline
void WcclFileOpSections<T>::append(const ptr_t& section)
{
	if (has_section(section->name())) {
		throw InvalidArgument("section", "Section named \"" + section->name() + "\" already added.");
	}
	sections_.push_back(section);
	name_section_map_[section->name()] = section;
}

template<class T> inline
typename T::op_t& WcclFileOpSections<T>::get_op(
	const std::string& name,
	size_t idx)
{
	return get_section(name).get(idx);
}

template<class T> inline
const typename T::op_t& WcclFileOpSections<T>::get_op(
	const std::string& name,
	size_t idx) const
{
	return get_section(name).get(idx);
}

template<class T> inline
typename WcclFileOpSections<T>::op_ptr_t WcclFileOpSections<T>::get_op_ptr(
	const std::string& name,
	size_t idx)
{
	return get_section(name).get_ptr(idx);
}

template<class T> inline
typename WcclFileOpSections<T>::op_ptr_c_t WcclFileOpSections<T>::get_op_ptr(
	const std::string& name,
	size_t idx) const
{
	return get_section(name).get_ptr(idx);
}

} /* end ns Wccl */
#endif // LIBWCCL_WCCLFILEOPSECTIONS_H