#ifndef _RSGVARIABLEMANAGER_HPP
#define _RSGVARIABLEMANAGER_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Random Shader Generator
 * ----------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Variable manager.
 *
 * Memory management:
 *  Variable manager owns variable objects until they are either explictly
 *  removed or moved to currently active scope. After that the ownership
 *  is transferred to Scope or the object that called removeEntry().
 *//*--------------------------------------------------------------------*/

#include "rsgDefs.hpp"
#include "rsgVariable.hpp"
#include "rsgVariableValue.hpp"
#include "rsgNameAllocator.hpp"

#include <iterator>
#include <vector>
#include <set>

namespace rsg
{

class ValueEntry
{
public:
							ValueEntry				(const Variable* variable);
							~ValueEntry				(void) {}

	const Variable*			getVariable				(void) const	{ return m_variable;	}

	ConstValueRangeAccess	getValueRange			(void) const	{ return m_valueRange.asAccess();	}
	ValueRangeAccess		getValueRange			(void)			{ return m_valueRange.asAccess();	}

private:
	const Variable*			m_variable;
	ValueRange				m_valueRange;
};

// Variable scope manages variable allocation.
class VariableScope
{
public:
										VariableScope			(void);
										~VariableScope			(void);

	Variable*							allocate				(const VariableType& type, Variable::Storage storage, const char* name);
	void								declare					(Variable* variable);		//!< Move from live set to declared set
	void								removeLive				(const Variable* variable);	//!< Just remove from live set (when migrating to parent).

	const std::vector<Variable*>&		getDeclaredVariables	(void) const	{ return m_declaredVariables;	}

	std::vector<Variable*>&				getLiveVariables		(void)			{ return m_liveVariables;		}
	const std::vector<Variable*>&		getLiveVariables		(void) const	{ return m_liveVariables;		}

private:
										VariableScope			(const VariableScope& other);
	VariableScope&						operator=				(const VariableScope& other);

	std::vector<Variable*>				m_declaredVariables;	//!< Variables declared in this scope. Not available for expressions.
	std::vector<Variable*>				m_liveVariables;		//!< Live variables (available for expression) that can be declared in this scope.
};

class ValueScope
{
public:
										ValueScope				(void);
										~ValueScope				(void);

	ValueEntry*							allocate				(const Variable* variable);
	ValueEntry*							findEntry				(const Variable* variable) const;
	void								setValue				(const Variable* variable, ConstValueRangeAccess value);
	void								removeValue				(const Variable* variable);

	std::vector<ValueEntry*>&			getValues				(void)			{ return m_entries;	}
	const std::vector<ValueEntry*>&		getValues				(void) const	{ return m_entries; }

	void								clear					(void);

private:
										ValueScope				(const ValueScope& other);
	ValueScope&							operator=				(const ValueScope& other);

	std::vector<ValueEntry*>			m_entries;
};

class ReservedScalars
{
public:
	int numScalars;

	ReservedScalars (void)
		: numScalars(0)
	{
	}
};

// \todo [2011-05-26 pyry] Clean up this a bit, separate const variant.
template <typename Item, typename Iterator, class Filter>
class FilteredIterator : public std::iterator<std::input_iterator_tag, Item>
{
public:
	FilteredIterator (Iterator iter, Iterator end, Filter filter)
		: m_iter	(iter)
		, m_end		(end)
		, m_filter	(filter)
	{
	}

	FilteredIterator operator+ (ptrdiff_t offset) const
	{
		Iterator nextEntry = m_iter;
		while (offset--)
			nextEntry = findNext(m_filter, nextEntry, m_end);
		return FilteredIterator(nextEntry, m_end, m_filter);
	}

	FilteredIterator& operator++ ()
	{
		// Pre-increment
		m_iter = findNext(m_filter, m_iter, m_end);
		return *this;
	}

	FilteredIterator operator++ (int)
	{
		// Post-increment
		FilteredIterator copy = *this;
		m_iter = findNext(m_filter, m_iter, m_end);
		return copy;
	}

	bool operator== (const FilteredIterator& other) const
	{
		return m_iter == other.m_iter;
	}

	bool operator!= (const FilteredIterator& other) const
	{
		return m_iter != other.m_iter;
	}

	const Item& operator* (void)
	{
		DE_ASSERT(m_iter != m_end);
		DE_ASSERT(m_filter(*m_iter));
		return *m_iter;
	}

private:
	static Iterator findNext (Filter filter, Iterator iter, Iterator end)
	{
		do
			iter++;
		while (iter != end && !filter(*iter));
		return iter;
	}

	Iterator		m_iter;
	Iterator		m_end;
	Filter			m_filter;
};

template <class Filter>
class ValueEntryIterator : public FilteredIterator<const ValueEntry*, std::vector<const ValueEntry*>::const_iterator, Filter>
{
public:
	ValueEntryIterator (std::vector<const ValueEntry*>::const_iterator begin, std::vector<const ValueEntry*>::const_iterator end, Filter filter)
		: FilteredIterator<const ValueEntry*, std::vector<const ValueEntry*>::const_iterator, Filter>(begin, end, filter)
	{
	}
};

class VariableManager
{
public:
									VariableManager					(NameAllocator& nameAllocator);
									~VariableManager				(void);

