/*
								+---------------------------------+
								|                                 |
								|   ***   Font tex-atlas   ***    |
								|                                 |
								|  Copyright  © -tHE SWINe- 2007  |
								|                                 |
								|          FontTexture.h          |
								|                                 |
								+---------------------------------+
*/

#pragma once
#ifndef __FONT_TEXTURE_INCLUDED
#define __FONT_TEXTURE_INCLUDED

/**
 *	@file gl2/FontTexture.h
 *	@author -tHE SWINe-
 *	@date 2007
 *	@brief font texture atlas
 *
 *	@date 2007-06-04
 *
 *	fixed clipped characters artifacts occuring on some fonts with strong character
 *	kerning by rendering glyphs into the center of the bitmap, thus ignoring character metrics
 *
 *	note MSVC compiler has strange error where it incorrectly reports unexpected symbol
 *	CFontTexturePage instead of undefined symbol TBmp in case neither Tga.h or Jpeg.h is included
 *
 *	@date 2007-07-17
 *
 *	added line descent font parameter, ie. CFontTexturePage::m_n_char_descent, new field in
 *	CFontTexturePage constructor and CFontTexturePage::n_Line_descent()
 *	char descent is also added to bitmap info-block so it's not compatible with bitmaps
 *	created by the older versions
 *
 *	@date 2007-12-24
 *
 *	improved linux compatibility by adding posix integer types
 *
 *	@date 2008-03-04
 *
 *	now using Integer.h header
 *
 *	@date 2008-08-08
 *
 *	added \#ifdef for windows 64
 *
 *	@date 2009-01-11
 *
 *	added the geometry padding feature
 *
 *	@date 2009-05-04
 *
 *	fixed mixed windows / linux line endings
 *
 *	@date 2009-05-23
 *
 *	removed all instances of std::vector::reserve and replaced them by stl_ut::Reserve_*
 *
 *	@date 2009-10-11
 *
 *	replaced stl container ::resize() by stl_ut::Resize_*() to avoid unhandled
 *	std::bad_alloc
 *
 *	@date 2009-10-20
 *
 *	fixed some warnings when compiling under VC 2005, implemented "Security
 *	Enhancements in the CRT" for VC 2008. compare against MyProjects_2009-10-19_
 *
 *	@date 2010-10-29
 *
 *	Unified windows detection macro to "\#if defined(_WIN32) || defined(_WIN64)".
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 */

#include "../Integer.h"
#include "../Tga.h"

#if defined(_WIN32) || defined(_WIN64)

/**
 *	@def FONT_TEXTURE_CREATE_FONTS
 *
 *	@brief enables ability to create font textures (needs windows GDI)
 *
 *	Only under windows, when defined, it's possible to create font pages
 *	on the fly using GDI (saves space taken by font textures, should be
 *	generaly faster than outline fonts drawn as polygons under OpenGL).
 */
#define FONT_TEXTURE_CREATE_FONTS

#endif // _WIN32 || _WIN64

#ifdef FONT_TEXTURE_CREATE_FONTS

/**
 *	@brief GDI bitmap
 *
 *	Simple class, enwrapping GDI bitmap for drawing it and into it.
 */
class CGDI_Bitmap {
private:
	int m_n_width;
	int m_n_height;
	int m_n_plane_num;
	int m_n_bit_count;
	unsigned char *m_p_buffer;
	BITMAPINFO m_t_info;
	HBITMAP m_h_dib;

	HDC m_h_dc; // dc for rendering to bitmap
	HGDIOBJ m_h_prev_dc_obj;

public:
	/**
	 *	@brief default constructor
	 *
	 *	Creates bitmap with size n_width per n_height pixels,
	 *		n_plane_num color planes and n_bit_count bits per pixel
	 *
	 *	@param[in] n_width is bitmap width in pixels
	 *	@param[in] n_height is bitmap height in pixels
	 *	@param[in] n_plane_num is number of bit planes (should be 1)
	 *	@param[in] n_bit_count is number of bits per pixel (should be 32)
	 *
	 *	@note Uncompressed RGB formats are supported only.
	 */
	CGDI_Bitmap(int n_width, int n_height, int n_plane_num = 1, int n_bit_count = 32);

