/*
 *  ppui/Button.cpp
 *
 *  Copyright 2009 Peter Barth
 *
 *  This file is part of Milkytracker.
 *
 *  Milkytracker 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  Milkytracker 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 Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "Button.h"
#include "GraphicsAbstract.h"
#include "Event.h"
#include "Screen.h"
#include "Font.h"
#include "PPUIConfig.h"

PPButton::PPButton(pp_int32 id, PPScreen* parentScreen, EventListenerInterface* eventListener, 
				   const PPPoint& location, const PPSize& size, 
				   bool border/*= true*/, 
				   bool clickable/*= true*/, 
				   bool update/*=true*/) :
	PPControl(id, parentScreen, eventListener, location, size),
	color(&PPUIConfig::getInstance()->getColor(PPUIConfig::ColorDefaultButton)),
	textColor(&PPUIConfig::getInstance()->getColor(PPUIConfig::ColorDefaultButtonText)),
	border(border),
	clickable(clickable),
	update(update),
	verticalText(false),
	flat(false),
	autoSizeFont(true),
	offset(0,0),
	invertShading(false),
	lMouseDown(false), rMouseDown(false)
{
	// default colors
	pressed = false;
	
	font = PPFont::getFont(PPFont::FONT_SYSTEM);
}

PPButton::~PPButton()
{
}

void PPButton::paint(PPGraphicsAbstract* g)
{
	if (!isVisible())
		return;

	PPPoint location = this->location;

	g->setRect(location.x, location.y, location.x + size.width, location.y + size.height);

	g->setColor(*color);

	//g->fill();
	{
		PPColor nsdColor = *color, nsbColor = *color;

		if (!pressed)
		{
			// adjust not so dark color
			nsdColor.scaleFixed(flat ? 65536 : 40000);
			
			// adjust bright color
			nsbColor.scaleFixed(flat ? 65536 : 80000);
		}
		else
		{
			// adjust not so dark color
			nsdColor.scaleFixed(flat ? 65536 : 30000);
			
			// adjust bright color
			nsbColor.scaleFixed(flat ? 65536 : 60000);
		}
		
		g->fillVerticalShaded(nsbColor, nsdColor, invertShading);

	}

	PPColor bColor = *color;

	g->setFont(font);

	if (!pressed)
	{			
		// adjust bright color
		bColor.scaleFixed(87163);
		
		g->setColor(bColor);
		
		g->drawHLine(location.x, location.x + size.width, location.y);
		g->drawVLine(location.y, location.y + size.height, location.x);

		// adjust dark color
		bColor = *color;
		bColor.scaleFixed(20000);

		g->setColor(bColor);
		
		g->drawHLine(location.x, location.x + size.width, location.y + size.height - 1);
		g->drawVLine(location.y, location.y + size.height, location.x + size.width - 1);
		
		if (text)
		{
			PPColor tColor = *textColor;
			if (!clickable || !enabled)
			{
				tColor.r = (tColor.r + color->r)>>1;
				tColor.g = (tColor.g + color->g)>>1;
				tColor.b = (tColor.b + color->b)>>1;
			}
			
			g->setColor(tColor);

			if (!verticalText)
			{
				pp_int32 cx = (size.width>>1)-(font->getStrWidth(text)>>1) + location.x;
				pp_int32 cy = (size.height>>1)-(font->getCharHeight()>>1) + location.y + 1;
				g->drawString(text, cx+offset.x, cy+offset.y);
			}
			else
			{
				pp_int32 cx = (size.width>>1)-(font->getCharWidth()>>1) + location.x;
				pp_int32 cy = (size.height>>1)-((font->getCharHeight()*text.length())>>1) + location.y + 1;
				g->drawStringVertical(text, cx+offset.x, cy+offset.y);
			}
		}
	}
	else
	{
		// adjust dark color
		bColor = *color;
		bColor.scaleFixed(32768);
		
		g->setColor(bColor);
		
		g->drawHLine(location.x, location.x + size.width, location.y);
		g->drawVLine(location.y, location.y + size.height, location.x);

		g->setColor(bColor.r>>1,bColor.g>>1,bColor.b>>1);
		g->drawHLine(location.x, location.x + size.width, location.y + size.height - 1);
		g->drawVLine(location.y, location.y + size.height, location.x + size.width - 1);
		
		//g->drawHLine(location.x, location.x + size.width, location.y + size.height - 1);
		//g->drawVLine(location.y, location.y + size.height, location.x + size.width - 1);
		
		if (text)
		{
			g->setColor(*textColor);

			if (!verticalText)
			{
				pp_int32 cx = (size.width>>1)-(font->getStrWidth(text)>>1) + location.x + 1;
				pp_int32 cy = (size.height>>1)-(font->getCharHeight()>>1) + location.y + 2;
				g->drawString(text, cx+offset.x, cy+offset.y);
			}
			else
			{
				pp_int32 cx = (size.width>>1)-(font->getCharWidth()>>1) + location.x + 1;
				pp_int32 cy = (size.height>>1)-((font->getCharHeight()*text.length())>>1) + location.y + 2;
				g->drawStringVertical(text, cx+offset.x, cy+offset.y);
			}
		}
	}

	if (border && enabled)
	{
		bColor.r = bColor.g = bColor.b = 0;
		
		g->setColor(bColor);
	
		g->setRect(location.x-1, location.y-1, location.x + size.width+1, location.y + size.height+1);
		
		g->drawHLine(location.x, location.x + size.width, location.y-1);
		g->drawVLine(location.y, location.y + size.height, location.x-1);
		g->drawHLine(location.x, location.x + size.width, location.y + size.height);
		g->drawVLine(location.y, location.y + size.height, location.x + size.width);
	}

}

