/*
 * Copyright (C) 2007, 2008  Miriam Ruiz <little_miry@yahoo.es>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _games_taghandler_h
#define _games_taghandler_h

#include <string>
#include <vector>
#include <iostream>

class FilterTagHandler
{
protected:
	class Element
	{
	public:
		inline Element(const std::string tag_name, unsigned int tag_position, unsigned int tag_flag)
		{ name = tag_name; position = tag_position; flag = tag_flag; }

		std::string name;
		unsigned int position;
		unsigned int flag;
	};

public:
	class Result
	{
	public:
		inline Result()
		{ size = 1; data = new unsigned int[size]; for (unsigned int i=0; i<size; i++) data[i] = 0; }

		~Result()
		{ if (data) delete[] data; }

		inline void Clear()
		{
			for (unsigned int i=0; i<size; i++) data[i] = 0;
		}

		inline void SetTag(unsigned int position, unsigned int flag)
		{
			Size(position+1);
			data[position] = data[position] | flag;
		}

		inline void ResetTag(unsigned int position, unsigned int flag)
		{
			Size(position+1);
			data[position] = data[position] & ~flag;
		}

		inline bool CheckAny(unsigned int position, unsigned int flag)
		{
			Size(position+1);
			if ((data[position] & flag) != 0) return true;
			return false;
		}

		inline bool CheckAll(unsigned int position, unsigned int flag)
		{
			Size(position+1);
			if ((data[position] & flag) == flag) return true;
			return false;
		}

		bool CompareAny(const Result &red)
		{
			unsigned int min_size = size;
			if (min_size > red.size) min_size = red.size;
			for (unsigned int i=0; i<min_size; i++)
				if ((data[i] & red.data[i]) != 0) return true;
			return false;
		}

		bool CompareAll(const Result &green)
		{
			unsigned int min_size = size;
			if (min_size > green.size) min_size = green.size;
			for (unsigned int i=0; i<min_size; i++)
				if ((data[i] & green.data[i]) != green.data[i]) return false;
			if (size >= green.size) return true;
			for (unsigned int j=size; j<green.size; j++)
				if (green.data[j] != 0) return false;
			return true;
		}

		void Print(std::ostream &out) const
		{
			unsigned int tmp_position = 0;
			unsigned int tmp_index = 0;
			for (unsigned int i=0; i<size; i++)
			{
				unsigned int tmp_flag = 1;
				unsigned int tmp_data = data[tmp_index];
				while (tmp_flag) {
					out << ( tmp_data & tmp_flag ? "*" : "-" ) ;
					tmp_flag = tmp_flag << 1;
					tmp_position++;
					if (tmp_position % 8 == 0) out << " " ;
				}
				tmp_index++;
				out << "  " ;
			}
		}

	protected:
		void Size(unsigned int new_size)
		{
			if (new_size > size)
			{
				unsigned int *new_data = new unsigned int[new_size];
				for (unsigned int i=0; i<new_size; i++) new_data[i] = 0;
				if (data)
				{
					for (unsigned int i=0; i<size; i++) new_data[i] = data[i];
					delete[] data;
				}
				data = new_data;
				size = new_size;
			}
		}

		unsigned int size;
		unsigned int *data;
	};

public:
	FilterTagHandler();
	~FilterTagHandler();

	const Element *GetTag(const std::string name);
	void AddTag(const std::string name);
	bool TagExists(const std::string name);
	void PrintAll(std::ostream &out) const;
	void Print(std::ostream &out, Result *result) const;

	inline void Clear()
	{ elements.clear(); current_position=0; current_flag=1; };

	inline void SetTag(Result *result, const std::string name)
	{
		AddTag(name);
		const Element *e = GetTag(name);
		result->SetTag(e->position, e->flag);
	}

	inline void SetTagIfExists(Result *result, const std::string name)
	{
		const Element *e = GetTag(name);
		if (e) result->SetTag(e->position, e->flag);
	}

	inline void ResetTag(Result *result, const std::string name)
	{
		AddTag(name);
		const Element *e = GetTag(name);
		result->ResetTag(e->position, e->flag);
	}

	inline void ResetTagIfExists(Result *result, const std::string name)
	{
		const Element *e = GetTag(name);
		if (e) result->ResetTag(e->position, e->flag);
	}

	inline bool CheckTag(Result *result, const std::string name)
	{
		//AddTag(name);
		const Element *e = GetTag(name);
		return e ? result->CheckAny(e->position, e->flag) : false;
	}

protected:
	std::vector<Element> elements;

	unsigned int current_position;
	unsigned int current_flag;
};

#endif