	/**
	 *	@brief destructor
	 */
	~CGDI_Bitmap();

	/**
	 *	@brief checks if constructor succeeded
	 *
	 *	@return In case constructor succeeded in creating bitmap, returns true. otherwise false.
	 */
	inline bool b_State() const
	{
		return m_h_dib != 0;
	}

	/**
	 *	@brief gets bitmap width
	 *
	 *	@return Returns bitmap width.
	 */
	inline int n_Width() const { return m_n_width; }

	/**
	 *	@brief gets bitmap height
	 *
	 *	@return Returns bitmap height.
	 */
	inline int n_Height() const { return m_n_height; }

	/**
	 *	@brief gets number of color planes
	 *
	 *	@return Returns number of bitmap color planes.
	 */
	inline int n_Plane_Num() const { return m_n_plane_num; }

	/**
	 *	@brief gets bit depth
	 *
	 *	@return Returns number of bits per pixel.
	 */
	inline int n_Bit_Count() const { return m_n_bit_count; }

	/**
	 *	@brief gets bitmap buffer
	 *
	 *	@return Returns pointer to bitmap buffer (do not delete!).
	 */
	inline unsigned char *p_Buffer() { return m_p_buffer; }

	/**
	 *	@brief gets bitmap buffer
	 *
	 *	@return Returns const pointer to bitmap buffer (do not delete!).
	 */
	inline const unsigned char *p_Buffer() const { return m_p_buffer; }

	/**
	 *	@brief gets bitmap DC
	 *
	 *	Use to render to bitmap. Creates device context and assigns this
	 *		bitmap to it.
	 *
	 *	@return Returns device context of the bitmap.
	 *
	 *	@note Don't forget to release DC by calling ReleaseDC() once rendering
	 *		is finished.
	 */
	HDC h_GetDC();

	/**
	 *	@brief releases device context
	 *
	 *	This should be called once device context obtained by calling h_GetDC() is
	 *		no longer needed.
	 *
	 *	@return Returns true on success or in case no context was created, otherwise false.
	 */
	bool ReleaseDC();

	/**
	 *	@brief draws bitmap
	 *
	 *	Draws bitmap using device context h_dc (not this bitmap's DC, either of screen
	 *		(window) / printer / other bitmap DC) on position n_x, n_y with raster
	 *		operation n_op.
	 *
	 *	@param[in] h_dc is device context to draw this bitmap to
	 *	@param[in] n_x is desired horizontal output position
	 *	@param[in] n_y is desired vertical output position
	 *	@param[in] n_op is raster operation (default SRCCOPY, see BitBlt documentation
	 *		on MSDN for more details)
	 */
	void Draw(HDC h_dc, int n_x, int n_y, int n_op = SRCCOPY) const;
};

#endif // FONT_TEXTURE_CREATE_FONTS

/**
 *	@brief glyph information
 *
 *	Structure, holding info about an character (metrics, bitmap, texture page placement).
 */
struct TCharacterInfo {
	unsigned char n_code; /**< character code */

	int n_bitmap_width; /**< width of actual bitmap containing character (without offset) */
	int n_bitmap_height; /**< height of actual bitmap containing character (without offset) */
	Vector2i v_baseline_offset; /**< offset from baseline point to bottom-left bitmap corner */
	int n_caret_shift; /**< width of character base (position shift for the next character) */

	Vector2i v_bitmap_pos; /**< origin in character atlas */

#ifdef FONT_TEXTURE_CREATE_FONTS

	/**
	 *	@brief creates character info
	 *
	 *	Create character info for n_character, using font selected in r_bitmap.
	 *		Renders character to the bitmap and determines it's bounding box by scanning
	 *		bitmap and finding range of affected scanlines / columns.
	 *
	 *	@param[in] n_character is requested character ascii code
	 *	@param[in,out] r_bitmap serves as temporal buffer for drawing
	 *		(must have sufficient size).
	 *
	 *	@return Returns true on success or false on failure (not enough memory).
	 *
	 *	@todo Think about supporting unicode here.
	 */
	bool Create(unsigned char n_character, CGDI_Bitmap &r_bitmap);

