#include "GUIElement.h"
#include "menumanager.h"
#include "menu.h"
#include "..\dxfunctions\dllinterface.h"
#include "..\dxfunctions\d3dapp.h"

GUIElement::GUIElement(MenuManager* const manager, const DWORD id)
{
	m_Manager = manager;
	m_ID = id;

	m_Manager->AddElement(this);

	//default initiations
	m_Menu = NULL;
	m_Parent = NULL;
	m_Asolute = true;
	m_X = m_Y = m_Xl = m_Yl = 0;
	m_Align = AL_RIGHTBOTTOM;
	m_Enable = true;
	m_Visible = true;
	m_Focus = false;
	m_FocusByClick = false;
	m_TabIndex = 0;

	m_CurKey = 0;
	m_FirstRepeat = false;
	m_LastPressEventTime = 0;

	m_MouseOver = false;
	ZeroMemory(&m_MouseButtons,sizeof(BYTE)*3);
	ZeroMemory(&m_MouseClickTime,sizeof(DWORD)*3);

	ZeroMemory(m_EventHandlers, sizeof(void*) * GE_COUNT);

//	m_HandledEvents = GE_NONE;
}

GUIElement::~GUIElement(void)
{
	if(m_Menu && m_TabIndex)
		m_Menu->DelTabEntry(this);
	FOREACH(elc, m_Childs, GUIElement)
	{
		elc->GetInfo()->m_Parent = NULL;
		m_Manager->DeleteElement(elc->GetInfo());
	}
	m_Childs.Release();
}

void GUIElement::AddChild(GUIElement*el)
{
	if(m_Menu && el->GetTabIndex())
		m_Menu->AddTabEntry(this);
	el->SetParent(this);
	el->SetMenu(m_Menu);
	m_Childs.AddTail(el,el->GetID());
}

void GUIElement::RemoveChild(GUIElement*el)
{
	ListElement<GUIElement>* lel = m_Childs.GetElement(el);
	if (lel)
	{
		el->SetParent(NULL);
		el->SetMenu(NULL);
		lel->Unlink();
		delete lel;
	}
}

void GUIElement::RemoveAllChilds(void)
{
	FOREACH(elc,m_Childs,GUIElement)
	{
		elc->GetInfo()->SetParent(NULL);
		elc->GetInfo()->SetMenu(NULL);
	}
	m_Childs.Release();
}

void GUIElement::SetTabIndex(DWORD tabindex)
{
	if(m_Menu && m_TabIndex)
		m_Menu->DelTabEntry(this);
	
	m_TabIndex = tabindex;

	if(m_Menu && m_TabIndex)
		m_Menu->AddTabEntry(this);
}