pp_int32 PPButton::dispatchEvent(PPEvent* event)
{ 
	if (eventListener == NULL)
		return -1;

	//if (!visible)
	//	return 0;

	switch (event->getID())
	{
		case eLMouseDrag:
		case eRMouseDrag:
		{
			PPPoint p = *reinterpret_cast<const PPPoint*>(event->getDataPtr());
			if (!hit(p) && clickable && pressed && update)
			{
				pressed = false;
				parentScreen->paintControl(this);
			}
			else if (hit(p) && clickable && !pressed && update)
			{
				pressed = true;
				parentScreen->paintControl(this);
			}
		}
		break;

		case eLMouseDown:
			handleButtonPress(lMouseDown, rMouseDown);
			break;

		case eLMouseUp:
			handleButtonRelease(lMouseDown, rMouseDown, event, eCommand);
			break;
			
		case eRMouseDown:
			handleButtonPress(rMouseDown, lMouseDown);
			break;

		case eRMouseUp:
			handleButtonRelease(rMouseDown, lMouseDown, event, eCommandRight);
			break;

		case eLMouseRepeat:
		case eRMouseRepeat:
			if (clickable && pressed)
			{
				PPEvent e(eCommandRepeat);
				eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e); 
			}
			break;
	}

	return eventListener->handleEvent(reinterpret_cast<PPObject*>(this), event); 
}

void PPButton::setText(const PPString& text) 
{ 
	bool lastCharIsPeriod = text.length() ? (text[text.length()-1] == '.') : false;
	
	this->text = text; 
	
	// Fall back to tiny font if string doesn't fit with current font
	if (autoSizeFont &&
		font != PPFont::getFont(PPFont::FONT_TINY) &&
		!verticalText && 
		((signed)font->getStrWidth(text) > size.width - (lastCharIsPeriod ? -6 : 2) ||
		 (signed)font->getCharHeight() > size.height))
	{
		font = PPFont::getFont(PPFont::FONT_TINY);
	}
}

void PPButton::handleButtonPress(bool& lMouseDown, bool& rMouseDown)
{
	if (rMouseDown)
	{
		lMouseDown = false;
		pressed = false;
		if (update)
			parentScreen->paintControl(this);
		return;
	}

	lMouseDown = true;
	if (clickable)
	{
		pressed = true;
		if (update)
			parentScreen->paintControl(this);
	}
}

void PPButton::handleButtonRelease(bool& lMouseDown, bool& rMouseDown, PPEvent* event, EEventDescriptor postEvent)
{
	lMouseDown = false;
	if (clickable && !rMouseDown && pressed)
	{
		pressed = false;
		
		if (update)
			parentScreen->paintControl(this);			
		
		if (hit(*reinterpret_cast<const PPPoint*>(event->getDataPtr())))
		{
			PPEvent e(postEvent);
			eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e); 
		}
		
	}
}