	/**
	 *	@brief draws character to texture page
	 *
	 *	Renders character to it's position v_bitmap_pos using device context h_dc.
	 *		This function is used at the end, when character positins have been determined
	 *		using bin packing algorithm, and texture pages are generated to be saved
	 *		to files.
	 *
	 *	@param[in] h_dc is device context to render to
	 *	@param[in] n_raster_height is texture page height (windows render upside-down)
	 */
	void Render(HDC h_dc, int n_raster_height) const;

	/**
	 *	@brief calculates bounding box area
	 *
	 *	Calculates character bounding box area. This is useful for priorizing characters
	 *		when inserting them to texture pages (big ones first, small ones will fill
	 *		remaining gaps at the end).
	 *
	 *	@param[in] n_padding is cahracter padding in pixels
	 *
	 *	@return Returns character bitmap area in pixels square, including padding.
	 */
	inline unsigned int n_Area(int n_padding) const
	{
		return (n_bitmap_width + n_padding) * (n_bitmap_height + n_padding);
	}

	/**
	 *	@brief sets character position in texture page
	 *
	 *	Places character bitmap to position r_v_min (pixels) in texture page.
	 *
	 *	@param[in] r_v_min is desired position of upper-left corner
	 */
	inline void Place(const Vector2i &r_v_min)
	{
		v_bitmap_pos = r_v_min;
	}

	/**
	 *	@brief resolves collision
	 *
	 *	Shifts character bitmap right, so it's bounding box doesn't collide with
	 *		r_t_other's bounding box. Padding is counted in.
	 *
	 *	@param[in] r_t_other is other character, which in texture space collides
	 *		with this character
	 *	@param[in] n_padding is cahracter padding in pixels
	 */
	inline void Shift_Right(const TCharacterInfo &r_t_other, int n_padding)
	{
		v_bitmap_pos.x = r_t_other.v_bitmap_pos.x +
			r_t_other.n_bitmap_width + 1 + n_padding; // padding
	}

	/**
	 *	@brief collision detection
	 *
	 *	Collision detection in texture space, based on bounding boxes. Padding
	 *		is counted in.
	 *
	 *	@param[in] r_t_other is the other character to detect collision with
	 *	@param[in] n_padding is cahracter padding in pixels
	 *
	 *	@return Returns true in case bitmaps of this character and r_t_other
	 *		are colliding in texture space, otherwise false.
	 */
	bool b_Collide(const TCharacterInfo &r_t_other, int n_padding);

#endif // FONT_TEXTURE_CREATE_FONTS

	/**
	 *	@brief gets minimal coordinate of character bounding-box
	/*
	 *	@return Returns bottom-left bitmap corner position in texture space.
	 */
	inline Vector2i v_Min() const
	{
		return v_bitmap_pos;
	}

	/**
	 *	@brief gets maximal coordinate of character bounding-box
	 *
	 *	@param[in] n_padding is character padding, in pixels
	 *
	 *	@return Returns top-right bitmap corner position in texture space.
	 */
	inline Vector2i v_Max(int n_padding) const
	{
		return v_bitmap_pos + Vector2i(n_bitmap_width + n_padding,
			n_bitmap_height + n_padding); // padding
	}

private:
#ifdef FONT_TEXTURE_CREATE_FONTS
	static inline bool b_Disjunct(int n_min1, int n_max1, int n_min2, int n_max2)
	{ return n_max1 < n_min2 || n_min1 > n_max2; }
	static bool b_Scanline_Empty(const uint32_t *p_buffer, int n_width);
	static bool b_Column_Empty(const uint32_t *p_buffer, int n_height, int n_width);
#endif // FONT_TEXTURE_CREATE_FONTS
};

/**
 *	@brief font texture page class
 *
 *	Holds information about characters inside texture page, is able to load itself
 *		from a bitmap, or to serialize itself to a bitmap (windows GDI is required
 *		for creating font texture pages).
 */
class CFontTexturePage {
private:
	std::vector<TCharacterInfo*> m_character_list;
	TCharacterInfo m_t_info_block;
	int m_n_max_char_num;
	int m_n_width, m_n_height;
	int m_n_space_width, m_n_newline_height, m_n_char_descent, m_n_internal_leading;
	int m_n_char_padding, m_n_geom_padding;

	TCharacterInfo *m_p_character;
	// contains linear block of characters in m_character list (if constructed from bitmap)