	int								getNumAllocatedScalars			(void) const { return m_numAllocatedScalars; }
	int								getNumAllocatedShaderInScalars	(void) const { return m_numAllocatedShaderInScalars; }
	int								getNumAllocatedShaderInVariables(void) const { return m_numAllocatedShaderInVariables; }
	int								getNumAllocatedUniformScalars	(void) const { return m_numAllocatedUniformScalars; }

	void							reserve							(ReservedScalars& store, int numScalars);
	void							release							(ReservedScalars& store);

	Variable*						allocate						(const VariableType& type);
	Variable*						allocate						(const VariableType& type, Variable::Storage storage, const char* name);

	void							setStorage						(Variable* variable, Variable::Storage storage);

	void							setValue						(const Variable* variable, ConstValueRangeAccess value);
	const ValueEntry*				getValue						(const Variable* variable) const;
	const ValueEntry*				getParentValue					(const Variable* variable) const;

	void							removeValueFromCurrentScope		(const Variable* variable);

	void							declareVariable					(Variable* variable);
	bool							canDeclareInCurrentScope		(const Variable* variable) const;
	const std::vector<Variable*>&	getLiveVariables				(void) const;

	void							pushVariableScope				(VariableScope& scope);
	void							popVariableScope				(void);

	void							pushValueScope					(ValueScope& scope);
	void							popValueScope					(void);

	template <class Filter>
	ValueEntryIterator<Filter>		getBegin						(Filter filter = Filter()) const;

	template <class Filter>
	ValueEntryIterator<Filter>		getEnd							(Filter filter = Filter()) const;

	template <class Filter>
	bool							hasEntry						(Filter filter = Filter()) const;

private:
									VariableManager					(const VariableManager& other);
	VariableManager&				operator=						(const VariableManager& other);

	VariableScope&					getCurVariableScope				(void)			{ return *m_variableScopeStack.back();	}
	const VariableScope&			getCurVariableScope				(void) const	{ return *m_variableScopeStack.back();	}

	ValueScope&						getCurValueScope				(void)			{ return *m_valueScopeStack.back();	}
	const ValueScope&				getCurValueScope				(void) const	{ return *m_valueScopeStack.back();	}

	std::vector<VariableScope*>		m_variableScopeStack;
	std::vector<ValueScope*>		m_valueScopeStack;

	std::vector<const ValueEntry*>	m_entryCache;	//!< For faster value entry access.

	int								m_numAllocatedScalars;
	int								m_numAllocatedShaderInScalars;
	int								m_numAllocatedShaderInVariables;
	int								m_numAllocatedUniformScalars;
	NameAllocator&					m_nameAllocator;
};

template <class Filter>
ValueEntryIterator<Filter> VariableManager::getBegin (Filter filter) const
{
	std::vector<const ValueEntry*>::const_iterator first = m_entryCache.begin();
	while (first != m_entryCache.end() && !filter(*first))
		first++;
	return ValueEntryIterator<Filter>(first, m_entryCache.end(), filter);
}

template <class Filter>
ValueEntryIterator<Filter> VariableManager::getEnd (Filter filter) const
{
	return ValueEntryIterator<Filter>(m_entryCache.end(), m_entryCache.end(), filter);
}

template <class Filter>
bool VariableManager::hasEntry (Filter filter) const
{
	for (std::vector<const ValueEntry*>::const_iterator i = m_entryCache.begin(); i != m_entryCache.end(); i++)
	{
		if (filter(*i))
			return true;
	}
	return false;
}

// Common filters

class AnyEntry
{
public:
	typedef ValueEntryIterator<AnyEntry> Iterator;

	bool operator() (const ValueEntry* entry) const
	{
		DE_UNREF(entry);
		return true;
	}
};

class IsWritableEntry
{
public:
	bool operator() (const ValueEntry* entry) const
	{
		switch (entry->getVariable()->getStorage())
		{
			case Variable::STORAGE_LOCAL:
			case Variable::STORAGE_SHADER_OUT:
			case Variable::STORAGE_PARAMETER_IN:
			case Variable::STORAGE_PARAMETER_OUT:
			case Variable::STORAGE_PARAMETER_INOUT:
				return true;

			default:
				return false;
		}
	}
};

template <Variable::Storage Storage>
class EntryStorageFilter
{
public:
	typedef ValueEntryIterator<EntryStorageFilter<Storage> > Iterator;

	bool operator() (const ValueEntry* entry) const
	{
		return entry->getVariable()->getStorage() == Storage;
	}
};

typedef EntryStorageFilter<Variable::STORAGE_LOCAL>			LocalEntryFilter;
typedef EntryStorageFilter<Variable::STORAGE_SHADER_IN>		ShaderInEntryFilter;
typedef EntryStorageFilter<Variable::STORAGE_SHADER_OUT>	ShaderOutEntryFilter;

} // rsg

#endif // _RSGVARIABLEMANAGER_HPP