// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef NL_PRIM_CHECKER_H
#define NL_PRIM_CHECKER_H

#include "nel/misc/types_nl.h"
#include "nel/misc/stream.h"

#include <nel/misc/polygon.h>

namespace NLMISC
{
	class CVectorD;
};

namespace NLLIGO
{
	class IPrimitive;
	class CPrimZone;
};

/**
 * A class that reads .primitive files from ligo and that allows
 * to define flags over pacs surfaces with CPrimZone
 * \author Benjamin Legros
 * \author Nevrax France
 * \date 2003
 */
class CPrimChecker
{
public:

	/// Constructor
	CPrimChecker();


	/// Bits value for landscape
	enum
	{
		Include = 1,
		Exclude = 2,
		ClusterHint = 4,
		Water = 8,
		Cliff = 16
	};


	/// Init CPrimChecker
	bool	build(const std::string &primitivesPath, const std::string &igLandPath, const std::string &igVillagePath, const std::string &outputDirectory = "./", bool forceRebuild = false);

	/// load CPrimChecker state
	bool	load(const std::string &outputDirectory = "./");

	/// Reads bits value at a given position
	uint8	get(uint x, uint y)	const	{ return _Grid.get(x, y); }

	/// Reads bits value at a given position
	uint16	index(uint x, uint y) const	{ return _Grid.index(x, y); }

	/// Gete water height
	float	waterHeight(uint index, bool &exists) const
	{
		if (index >= _WaterHeight.size())
		{
			exists = false;
			return 0.0;
		}
		exists = true;
		return _WaterHeight[index];
	}

	/// Render a CPolygon of bit value
	void	renderBits(const NLMISC::CPolygon &poly, uint8 bits);

private:

	/// \name Grid management
	//@{

	/**
	 * A class that allows to store 65536x65536 elements with minimum allocation and dynamic allocation of elements
	 */
	class CGrid
	{
	public:

		/// Constructor
		CGrid()		{ clear(); }

		/// Set bits in grid
		void	set(uint x, uint y, uint8 bits)
		{
			CGridCell	*cell = _Grid[((x&0xff00)>>8) + (y&0xff00)];
			if (cell == NULL)
			{
				cell = new CGridCell();
				_Grid[((x&0xff00)>>8) + (y&0xff00)] = cell;
			}
			cell->set(x, y, bits);
		}

		/// Get bits in grid
		uint8	get(uint x, uint y) const
		{
			CGridCell	*cell = _Grid[((x&0xff00)>>8) + (y&0xff00)];
			return cell != NULL ? cell->get(x, y) : (uint8)0;
		}

		/// Set bits in grid
		void	index(uint x, uint y, uint16 idx)
		{
			CGridCell	*cell = _Grid[((x&0xff00)>>8) + (y&0xff00)];
			if (cell == NULL)
			{
				cell = new CGridCell();
				_Grid[((x&0xff00)>>8) + (y&0xff00)] = cell;
			}
			cell->index(x, y, idx);
		}

		/// Get bits in grid
		uint16	index(uint x, uint y) const
		{
			CGridCell	*cell = _Grid[((x&0xff00)>>8) + (y&0xff00)];
			return cell != NULL ? cell->index(x, y) : (uint16)0;
		}


		/// Clear grid
		void	clear()
		{
			for (uint i=0; i<256*256; ++i)
				if (_Grid != NULL)
				{
					delete _Grid[i];
					_Grid[i] = NULL;
				}
		}

		/// Serializes
		void	serial(NLMISC::IStream &f)
		{
			f.serialCheck((uint32)('PCHK'));
			f.serialVersion(0);

			if (f.isReading())
				clear();

			for (uint i=0; i<256*256; ++i)
			{
				bool	present = (_Grid[i] != NULL);
				f.serial(present);

				if (present)
				{
					if (_Grid[i] == NULL)
						_Grid[i] = new CGridCell();
					_Grid[i]->serial(f);
				}
			}
		}

	private:

		/**
		 * A class that allows to store 256x256 elements
		 */
		class CGridCell
		{
			class CGridElm
			{
				uint16		_Value;

			public:

				CGridElm() : _Value(0)	{}

				uint8		flags() const						{ return (uint8)((_Value >> 11) & 0x1f); }
				void		flags(uint8 bits)					{ _Value |= (((uint16)bits) << 11); }
				uint16		index() const						{ return _Value & 0x07ff; }
				void		index(uint16 idx)					{ _Value = ((idx & 0x07ff) | (_Value & 0xf800)); }

				void		serial(NLMISC::IStream &f)			{ f.serial(_Value); }
			};

		public:

			/// Constructor
			CGridCell()	{}

			/// Set bits in grid
			void	set(uint x, uint y, uint8 bits)		{ _Grid[(x&0xff) + ((y&0xff)<<8)].flags(bits); }

			/// Get bits in grid
			uint8	get(uint x, uint y) const			{ return _Grid[(x&0xff) + ((y&0xff)<<8)].flags(); }


			///
			uint16	index(uint x, uint y)				{ return _Grid[(x&0xff) + ((y&0xff)<<8)].index(); }
			///
			void	index(uint x, uint y, uint idx)		{ _Grid[(x&0xff) + ((y&0xff)<<8)].index(idx); }


			/// Serializes
			void	serial(NLMISC::IStream &f)			{ for (uint i=0; i<256*256; ++i) f.serial(_Grid[i]); }

		private:
			CGridElm	_Grid[256*256];
		};

		CGridCell		*_Grid[256*256];
	};

	CGrid				_Grid;

	std::vector<float>	_WaterHeight;

	//@}



	/// Reads a primitive file and renders contained primitives into grid
	void	readFile(const std::string &filename);

	/// Reads a primitive and its sub primitives
	void	readPrimitive(NLLIGO::IPrimitive *primitive);

	/// Renders a primitive
	void	render(NLLIGO::CPrimZone *zone, uint8 bits);

	/// Render a water shape, as a CPolygon
	void	render(const NLMISC::CPolygon &poly, uint16 value);
};


#endif // NL_PRIM_CHECKER_H

/* End of prim_checker.h */