/*
  Copyright (C) 2001 Kimmo Pekkola

  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.
*/
/*
  $Header: /home/cvsroot/Rainmeter/Library/MeterString.cpp,v 1.1.1.1 2005/07/10 18:51:06 rainy Exp $

  $Log: MeterString.cpp,v $
  Revision 1.1.1.1  2005/07/10 18:51:06  rainy
  no message

  Revision 1.11  2004/07/11 17:18:32  rainy
  Fixed width calculation.

  Revision 1.10  2004/06/05 10:55:54  rainy
  Too much changes to be listed in here...

  Revision 1.9  2003/02/10 18:12:44  rainy
  Now uses GDI+

  Revision 1.8  2002/07/01 15:32:20  rainy
  Added NumOfDecimals

  Revision 1.7  2002/04/26 18:22:02  rainy
  Added possibility to hide the meter.

  Revision 1.6  2002/03/31 09:58:53  rainy
  Added some comments

  Revision 1.5  2001/12/23 10:14:33  rainy
  Hex color values are now also supported.

  Revision 1.4  2001/10/14 07:32:32  rainy
  In error situations CError is thrown instead just a boolean value.

  Revision 1.3  2001/09/26 16:26:23  rainy
  Small adjustement to the interfaces.

  Revision 1.2  2001/09/01 12:57:33  rainy
  Added support for percentual measuring.

  Revision 1.1  2001/08/12 15:35:08  Rainy
  Inital Version

*/

#pragma warning(disable: 4786)
#pragma warning(disable: 4996)

#include "MeterString.h"
#include "Rainmeter.h"
#include "Measure.h"
#include "Error.h"

using namespace Gdiplus;

/*
** CMeterString
**
** The constructor
**
*/
CMeterString::CMeterString(CMeterWindow* meterWindow) : CMeter(meterWindow)
{
	m_Color = RGB(255, 255, 255);
	m_AutoScale = true;
	m_Align = ALIGN_LEFT;
	m_Font = NULL;
	m_FontFamily = NULL;
	m_Style = NORMAL;
	m_FontSize = 10;
	m_Scale = 1.0;
	m_NoDecimals = true;
	m_Percentual = true;
	m_AntiAlias = false;
	m_ClipString = false;
	m_NumOfDecimals = -1;
	m_DimensionsDefined = false;
	m_Angle = 0.0;
}

/*
** ~CMeterString
**
** The destructor
**
*/
CMeterString::~CMeterString()
{
	if(m_Font) delete m_Font;
	if(m_FontFamily) delete m_FontFamily;
}

/*
** GetX
**
** Returns the X-coordinate of the meter
**
*/
int CMeterString::GetX(bool abs)
{
	int x = CMeter::GetX();

	if (!abs) 
	{
		switch(m_Align)
		{
		case ALIGN_CENTER:
			x = x - (m_W / 2);
			break;

		case ALIGN_RIGHT:
			x -= m_W;
			break;

		case ALIGN_LEFT:
			// This is already correct
			break;
		}
	}

	return x;
}


/*
** Initialize
**
** Create the font that is used to draw the text.
**
*/
void CMeterString::Initialize()
{
	CMeter::Initialize();

	m_FontFamily = new FontFamily(m_FontFace.c_str());
	Status status = m_FontFamily->GetLastStatus();
	if(Ok != status)
	{
		delete m_FontFamily;
		m_FontFamily = NULL;
	}

	FontStyle style = FontStyleRegular;

	switch(m_Style)
	{
	case ITALIC:
		style = FontStyleItalic;
		break;

	case BOLD:
		style = FontStyleBold;
		break;

	case BOLDITALIC:
		style = FontStyleBoldItalic;
		break;
	}

	// Adjust the font size with screen DPI
	HDC dc = GetDC(GetDesktopWindow());
	int dpi = GetDeviceCaps(dc, LOGPIXELSX);
	ReleaseDC(GetDesktopWindow(), dc);

	REAL size = (REAL)m_FontSize * (96.0 / (REAL)dpi);

	if (m_FontFamily)
	{
		m_Font = new Gdiplus::Font(m_FontFamily, size, style);
	}
	else
	{
		m_Font = new Gdiplus::Font(FontFamily::GenericSansSerif(), size, style);
	}

	status = m_Font->GetLastStatus();
	if(Ok != status)
	{
	    throw CError(std::wstring(L"Unable to create font: ") + m_FontFace, __LINE__, __FILE__);
	}
}

