//
// File: Checkbox.cpp
// Created by: Netzzwerg
//
/*****
*
*  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
*
*****/

#include "gui/Container.h"
#include "gui/GUIHandler.h"
#include "gui/Scrollbar.h"
#include "Debug.h"
#include "Config.h"

using namespace std;

namespace GUI {

static int ScrollLeftUp(Control *control)
{
	Scrollbar *scroll = dynamic_cast<Scrollbar*>(control->GetParent());
	if (!scroll) return false;

	scroll->SetPos(scroll->GetPos()-1);

	return true;
}

static int ScrollRightDown(Control *control)
{
	Scrollbar *scroll = dynamic_cast<Scrollbar*>(control->GetParent());
	if (!scroll) return false;
	
	scroll->SetPos(scroll->GetPos()+1);

	return true;
}

Scrollbar::Scrollbar(int x, int y, int w, int h)
{
	memset(m_scroll_gump, 0, sizeof(m_scroll_gump));
	SetFlag (GUMPFLAG_MOVABLE, false);

	SetPosition(x,y);
	SetSize(w,h);

	m_vertical = false;
	m_use_arrow_button = false;

	m_min = 0, m_max = 100;
	m_pos = m_prev_pos = 0;

	m_ltup.SetDirectOnClick(true);
	m_rtdn.SetDirectOnClick(true);

	m_drag = false;

	control_type = CONTROLTYPE_SCROLLBAR;

	m_onchange_message.type = MESSAGE_NONE;
	m_callback_OnChange = NULL;
	m_direct_onchange = false;

	m_ltup.OnClick(ScrollLeftUp);
	m_rtdn.OnClick(ScrollRightDown);

	m_ltup.SetParent(this);
	m_rtdn.SetParent(this);
}

Scrollbar::~Scrollbar()
{
}

void Scrollbar::SetGump (int type, int gump)
{
	if (type >= 0 && type < SCROLL_NUM_PART)
		m_scroll_gump[type] = gump;

	if (type >= SCROLL_LTUP_NORMAL && type <= SCROLL_LTUP_PRESSED)
		m_ltup.SetGump(type - SCROLL_LTUP_NORMAL, gump);
	else
	if (type >= SCROLL_RTDN_NORMAL && type <= SCROLL_RTDN_PRESSED)
		m_rtdn.SetGump(type - SCROLL_RTDN_NORMAL, gump);
}

int Scrollbar::GetGump (int type)
{
	if (type >= 0 && type < SCROLL_NUM_PART)
		return m_scroll_gump[type];
	return 0;
}

int  Scrollbar::GetPos() const 
{ 
	return m_pos; 
}

void Scrollbar::SetPos(int pos, bool fire_evnet) 
{ 
	int prev_pos = m_pos;
	m_pos = min(max(m_min, pos), m_max); 

	if (m_pos != prev_pos && fire_evnet) 
		FireOnChangeEvent();
}

float Scrollbar::PtUnit()
{
	if (m_track_length <= 0) return 0.0f;

	int len1 = GetRangeLength();
	int len2 = m_track_length;
	
	return float(len2) / len1;
}
 
int Scrollbar::Pt2Pos(point pt)
{
	if (IsVertical())
		return int(float(pt.y - m_rect[TRACK].top)  / PtUnit() + 0.5f);
	else
		return int(float(pt.x - m_rect[TRACK].left) / PtUnit() + 0.5f);
}


point Scrollbar::Pos2Pt(int pos)
{
	point p = m_rect[TRACK].left_top();

	if (IsVertical())
		p.y += pos < m_max ? int(pos * PtUnit() + 0.5f) :  m_track_length;
	else
		p.x += pos < m_max ? int(pos * PtUnit() + 0.5f) : m_track_length;

	return p;
}

void Scrollbar::RecalLayout(GumpHandler * gumps)
{
	rect rect;
	GetRect(rect);

	int x1 = rect.left,  y1 = rect.top;
	int x2 = rect.right, y2 = rect.bottom;

	Texture *texture = NULL;

	// draw arrow button
	if (IsUseArraowButton()) {
		m_ltup.SetPosition(x1, y1);
		m_ltup.Draw(gumps);
		m_ltup.GetRect(m_rect[LTUP]);

		if (IsVertical())
			y1 += m_ltup.GetHeight();
		else
			x1 += m_ltup.GetWidth();

		if (IsVertical()) {
			y2 -= m_rtdn.GetHeight();
			m_rtdn.SetPosition(x1, y2);
		}
		else {
			x2 -= m_rtdn.GetWidth();
			m_rtdn.SetPosition(x2, y1);
		}

		m_rtdn.Draw(gumps);
		m_rtdn.GetRect(m_rect[RTDN]);
	}

	// draw track
	int w = x2 - x1 + 1, h = y2 - y1 + 1;

	//hk:fix this
	if (IsVertical())
		texture = LoadGump(m_scroll_gump[SCROLL_TRACK], gumps, false);
	else
		texture = LoadGumpTiled(m_scroll_gump[SCROLL_TRACK], gumps, w, h);
	
	DrawRect(x1,y1,w,h,texture, alpha);
	m_rect[TRACK].set_rect(x1, y1, x1+w, y1+h);

	// draw thumb
	texture = LoadGump(m_scroll_gump[SCROLL_THUMB], gumps, false);
	if (!texture) return;
	size s = get_texture_size(texture);
	m_track_length = IsVertical() ? h - s.cy: w - s.cx;
	
	point p = Pos2Pt(GetPos());
	DrawRect(p.x,p.y, texture, alpha);
	
	m_rect[THUMB].set_rect(p.x, p.y, p.x+s.cx, p.y+s.cy);
}


void Scrollbar::Draw (GumpHandler * gumps)
{
	Control::Draw (gumps);

	RecalLayout(gumps);
	return;

	// draw arrow
	m_ltup.Draw(gumps);
	m_rtdn.Draw(gumps);

	// draw track
	rect &r1 = m_rect[TRACK];
	Texture *texture = NULL;
	
	if (IsVertical())
		texture = LoadGump(m_scroll_gump[SCROLL_TRACK], gumps, false);
	else
		texture = LoadGumpTiled(m_scroll_gump[SCROLL_TRACK], gumps, r1.width(), r1.height());
	DrawRect(r1.left, r1.top, r1.width(), r1.height(), texture, alpha);
	
	// draw thumb
	texture = LoadGump(m_scroll_gump[SCROLL_THUMB], gumps, false);
	if (!texture) return;
	rect &r2 = m_rect[THUMB];
	r2.move_xy(Pos2Pt(GetPos()));
	DrawRect(r2.left, r2.top, texture, alpha);
}

/*
void Scrollbar::Draw (GumpHandler * gumps)
{
Control::Draw (gumps);

rect rect;
GetRect(rect);

int x1 = rect.left,  y1 = rect.top;
int x2 = rect.right, y2 = rect.bottom;

Texture *texture = NULL;

// draw arrow button
if (IsUseArraowButton()) {
m_ltup.SetPosition(x1, y1);
m_ltup.Draw(gumps);

m_ltup.GetRect(m_rect[LTUP]);

if (IsVertical())
y1 += m_ltup.GetHeight();
else
x1 += m_ltup.GetWidth();

if (IsVertical()) {
y2 -= m_rtdn.GetHeight();
m_rtdn.SetPosition(x1, y2);
}
else {
x2 -= m_rtdn.GetWidth();
m_rtdn.SetPosition(x2, y1);
}
m_rtdn.Draw(gumps);

m_rtdn.GetRect(m_rect[RTDN]);
}

// draw track
int w = x2 - x1 + 1, h = y2 - y1 + 1;

texture = LoadGump(m_scroll_gump[SCROLL_TRACK], gumps, false);
if (texture) {
DrawRect(x1,y1,w,h, texture, alpha);
}

m_rect[TRACK].set_rect(x1, y1, x1+w, y1+h);

// draw thumb
//int x = x1, y = y1;
float per = float(m_pos) / (m_max - m_min + 1);

texture = LoadGump(m_scroll_gump[SCROLL_THUMB], gumps, false);
if (!texture) return;

size s = get_texture_size(texture);

if (IsVertical()) {
h  -= s.cy;
y1 += int(float(h) * per + 0.5f);
}
else {
w  -= s.cy;
x1 += int(float(w) * per + 0.5f);
}

DrawRect(x1,y1, texture, alpha);

m_rect[THUMB].set_rect(x1, y1, x1+s.cx, y1+s.cy);

//m_tx1 = x1,     m_ty1 = y1;
//m_tx2 = x1 + w, m_ty2 = y1 + h;

//	m_tx2 = x1 + s.cx, m_ty2 = y1 + s.cy;	

m_track_length = IsVertical() ? h : w;
}
*/

bool Scrollbar::IsOverThumb(int x, int y)
{
	return m_rect[THUMB].pt_in_rect(point(x,y));
}

void Scrollbar::FireOnChangeEvent()
{
	m_onchange_message.callback.key = GetPos();
	if (m_onchange_message.type != MESSAGE_NONE)
		stack.Push (m_onchange_message);

	if (IsDirectOnChange())
		DoOnChange(GetPos());
	else 
	{
		gui_message msg = CreateCallbackMessage (CALLBACK_ONCHANGE);
		msg.callback.key = GetPos();
		stack.Push (msg);
	}
}

int Scrollbar::HandleMessage (gui_message * msg)
{
	//if (Container::HandleMessage(msg)) return true;

	if (!msg)
	{
		pDebug.Log ("NULL msg in Scrollbar::HandleMessage(gui_message *)",
			__FILE__, __LINE__, LEVEL_ERROR);
		return false;
	}

	if (!m_drag && (m_ltup.HandleMessage(msg) || m_rtdn.HandleMessage(msg)))
	{
		return true;
	}

	int x = msg->mouseevent.x, y = msg->mouseevent.y;

	switch (msg->type)
	{
	case MESSAGE_MOUSEDOWN:
		if (MouseIsOver (x, y))
		{
			if (IsOverThumb(x, y)) 
			{
				m_prev_pos = GetPos();
				m_drag = true;
			}

			return true;
		}
		break;
	case MESSAGE_MOUSEMOTION:
		if (m_drag)
		{
			SetPos(Pt2Pos(point(x,y)),false);	
			return true;
		}
		break;
	case MESSAGE_MOUSEUP:
		if (m_drag)
		{
			if (GetPos() != m_prev_pos) 
				FireOnChangeEvent();
		}
		else if (MouseIsOver (x, y))
		{
			int d = 0;
			int u = max(1, int(float(GetRangeLength()) / 10 + 0.5f));
			
			if (IsVertical())
				d = m_rect[THUMB].top  > y ? -1 : 1;
			else
				d = m_rect[THUMB].left > x ? -1 : 1;
			
			SetPos(GetPos() + d * u);
		}

		m_drag = false;
		break;
	default:
		Control::HandleMessage (msg);
	}

	return false;
}

void Scrollbar::DoOnChange (int pos)
{
	if (m_callback_OnChange)
		m_callback_OnChange (this, pos);
}

void Scrollbar::SetChangeMessage (gui_message * msg)
{
	m_onchange_message = *msg;
}

void Scrollbar::OnChange (int (*callback) (Control * sender, int pos))
{
	m_callback_OnChange = callback;
}


};