#include <windows.h>
#include "uifontstring.h"

// ----------------------------------------------------------------------------------
inline bool IsNumeric( WCHAR ch, bool isSymbol )
{
	if ( ch >= L'0' && ch <= '9' )
		return true;

	if ( isSymbol )
	{
		switch ( ch )
		{
		case L'+':
		case L'-':
		case L'*':
		case L'/':
		case L',':
		case L'.':
			return true;			
		}
	}		

	return false;
}

// ----------------------------------------------------------------------------------
CUiFontString::CUiFontString(const char* name, CUiLayout* pParent, CUi* pUi)
	: CUiLayout(name, pParent, pUi)
{
	m_uiType			= eUiType_FontString;

	m_text				= NULL;
	m_bytes				= 255;	

	m_pFontType			= NULL;
	m_pDotChar			= NULL;
	m_pShadow			= NULL;
	m_spacing			= 0.0f;
	m_justifyVType		= eJustifyV_Middle;
	m_justifyHType		= eJustifyH_Center;
	m_internalChangedSize = false;

	m_charData.clear();
	m_strData.clear();
}

// ----------------------------------------------------------------------------------
CUiFontString::~CUiFontString()
{	
	UI_SAFE_DELETE_ARRAY(m_text);

	ClearFontShadow();

	m_charData.clear();
	m_strData.clear();
}

// ----------------------------------------------------------------------------------
void CUiFontString::Render(float alpha)
{	
	Render(alpha, m_position.m_x, m_position.m_y);
}

// ----------------------------------------------------------------------------------
void CUiFontString::Render(float alpha, float x, float y)
{
	if (m_strData.empty() || !m_isVisible || m_pFontType == NULL)
		return;	

	float sx, sy, charWidth, dotWidth;
	float scale = GetRealScale();
	float fontHeight = m_pFontType->GetRealHeight() * scale;
	float spacing = m_spacing * scale;
	CUiSize size = m_size * scale;
	int maxLine = min( (int)m_strData.size(), (int)(size.m_y / fontHeight) );

	sy = y;

	// 跟據對齊方式,取得Y軸座標值
	switch ( m_justifyVType )
	{
	case eJustifyV_Top:
		break;

	case eJustifyV_Middle:
		sy += (size.m_y - fontHeight * maxLine) / 2.0f;
		break;

	case eJustifyV_Bottom:
		sy += size.m_y - fontHeight * maxLine;
		break;
	}

	// 開始繪出字元
	CRuFontChar::PaintBegin();

	for ( int i = 0; i < maxLine; i++ )
	{	
		int begin = m_strData[i].begin;
		int end = m_strData[i].end;
		float w = m_strData[i].width * scale;
		bool isDrawDot = false;
		sx = x;
		
		// 若已經是顯示的最後一行並且還有資料未顯示
		if ( i + 1 == maxLine && maxLine < (int)m_strData.size() )
		{
			// 在字串最後繪出"..."
			isDrawDot = true;

			// 找到最後顯示字元
			StringInfo& next = m_strData[i+1];
			for (int k = next.begin; k < next.end; k++)
			{
				CRuFontChar* pChar = m_charData[k].pChar;
				charWidth = pChar->GetXSize() * scale;
				if ( w + charWidth > size.m_x )
					break;
				else
				{
					w += charWidth + spacing;
					end = k + 1;
				}
			}

			// 調整"..."加入後大小
			dotWidth = m_pDotChar->GetXSize() * scale;
			w += dotWidth * 3.0f;
			while ( w > size.m_x )
			{
				charWidth = m_charData[end].pChar->GetXSize() * scale;
				w -= charWidth + spacing;
				end--;
				if ( end < begin )
					break;
			}
		}

		// 跟據對齊方式,取得X軸座標值
		switch ( m_justifyHType )
		{
		case eJustifyH_Left:
			break;

		case eJustifyH_Center:
			sx += (size.m_x - w) / 2;
			break;

		case eJustifyH_Right:
			sx += size.m_x - w;
			break;
		}

		// paint 
		PaintChar(sx, sy, begin, end, alpha, scale);
		if (isDrawDot)
		{
			DWORD color = m_color.GetColor();
			sx += w - dotWidth * 3.0f;
			for (int i = 0; i < 3; i++)
			{
				m_pDotChar->Paint(sx, sy, scale, color, m_pShadow);
				sx += dotWidth;
			}
		}

		sy += fontHeight;
	}
}