	class CGrayBlockReader;
#ifdef FONT_TEXTURE_CREATE_FONTS
	class CGDIRenderBitmaps;
	class CSolveCollisions;
	class CGrayBlockWriter;
	template <class CBlockWriter>
	class CWriteIntLayout;
	class CSumArea;
#endif // FONT_TEXTURE_CREATE_FONTS

public:
	typedef std::vector<TCharacterInfo>::iterator tcharinfo_iterator; /**< character info iterator type */

#ifdef FONT_TEXTURE_CREATE_FONTS
	/**
	 *	@brief default constructor
	 *
	 *	Attempts to insert characters in range [p_character_begin, p_character_end), while keeping
	 *		maximal texture dimensions below or equal to n_max_size while keeping 1 pixel spacing
	 *		between characters and border and n_padding spacing between characters themselves.
	 *
	 *	@param[in] p_character_begin is iterator, pointing to the first character to be added
	 *	@param[in] p_character_end is iterator, pointing one after the last character to be
	 *		added. Formally, characters in range [p_character_begin, p_character_end) are
	 *		(attempted to be) added.
	 *	@param[in] n_char_padding is distance between character bitmaps in pixels (to avoid
	 *		characters bleeding into each other when displaying with bilinear filtering
	 *		and / or mip-mapping)
	 *	@param[in] n_geom_padding is padding between space used by character and it's bounding
	 *		box, in pixels. This space is (in contrast with n_char_padding) displayed with
	 *		character, typical use may be character bitmap post-processing, such as adding glow.
	 *	@param[in] n_space_width is width of space (' ') character, in pixels
	 *	@param[in] n_newline_height is height of characters, in pixels, including internal
	 *		and external leading and line descent
	 *	@param[in] n_line_descent is distance between baseline and bottom bounding box of
	 *		characters (characters like gjpqy), in pixels
	 *	@param[in] n_internal_leading is height of diacritic / markings above characters,
	 *		in pixels
	 *	@param[in] n_max_size is maximal texture size limit, should be power of two
	 *
	 *	@remarks Call n_Character_Num() to see how many of characters were actually added.
	 *
	 *	@note Power-of-two texture dimensions are used only.
	 */
	CFontTexturePage(tcharinfo_iterator p_character_begin,
		tcharinfo_iterator p_character_end, int n_char_padding, int n_geom_padding,
		int n_space_width, int n_newline_height, int n_line_descent,
		int n_internal_leading, int n_max_size);

	/**
	 *	@brief adds more characters
	 *
	 *	Attempts to insert characters in range [p_character_begin, p_character_end),
	 *		while using the same parameters and keeping the same constraints as set
	 *		in constructor.
	 *
	 *	@param[in] p_character_begin is iterator, pointing to the first character to be added
	 *	@param[in] p_character_end is iterator, pointing one after the last character to be
	 *		added. Formally, characters in range [p_character_begin, p_character_end) are
	 *		(attempted to be) added.
	 *
	 *	@return Returns next character to be inserted to another texture page
	 *		(p_character_begin in case no character could be fit in the page, or
	 *		p_character_end in case all the characters were placed in the page).
	 */
	tcharinfo_iterator p_AddCharacters(tcharinfo_iterator p_character_begin,
		tcharinfo_iterator p_character_end);

#endif // FONT_TEXTURE_CREATE_FONTS

	/**
	 *	@brief constructor
	 *
	 *	Loads texture page from bitmap p_bitmap, assumes grayscale layout info block
	 *		stored in bottom-left corner of bitmap. In case of success, n_Character_Num()
	 *		should return positive number of characters (font tool should never create
	 *		empty texture page), otherwise there will be no characters.
	 *
	 *	@param[in] p_bitmap is input bitmap, containg font texture page, created
	 *		by p_Create_Bitmap() (may be created by another instance of CFontTexturePage,
	 *		or loaded from a file).
	 *
	 *	@remarks Call n_Character_Num() to see if constructor succeeded.
	 */
	CFontTexturePage(const TBmp *p_bitmap);
	
	/**
	 *	@brief destructor
	 */
	~CFontTexturePage()
	{
		if(m_p_character)
			delete[] m_p_character;
	}