/*
** ReadConfig
**
** Read the meter-specific configs from the ini-file.
**
*/
void CMeterString::ReadConfig(const WCHAR* section)
{
	WCHAR tmpName[256];

	// Read common configs
	CMeter::ReadConfig(section);

	CConfigParser& parser = m_MeterWindow->GetParser();

	// Check for extra measures
	int i = 2;
	bool loop = true;
	do 
	{
		swprintf(tmpName, L"MeasureName%i", i);
		std::wstring measure = parser.ReadString(section, tmpName, L"");
		if (!measure.empty())
		{
			m_MeasureNames.push_back(measure);
		}
		else
		{
			loop = false;
		}
		i++;
	} while(loop);

	m_Color = parser.ReadColor(section, L"FontColor", Color::Black);

	m_Prefix = parser.ReadString(section, L"Prefix", L"");
	m_Postfix = parser.ReadString(section, L"Postfix", L"");
	m_Text = parser.ReadString(section, L"Text", L"");

	m_AntiAlias = 0!=parser.ReadInt(section, L"AntiAlias", 0);
	m_Percentual = 0!=parser.ReadInt(section, L"Percentual", 0);
	m_AutoScale = 0!=parser.ReadInt(section, L"AutoScale", 0);
	m_ClipString = 0!=parser.ReadInt(section, L"ClipString", 0);

	m_FontSize = parser.ReadInt(section, L"FontSize", 10);
	m_NumOfDecimals = parser.ReadInt(section, L"NumOfDecimals", -1);

	m_Angle = parser.ReadFloat(section, L"Angle", 0.0);

	std::wstring scale;
	scale = parser.ReadString(section, L"Scale", L"1");

	if (wcschr(scale.c_str(), '.') == NULL)
	{
		m_NoDecimals = true;
	}
	else
	{
		m_NoDecimals = false;
	}
	m_Scale = wcstod(scale.c_str(), NULL);

	m_FontFace = parser.ReadString(section, L"FontFace", L"Arial");

	std::wstring align;
	align = parser.ReadString(section, L"StringAlign", L"LEFT");

	if(_wcsicmp(align.c_str(), L"LEFT") == 0)
	{
		m_Align = ALIGN_LEFT;
	}
	else if(_wcsicmp(align.c_str(), L"RIGHT") == 0)
	{
		m_Align = ALIGN_RIGHT;
	}
	else if(_wcsicmp(align.c_str(), L"CENTER") == 0)
	{
		m_Align = ALIGN_CENTER;
	}
	else
	{
        throw CError(std::wstring(L"No such StringAlign: ") + align, __LINE__, __FILE__);
	}

	std::wstring style;
	style = parser.ReadString(section, L"StringStyle", L"NORMAL");

	if(_wcsicmp(style.c_str(), L"NORMAL") == 0)
	{
		m_Style = NORMAL;
	}
	else if(_wcsicmp(style.c_str(), L"BOLD") == 0)
	{
		m_Style = BOLD;
	}
	else if(_wcsicmp(style.c_str(), L"ITALIC") == 0)
	{
		m_Style = ITALIC;
	}
	else if(_wcsicmp(style.c_str(), L"BOLDITALIC") == 0)
	{
		m_Style = BOLDITALIC;
	}
	else
	{
        throw CError(std::wstring(L"No such StringStyle: ") + style, __LINE__, __FILE__);
	}

	if (-1 != parser.ReadInt(section, L"W", -1) && -1 != parser.ReadInt(section, L"H", -1))
	{
		m_DimensionsDefined = true;
	}
}