void GUIElement::Update(bool withchilds)
{
	if(!m_Enable || !m_Visible || !m_Manager->GetInputDevice())
		return;

	DXVector2 dpos = GetDrawPos();

	if(m_Focus)
	{
		//keyboard
		for(int i=0;i<256;i++)
		{
			BYTE state = m_Manager->GetInputDevice()->GetKey(i);
			if(state & KEY_DOWN)
			{
				HandleEvent(GE_KEYDOWN,i);

				m_CurKey = i;
				m_FirstRepeat = true;
				m_LastPressEventTime = m_Manager->GetDevice()->GetApp()->GetClock().RunTime();
				HandleEvent(GE_KEYPRESS,i);

				if(m_TabIndex && i == DIK_TAB)
					m_Menu->FocusNext(m_TabIndex);
			}
			else if(state & KEY_UP)
			{
				HandleEvent(GE_KEYUP,i);

				m_CurKey = 0;
				m_FirstRepeat = false;
				m_LastPressEventTime = 0;
			}
			
			if(state & KEY_PRESS)
			{
				if(m_CurKey == i)
				{
					if(m_FirstRepeat)
					{
						if(m_Manager->GetDevice()->GetApp()->GetClock().RunTime() - m_LastPressEventTime >= (unsigned)m_Manager->GetInputDevice()->GetKeyboardDelay())
						{
							m_FirstRepeat = false;
							m_LastPressEventTime = m_Manager->GetDevice()->GetApp()->GetClock().RunTime();
							HandleEvent(GE_KEYPRESS,i);
						}
					}
					else
					{
						if(m_Manager->GetDevice()->GetApp()->GetClock().RunTime() - m_LastPressEventTime >= (unsigned)m_Manager->GetInputDevice()->GetKeyboardSpeed())
						{
							m_LastPressEventTime = m_Manager->GetDevice()->GetApp()->GetClock().RunTime();
							HandleEvent(GE_KEYPRESS,i);
						}
					}
				}
			}
		}
	}

	if(m_Manager->GetInputDevice()->GetCursorX() >= dpos.x && m_Manager->GetInputDevice()->GetCursorX() <= dpos.x + m_Xl && 
		m_Manager->GetInputDevice()->GetCursorY() >= dpos.y && m_Manager->GetInputDevice()->GetCursorY() <= dpos.y + m_Yl)
	{
		m_Manager->m_MouseOver = this;
	}
	else //mouse not over
	{
		if(m_FocusByClick)
		{
			for(int i=0;i<3;i++) //check for click anywhere else
			{
				if(GetManager()->GetInputDevice()->GetMouseButton((MOUSEBUTTON)i) & BUTTONDOWN)
					SetFocus(false);
			}
		}

		if(m_MouseOver)
		{
			m_MouseOver = false;

			ZeroMemory(&m_MouseButtons,sizeof(BYTE)*3);
			ZeroMemory(&m_MouseClickTime,sizeof(DWORD)*3);

			HandleEvent(GE_MOUSEOFF);

			m_CurKey = 0;
			m_FirstRepeat = false;
			m_LastPressEventTime = 0;
		}
	}

	HandleEvent(GE_UPDATE);

	//check if still has focus
	if(m_Focus)
		HandleEvent(GE_HASFOCUS);

	if(withchilds && m_Enable && m_Visible)
	{
		FOREACH(elc,m_Childs,GUIElement)
		{
			elc->GetInfo()->Update();
		}
	}
}

void GUIElement::UpdateMouseOver(void)
{
	//mouse
	if(!m_MouseOver)
		HandleEvent(GE_MOUSEON);

	HandleEvent(GE_MOUSEOVER);

	m_MouseOver = true;
	
	if(m_Manager->GetInputDevice()->GetMouseDZ())
		HandleEvent(GE_WHEEL,m_Manager->GetInputDevice()->GetMouseDZ());

	for(int i=0;i<3;i++)
	{
		MOUSEBUTTON mb = (MOUSEBUTTON)i;

		if(GetManager()->GetInputDevice()->GetMouseButton(mb) & BUTTONDOWN)
		{				
			if(m_FocusByClick)
				SetFocus(true);

			HandleEvent(GE_BUTTONDOWN,mb);
			m_MouseButtons[mb] = 1;

			m_Manager->m_DragStart = this;
			m_Manager->m_DragButton = mb;
		}
		else if(GetManager()->GetInputDevice()->GetMouseButton(mb) & BUTTONUP)
		{
			HandleEvent(GE_BUTTONUP,mb);
			if(m_MouseButtons[mb])
			{
				HandleEvent(GE_BUTTONCLICK,mb);
				if(m_Manager->GetDevice()->GetApp()->GetClock().RunTime() - m_MouseClickTime[mb] <= m_Manager->GetInputDevice()->GetMouseDoubleClickTime())
					HandleEvent(GE_BUTTONDCLICK,mb);
				m_MouseClickTime[mb] = m_Manager->GetDevice()->GetApp()->GetClock().RunTime();
			}
			m_MouseButtons[mb] = 0;
		}
	}
}

void GUIElement::Draw(bool withchilds)
{
	if(!m_Visible)
		return;

	HandleEvent(GE_DRAW);

	if(withchilds && m_Visible)
	{
		FOREACH(elc,m_Childs,GUIElement)
		{
			elc->GetInfo()->Draw();
		}
	}
}

void GUIElement::SetMenu(Menu* menu)
{
	m_Menu = menu;
	FOREACH(elc,m_Childs,GUIElement)
	{
		elc->GetInfo()->SetMenu(menu);
	}
}