	/**
	 *	@brief gets width
	 *
	 *	@return Returns texture page width, in pixels.
	 */
	inline int n_Width() const
	{
		return m_n_width;
	}

	/**
	 *	@brief gets height
	 *
	 *	@return Returns texture page height, in pixels.
	 */
	inline int n_Height() const
	{
		return m_n_height;
	}

	/**
	 *	@brief gets character padding
	 *
	 *	@return Returns character padding, in pixels.
	 */
	inline int n_Char_Padding() const
	{
		return m_n_char_padding;
	}

	/**
	 *	@brief gets geometry padding
	 *
	 *	@return Returns geometry padding, in pixels.
	 */
	inline int n_Geom_Padding() const
	{
		return m_n_geom_padding;
	}

	/**
	 *	@brief gets space width
	 *
	 *	@return Returns space width (carret shift for character ' '), in pixels.
	 */
	inline int n_Space_Width() const
	{
		return m_n_space_width;
	}

	/**
	 *	@brief gets newline height
	 *
	 *	@return Returns distance between two baselines one above the other, in pixels.
	 */
	inline int n_Newline_Height() const
	{
		return m_n_newline_height;
	}

	/**
	 *	@brief gets line descent
	 *
	 *	@return Returns space required under baseline, in pixels.
	 */
	inline int n_Line_Descent() const
	{
		return m_n_char_descent;
	}

	/**
	 *	@brief gets internal leading
	 *
	 *	@return Returns space space occupied by punctation marks
	 *		(included in height), in pixels.
	 */
	inline int n_Internal_Leading() const
	{
		return m_n_internal_leading;
	}

	/**
	 *	@brief gets page capacity
	 *
	 *	@return Returns maximal number of characters that can be contained
	 *		in this texture page.
	 *
	 *	@note This value is given by size of character information block,
	 *		which is fixed once bin packing begins.
	 */
	inline int n_Max_Character_Num() const
	{
		return m_n_max_char_num;
	}

	/**
	 *	@brief gets character count
	 *
	 *	@return Returns number of characters contained in this texture page.
	 *
	 *	@note This function is also used as indicator of constructor success,
	 *		on success, it should return nonzero number of characters.
	 */
	inline int n_Character_Num() const
	{
		if(m_character_list.empty())
			return 0;
		return m_character_list.size() - 1; // without info block
	}

	/**
	 *	@brief character info access
	 *
	 *	@param[in] n_index is zero-based character index
	 *
	 *	@return Returns info for character with zero-based index n_index from this page
	 */
	inline const TCharacterInfo &t_Character(int n_index) const
	{
		return *m_character_list[n_index + 1];
	}

	/**
	 *	@brief character loop
	 *
	 *	std::for_each lookalike for looping trough all characters, contained in this
	 *		texture page. Calls t_object with each TCharacterInfo in this page as
	 *		the only argument.
	 *
	 *	@param[in] t_object is functor, implementing <tt>void operator (TCharacterInfo t_char_info)</tt>
	 *
	 *	@return Returns t_object.
	 */
	template <class CFunctionObject>
	inline CFunctionObject for_each_character(CFunctionObject t_object) const
	{
		return std::for_each(m_character_list.begin() + 1, m_character_list.end(), t_object);
	}

#ifdef FONT_TEXTURE_CREATE_FONTS

	/**
	 *	@brief bakes font texture page
	 *
	 *	Renders bitmap with characters (must be the same font as the one used to
	 *		get character parameters, or one with the same parameters, otherwise
	 *		no error occurs, but characters will miss their place in the texture
	 *		and when used for rendering text, output would be most likely
	 *		unreadable).
	 *
	 *	@param[in] h_font is GDI font object to render characters with
	 *
	 *	@return Returns a new bitmap on success, 0 on failure.
	 *
	 *	@note Caller is responsible for deleting returned bitmap.
	 */
	CGDI_Bitmap *p_Create_Bitmap(HFONT h_font) const;

#endif // FONT_TEXTURE_CREATE_FONTS
};

#ifdef FONT_TEXTURE_CREATE_FONTS

/**
 *	@brief utility for building font texture atlases
 *
 *	While CFontTexturePage can be used to build a single texture page, contating
 *		some characters, this class can be used to easily create atlas of (multiple)
 *		texture pages containing the whole character set. This is used when
 *		creating bitmap fonts by both CGLBitmapFont and the ÜberLame font texture
 *		utility.
 */