// ----------------------------------------------------------------------------------
void CUiFontString::Render(float alpha, float x, float y, int begin, int end)
{
	if ( m_charData.empty() )
		return;

	begin = min( max(0, begin), (int)m_charData.size() );
	end = min( max(0, end), (int)m_charData.size() );

	// 開始繪出字元
	CRuFontChar::PaintBegin();

	// paint 
	PaintChar(x, y, min(begin, end), max(begin, end), alpha, GetRealScale());
}

// ----------------------------------------------------------------------------------
void CUiFontString::SetAnchorSize(CUiSize size)
{
	if ( m_anchorSize != size )
	{
		m_anchorSize = size;
		if ( m_anchorSize.m_x == 0.0f || m_anchorSize.m_y == 0.0f )
			ModifySize();
		else
			SetSize(m_anchorSize);
	}
}

// ----------------------------------------------------------------------------------
void CUiFontString::SetSize(CUiSize& size, bool isReAnchor)
{
	if (m_size != size)
	{
		CUiLayout::SetSize(size);
		if (!m_internalChangedSize)
			GetLineInfo();
	}
}

// ----------------------------------------------------------------------------------
void CUiFontString::SetFontType(CRuFontType* pFontType)
{
	m_pDotChar = NULL;
	m_pFontType = pFontType;

	SetText(m_text);
	if (m_pFontType)
		m_pDotChar = CRuFontChar::GetFontChar(m_pFontType, L'.');
}

// ----------------------------------------------------------------------------------
void CUiFontString::SetBytes(int bytes)
{
	if (m_bytes != bytes)
	{
		WCHAR* tempStr = new WCHAR[bytes+1];

		if ( m_text )
		{
			wcsncpy( tempStr, m_text, min(m_bytes, bytes) );
			tempStr[bytes] = 0;
			delete [] m_text;
		}
		m_text = tempStr;
		m_bytes = bytes;
	}
}

// ----------------------------------------------------------------------------------
void CUiFontString::SetColor(float r, float g, float b)
{
	if ( m_color.m_r != r || m_color.m_g != g || m_color.m_b != b )
	{
		CUiLayout::SetColor(r, g, b);
		DWORD ch_color = m_color.GetColor() | 0xFF000000;
		for ( vector<CharInfo>::iterator iter = m_charData.begin(); iter != m_charData.end(); iter++ )
		{
			(*iter).color = ch_color;
		}
	}
}

// ----------------------------------------------------------------------------------
void CUiFontString::SetText(const WCHAR* textStr, bool isParse)
{
	if (textStr)
	{
		if (m_text == NULL)
			m_text = new WCHAR[m_bytes+1];
		else if (wcsncmp(textStr, m_text, m_bytes) == 0)
			return;

		wcsncpy(m_text, textStr, m_bytes);
		m_text[m_bytes] = 0;
		
		// 重新取得顯示字元
		GetFontChar(m_text, isParse);

		// 若顯示面積跟據字串大小而做變動
		ModifySize();

		// 取得每一行秀出文字
		GetLineInfo();
	}
}

// ----------------------------------------------------------------------------------
void CUiFontString::SetTextA(const char* text, bool isParse)
{
	WCHAR* tmp = TransToWChar(text);
	if (tmp)
	{
		SetText(tmp, isParse);
		delete [] tmp;
	}
}

// ----------------------------------------------------------------------------------
void CUiFontString::SetFontShadow(RuFontShadow* shadow)
{
	if (shadow)
	{
		if (m_pShadow == NULL)
			m_pShadow = new RuFontShadow;
		*m_pShadow = *shadow;
	}
	else
		ClearFontShadow();
}

// ----------------------------------------------------------------------------------
void CUiFontString::ClearFontShadow()
{
	UI_SAFE_DELETE(m_pShadow);
}