GUIElement* GUIElement::GetChildByID(const DWORD id) const
{
	ListElement<GUIElement>* ec = this->m_Childs.GetElement(id);

	if (ec)
		return ec->GetInfo();
	
	FOREACH(el, m_Childs, GUIElement)
	{
		GUIElement* c = el->GetInfo()->GetChildByID(id);
		if (c)
			return c;
	}
	
	return NULL;
}

void GUIElement::Unlink(void)
{
	m_Parent = NULL;
	SetMenu(NULL);
}

void GUIElement::Delete(void)
{
	if(m_Parent)
		m_Parent->GetChilds().GetElement(m_ID)->Delete();
	m_Manager->DeleteElement(this);
	//delete this;
}

void GUIElement::SetEventHandler(const GUIEVENT evnt, void(*handler)(GUIElement* const sender, const GUIEVENT evnt, void* const pData))
{
	m_EventHandlers[(int)evnt] = handler;
}

DXVector2 GUIElement::GetDrawPos(void) const
{
	float x,y;

	switch(m_Align)
	{
		case AL_RIGHTBOTTOM: x = m_X; y = m_Y; break;
		case AL_RIGHTCENTER: x = m_X;y = m_Y-m_Yl*0.5f; break;
		case AL_RIGHTTOP: x = m_X; y = m_Y-m_Yl; break;
		case AL_LEFTBOTTOM: x = m_X-m_Xl; y = m_Y; break;
		case AL_LEFTCENTER: x = m_X-m_Xl; y = m_Y-m_Yl*0.5f; break;
		case AL_LEFTTOP: x = m_X-m_Xl; y = m_Y-m_Yl; break;
		case AL_CENTERBOTTOM: x = m_X-m_Xl*0.5f; y = m_Y; break;
		case AL_CENTER: x = m_X-m_Xl*0.5f; y = m_Y-m_Yl*0.5f; break;
		case AL_CENTERTOP: x = m_X-m_Xl*0.5f; y = m_Y-m_Yl; break;
		default: x = m_X; y = m_Y; break;
	}

	if(m_Parent)
	{
		DXVector2 pdp = m_Parent->GetDrawPos();
		x += pdp.x;
		y += pdp.y;
	}

	return DXVector2(x,y);
}

void GUIElement::HandleEvent(GUIEVENT evnt,DWORD param)
{
	if(!evnt)
		return;

	switch(evnt)
	{
	case GE_UPDATE: OnUpdate(); break;
	case GE_DRAW: OnDraw(); break;
	case GE_MOUSEOVER: OnMouseOver(); break;
	case GE_MOUSEON: OnMouseOn(); break;
	case GE_MOUSEOFF: OnMouseOff(); break;
	case GE_BUTTONDOWN: OnButtonDown((MOUSEBUTTON)param); break;
	case GE_BUTTONUP: OnButtonUp((MOUSEBUTTON)param); break;
	case GE_BUTTONCLICK: OnButtonClick((MOUSEBUTTON)param); break;
	case GE_BUTTONDCLICK: OnButtonDClick((MOUSEBUTTON)param); break;
	case GE_WHEEL: OnWheel(param); break;
	case GE_HASFOCUS: OnHasFocus(); break;
	case GE_GOTFOCUS: OnGotFocus(); break;
	case GE_LOSTFOCUS: OnLostFocus(); break;
	case GE_KEYDOWN: OnKeyDown(param); break;
	case GE_KEYUP: OnKeyUp(param); break;
	case GE_KEYPRESS: OnKeyPress(param); break;
	case GE_VALUECHANGED: OnValueChanged(); break;
	}

	//if(m_HandledEvents & evnt && m_Menu)
	//	m_Menu->OnEvent(m_ID,evnt,param);
	if (m_EventHandlers[(int)evnt])
		m_EventHandlers[(int)evnt](this, evnt, (void*)param);
}

void GUIElement::OnFwdEvent(GUIElement* const sender, const GUIEVENT evnt, void* pData)
{
	GUIElement* me = sender->GetParent();
	me->HandleEvent(evnt, (DWORD)pData);
}