class CFontTexture_Util {
public:
	/**
	 *	@brief creates character list
	 *
	 *	Fills r_character_list with list of characters in range
	 *		[n_first_char, n_first_char + n_char_num), r_bitmap is used for
	 *		glyph rendering.
	 *
	 *	@param[in] n_first_char is the first character in desired range
	 *	@param[in] n_char_num is number of characters
	 *	@param[out] r_character_list is output for character information
	 *		(note characters are added, original contents aren't lost)
	 *	@param[in,out] r_bitmap is temp bitmap to draw characters to,
	 *		it must have correct font set
	 *
	 *	@return Returns true on success, false on failure.
	 *
	 *	@todo Add unicode support here.
	 */
	static bool Create_CharacterList(unsigned char n_first_char,
		unsigned char n_char_num, std::vector<TCharacterInfo> &r_character_list,
		CGDI_Bitmap &r_bitmap);

	/**
	 *	@brief helper object
	 *
	 *	Small function object for sorting characters descendingly by their areas with padding
	 *		(better space use efficiency if big ones are inserted first).
	 */
	class CSortCharacters {
	private:
		const int m_n_padding;

	public:
		/**
		 *	@brief default constructor
		 *
		 *	@param[in] n_padding is padding to be used to calculate character bounding boxes
		 */
		inline CSortCharacters(int n_padding = 1)
			:m_n_padding(n_padding)
		{}

		/**
		 *	@brief character bounding box area comparator
		 *
		 *	Compares area of two character bounding boxes in order to sort them
		 *		in descending order using std::sort.
		 *
		 *	@param[in] r_char_a is the first character
		 *	@param[in] r_char_b is the other character
		 *
		 *	@return Returns true if area of bounding box of r_char_a is greater than
		 *		area of bounding box of r_char_b, otherwise returns false.
		 */
		inline bool operator ()(const TCharacterInfo &r_char_a,
			const TCharacterInfo &r_char_b) const
		{
			return r_char_a.n_Area(m_n_padding) > r_char_b.n_Area(m_n_padding);
		}
	};

	/**
	 *	@brief builds all texture pages for a charset
	 *
	 *	Fills list of texture pages r_texture_list by characters from r_character_list,
	 *		while keeping n_char_padding pixels distances between characters and
	 *		having texture pages with dimensions equal or below n_max_texture_size.
	 *
	 *	@param[out] r_texture_list is list of texture pages to contain all specified characters
	 *	@param[in,out] r_character_list is list of characters to be added, character
	 *		positions will be found
	 *	@param[in] n_char_padding is distance between character bitmaps in pixels (to avoid
	 *		characters bleeding into each other when displaying with bilinear filtering
	 *		and / or mip-mapping)
	 *	@param[in] n_geom_padding is padding between space used by character and it's bounding
	 *		box, in pixels. This space is (in contrast with n_char_padding) displayed with
	 *		character, typical use may be character bitmap post-processing, such as adding glow.
	 *	@param[in] n_space_width is width of space (' ') character, in pixels
	 *	@param[in] n_newline_height is height of characters, in pixels, including internal
	 *		and external leading and line descent
	 *	@param[in] n_line_descent is distance between baseline and bottom bounding box of
	 *		characters (characters like gjpqy), in pixels
	 *	@param[in] n_internal_leading is height of diacritic / markings above characters,
	 *		in pixels
	 *	@param[in] n_max_texture_size is maximal texture size limit, should be power of two
	 *
	 *	@return Returns true on success, false on failure.
	 *
	 *	@note Characters in r_character_list should be already sorted by bounding box size
	 *		(std:sort with CSortCharacters can be used to do that).
	 */
	static bool Create_TexturePageList(std::vector<CFontTexturePage*> &r_texture_list,
		std::vector<TCharacterInfo> &r_character_list, int n_char_padding, int n_geom_padding,
		int n_max_texture_size, int n_space_width, int n_newline_height, int n_line_descent,
		int n_internal_leading);
};

#endif // FONT_TEXTURE_CREATE_FONTS

#endif // !__FONT_TEXTURE_INCLUDED