// ----------------------------------------------------------------------------------
void CUiFontString::PaintChar(float x, float y, int begin, int end, float alpha, float scale)
{
	float sx = x;
	for ( int i = begin; i < end; i++ )
	{
		CUiRGBA color;
		color = m_charData[i].color;
		color.SetAlphaScale(alpha);
		m_charData[i].pChar->Paint(sx, y, scale, color.m_value, m_pShadow);
		sx += (m_charData[i].pChar->GetXSize() + m_spacing) * scale;
	}
}

// ----------------------------------------------------------------------------------
float CUiFontString::Separate(int index, int &end)
{
	WCHAR ch		= m_charData[index].pChar->GetChar();
	float width		= m_charData[index].pChar->GetXSize();

	index++;
	if ( ch != L' ' && ch < 0xFF )
	{
		// 文字字元,包函英文字母,控制字元以及特殊字元
		if ( IsCharAlphaW( ch ) )
		{
			while ( true )
			{
				if ( index >= (int)m_charData.size() )
					break;
				else if ( !IsCharAlphaW( m_charData[index].pChar->GetChar() ) )
					break;					
				width += m_charData[index].pChar->GetXSize() + m_spacing;
				index++;
			}
		}
		// 數字
		else if ( IsNumeric( ch, false ) )
		{
			while ( true )
			{
				if ( index >= (int)m_charData.size() )
					break;
				else if ( !IsNumeric(m_charData[index].pChar->GetChar() , true) )
					break;
				width += m_charData[index].pChar->GetXSize() + m_spacing;
				index++;
			}
		}
	}

	end = index;
	return width;
}

// ----------------------------------------------------------------------------------
float CUiFontString::CaretToPos(int begin, int caret)
{
	if ( m_charData.empty() )
		return 0;

	float pos = 0.0f;
	float scale = GetRealScale();

	begin = min( max(begin, 0), (int)m_charData.size() );
	caret = min( max(caret, 0), (int)m_charData.size() );

	for (int i = begin; i < caret; i++)
		pos += (m_charData[i].pChar->GetXSize() + m_spacing) * scale;

	return pos;
}

// ----------------------------------------------------------------------------------
int CUiFontString::PosToCaret(int begin, float width, bool& trail)
{
	float scale = GetRealScale();
	float charWidth, spacing;
	int pos = max(begin, 0);

	spacing = m_spacing * scale;
	for ( ; pos < (int)m_charData.size(); pos++ )
	{
		charWidth = m_charData[pos].pChar->GetXSize() * scale;
		if ( charWidth > width )
		{
			trail = ((width / 2.0f) > charWidth);
			break;
		}
		width -= charWidth + spacing;
	}
	return pos;
}

// ----------------------------------------------------------------------------------
void CUiFontString::GetFontChar(const WCHAR* textStr, bool isParse, DWORD color)
{
	if (textStr && m_pFontType)
	{
		m_charData.clear();
		if (color == 0)
			color = m_color.GetColor();

		CharInfo charInfo;
		DWORD tmp_color = color;
		WCHAR* currText = m_text;
		while ( currText && *currText )
		{
			bool isDraw = true;
			if ( isParse && *currText == L'|' )
			{
				switch (currText[1])
				{
				case L'c':
					if (wcslen(currText) > 9)
					{
						isDraw = false;
						tmp_color = HexStrToInt(currText + 2);
						currText += 10;
					}
					break;

				case L'r':
					isDraw = false;
					tmp_color = color;
					currText += 2;
					break;
				}
			}

			if ( isDraw )
			{
				if ( (charInfo.pChar = CRuFontChar::GetFontChar(m_pFontType, *currText)) != NULL )
				{
					charInfo.color = tmp_color;
					m_charData.push_back( charInfo );
				}
				currText++;
			}
		}
	}
}