/*
** Update
**
** Updates the value(s) from the measures.
**
*/
bool CMeterString::Update()
{
	if (CMeter::Update())
	{
		std::vector<std::wstring> stringValues;

		int decimals = (m_NoDecimals && !m_AutoScale) ? 0 : 1;
		if (m_NumOfDecimals != -1) decimals = m_NumOfDecimals;

		if (m_Measure) stringValues.push_back(m_Measure->GetStringValue(m_AutoScale, m_Scale, decimals, m_Percentual));

		// Get the values for the other measures
		for (int i = 0; i < m_Measures.size(); i++)
		{
			stringValues.push_back(m_Measures[i]->GetStringValue(m_AutoScale, m_Scale, decimals, m_Percentual));
		}

		// Create the text
		m_String = m_Prefix;
		if (m_Text.empty())
		{
			if (stringValues.size() > 0)
			{
				m_String += stringValues[0]; 
			}
		}
		else
		{
			WCHAR buffer[256];
			// Create the actual text (i.e. replace %1, %2, .. with the measure texts)
			std::wstring tmpText = m_Text;

			for (int i = 0; i < stringValues.size(); i++)
			{
				wsprintf(buffer, L"%%%i", i + 1);

				size_t start = 0;
				size_t pos = std::wstring::npos;

				do 
				{
					pos = tmpText.find(buffer, start);
					if (pos != std::wstring::npos)
					{
						tmpText.replace(tmpText.begin() + pos, tmpText.begin() + pos + wcslen(buffer), stringValues[i]);
						start = pos + stringValues[i].length();
					}
				} while(pos != std::wstring::npos);
			}

			m_String += tmpText;
		}
		m_String += m_Postfix;

		if (!m_DimensionsDefined)
		{
			// Calculate the text size
			RectF rect;
			DrawString(&rect);
			m_W = (int)rect.Width;
			m_H = (int)rect.Height;
		}

		return true;
	}
	return false;
}

/*
** Draw
**
** Draws the meter on the double buffer
**
*/
bool CMeterString::Draw()
{
	if(!CMeter::Draw()) return false;

	return DrawString(NULL);
}

/*
** DrawString
**
** Draws the string or calculates it's size
**
*/
bool CMeterString::DrawString(RectF* rect)
{
	Graphics graphics(m_MeterWindow->GetDoubleBuffer());
	StringFormat stringFormat;
	
	if (m_AntiAlias)
	{
		graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
	}
	else
	{
		graphics.SetTextRenderingHint(TextRenderingHintSingleBitPerPixelGridFit);
	}

	switch(m_Align)
	{
	case ALIGN_CENTER:
		stringFormat.SetAlignment(StringAlignmentCenter);
		break;

	case ALIGN_RIGHT:
		stringFormat.SetAlignment(StringAlignmentFar);
		break;

	case ALIGN_LEFT:
		stringFormat.SetAlignment(StringAlignmentNear);
		break;
	}

	int x = GetX();
	int y = GetY();

	if (rect)
	{
		PointF pos(x, y);
		graphics.MeasureString(m_String.c_str(), -1, m_Font, pos, rect);
	}
	else
	{
		RectF rc((REAL)x, (REAL)y, (REAL)m_W, (REAL)m_H);
		SolidBrush solidBrush(m_Color);

		if (m_ClipString)
		{
			stringFormat.SetTrimming(StringTrimmingEllipsisCharacter);
		}
		else
		{
			stringFormat.SetTrimming(StringTrimmingNone);
			stringFormat.SetFormatFlags(StringFormatFlagsNoClip | StringFormatFlagsNoWrap);
		}

		double angle = m_Angle * 180.0f / 3.14159265f;		// Convert to degrees
		graphics.TranslateTransform(CMeter::GetX(), y);
		graphics.RotateTransform(angle);
		graphics.TranslateTransform(-CMeter::GetX(), -y);

		graphics.DrawString(m_String.c_str(), -1, m_Font, rc, &stringFormat, &solidBrush);
	}

	return true;
}

/*
** BindMeasure
**
** Overridden method. The string meters need not to be bound on anything
**
*/
void CMeterString::BindMeasure(std::list<CMeasure*>& measures)
{
	if (m_MeasureName.empty()) return;	// Allow NULL measure binding

	CMeter::BindMeasure(measures);

	std::vector<std::wstring>::iterator j = m_MeasureNames.begin();
	for (; j != m_MeasureNames.end(); j++)
	{
		// Go through the list and check it there is a secondary measures for us
		std::list<CMeasure*>::iterator i = measures.begin();
		for( ; i != measures.end(); i++)
		{
			if(_wcsicmp((*i)->GetName(), (*j).c_str()) == 0)
			{
				m_Measures.push_back(*i);
				break;
			}
		}

		if (i == measures.end())
		{
	        throw CError(std::wstring(L"The meter [") + m_Name + L"] cannot be bound with [" + (*j) + L"]!", __LINE__, __FILE__);
		}
	}
}