// ----------------------------------------------------------------------------------
void CUiFontString::GetLineInfo()
{
	StringInfo strInfo;
	float scale		= GetRealScale();
	int index		= 0;
	strInfo.begin	= index;
	strInfo.end		= index;
	strInfo.width	= 0.0f;

	m_strData.clear();
	while ( index < (int)m_charData.size() )
	{
		if ( m_charData[index].pChar->GetChar() == L'\n' )
		{				
			m_strData.push_back( strInfo );
			strInfo.begin	= ++index;
			strInfo.end		= index;
			strInfo.width	= 0.0f;
		}
		else
		{
			int start = index;
			int end;
			float w = Separate(start, end);
			
			// 兩字串兩加寬度大於元件寬度,則需將後半字串做拆解處理
			if ( strInfo.width + w > m_size.m_x )
			{
				if ( strInfo.end != strInfo.begin )
				{
					m_strData.push_back( strInfo );
					strInfo.begin	= index;
					strInfo.end		= index;
					strInfo.width	= 0.0f;
				}
				
				float charWidth;
				for ( int i = start; i < end; i++ )
				{
					charWidth = m_charData[i].pChar->GetXSize();
					if (strInfo.width + charWidth < m_size.m_x)
					{
						strInfo.width += charWidth + m_spacing;
						strInfo.end = i + 1;
					}
					else
					{
						m_strData.push_back( strInfo );
						strInfo.begin	= i;
						strInfo.end		= i;
						strInfo.width	= charWidth;
					}
				}
			}
			else
			{
				strInfo.end		= end;
				strInfo.width	+= w + m_spacing;
			}
			index = end;
		}
	}

	if ( strInfo.end != strInfo.begin )
		m_strData.push_back( strInfo );
}

// ----------------------------------------------------------------------------------
void CUiFontString::ModifySize()
{
	if (m_pFontType == NULL)
		return;
	
	if ( m_anchorSize.m_x == 0.0f || m_anchorSize.m_y == 0.0f )
	{
		CUiSize size = m_anchorSize;

		// 調整寬度
		if ( size.m_x == 0.0f )
		{
			float x = 0.0f;
			int index = 0;
			for ( ; index < (int)m_charData.size(); index++ )
			{
				CRuFontChar* pChar = m_charData[index].pChar;
				if (pChar->GetChar() == L'\n')
				{
					if ( x > size.m_x )
						size.m_x = x;
					x = 0.0f;
				}
				else
					x += pChar->GetXSize() + m_spacing;
			}

			if ( x > size.m_x )
				size.m_x = x;
		}

		// 調整高度
		if ( size.m_y == 0.0f )
		{
			float x = 0.0f;
			int index = 0;
			while ( index < (int)m_charData.size() )
			{
				CRuFontChar* pChar = m_charData[index].pChar;
				if ( pChar->GetChar() == L'\n' )
				{
					index++;
					x = 0.0f;
					size.m_y += m_pFontType->GetRealHeight();
				}
				else
				{
					int end;
					float w = Separate(index, end);
					if ( x + w > size.m_x )
					{
						x = w;
						size.m_y += m_pFontType->GetRealHeight();
					}
					else
						x += w + m_spacing;
					index = end;
				}
			}

			if ( x != 0.0f )
				size.m_y += m_pFontType->GetRealHeight();
		}

		m_internalChangedSize = true;
		SetSize(size);
		m_internalChangedSize = false;
	}
}

// ----------------------------------------------------------------------------------
int CUiFontString::HexStrToInt(const WCHAR *valueStr)
{
	int value = 0;
	if ( valueStr )
	{
		int len = wcslen( valueStr );
		if ( len > 8 )
			len = 8;
		for ( int i = 0; i < len; i++ )
		{
			int lo = len - i - 1;
			if ( valueStr[lo] >= L'0' && valueStr[lo] <= L'9' )
				value += (valueStr[lo] - L'0') * (int)pow(16.0f, i);
			else if ( valueStr[lo] >= L'A' && valueStr[lo] <= L'F' )
				value += (valueStr[lo] - L'A' + 10) * (int)pow(16.0f, i);
			else if ( valueStr[lo] >= L'a' && valueStr[lo] <= L'f' )
				value += (valueStr[lo] - L'a' + 10) * (int)pow(16.0f, i);
		}
	}
	return value;
}