/*********************************************************************
*	ListBox
*	SOURCE FILE
*	Autor:	Michal Jirouš
*	Datum: 3.7.2008
*	Soubor: listbox.cpp
*	Popis: Seznam, skladajici se z panelu, ktere mohou ovladat jednu komponentu.
*			Panely jsou usporadany vertikalne a lze vzdy jeden oznacit
*			jako aktivni. Mezi panely lze prechazet pomoci kurzorovych
*			sipek nahoru a dolu a pomoci kliknuti mysi na nektery panel.
**********************************************************************/

#include "gfg.h"
#include "mathematic.h"

ListBox::ListBox( float w, float h )
{
	verticalScroll = NULL;
	baseInit( w, h );
	m_IterSelectedPanel = m_Panels.end();	//ukazatel aktivniho prvku
	m_iSize = 0;
	verticalScroll = new Scroll( GFG_LABEL_SCROLLBAR_WIDTH, m_ScissorBox.h );
	verticalScroll->setParent( this );
	verticalScroll->setPosition( m_ScissorBox.x + m_ScissorBox.w, m_ScissorBox.y );
	//TODO
	m_fTranslate = 0;
	setPadding( GFG_STANDARD_PADDING );
	m_fAnimatedTranslate = 0;
	m_iObjectType = GFG_LISTBOX;
}

ListBox::~ListBox()
{
	delete verticalScroll;

	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
		delete (*iter);
	m_Panels.clear();
}

//prida novou komponentu - musi vytvorit novy panel a jemu ji predat
//a panel umistit do seznamu panelu
void ListBox::addComponentToNewPanel( CBaseComponent *component, bool pushBack )
{
	if( component )
	{
		CBasePanel *panel = new CBasePanel( m_ScissorBox.w, component->getHeight() );
		panel->setComponent( component );
		addPanel( panel, pushBack );
	}
}

void ListBox::addPanel( CBasePanel *panel, bool pushBack )
{
	if( panel )
	{
		panel->setWidth( m_ScissorBox.w );
		
		if( pushBack )
			m_Panels.push_back( panel );
		else
			m_Panels.push_front( panel );
		
		panel->setParent( this );
		m_iSize += panel->getHeight();
		
		if( pushBack )
			panel->setPosition( m_ScissorBox.x, m_ScissorBox.y + m_ScissorBox.h - m_iSize + m_fAnimatedTranslate );
		else
		{
			panel->setPosition( m_ScissorBox.x, m_ScissorBox.y + m_ScissorBox.h + m_fAnimatedTranslate );
			/*for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
				(*iter)->setPositionOffset( 0, -panel->getHeight() );*/
			m_fTranslate += panel->getHeight();
			m_fAnimatedTranslate += panel->getHeight();
		}
		
		if( m_IterSelectedPanel == m_Panels.end() )
		{
			m_IterSelectedPanel = m_Panels.begin();
			(*m_IterSelectedPanel)->inputController( GFG_GOT_FOCUS, 0, 0, 0, 0 );
		}
		
		updateScrollSizes();	//aktualizace parametru rolovaciho panelu
		
		updateVisibility();		//aktualizace ciloveho posunu
		changeTranslation(m_fTranslate);
	}
}

void ListBox::updateScrollSizes()
{
	if( verticalScroll  ) 
		verticalScroll->setScrollParameters( m_iSize, m_ScissorBox.h );
}

void ListBox::removePanel(  )
{
	if( !m_Panels.empty() )
	{
		m_iSize -= m_Panels.back()->getHeight();

		delete m_Panels.back();
		m_Panels.pop_back();
	
	}
	/*if( m_IterSelectedPanel != m_Panels.end() )
		removePanel( *m_IterSelectedPanel );*/
}

void ListBox::removePanel( CBasePanel *panel )
{
	/*if( panel == NULL )	
		return;
	bool bShort = false;
	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
		if( (*iter) == panel )
		{
			m_iSize -= (*iter)->getHeight();
			bShort = true;
		}
		else if( bShort )
			(*iter)->inputController( CHANGE_POSITION, 0, panel->getHeight(), 0, 0 );
	if( *m_IterSelectedPanel == panel )
		m_IterSelectedPanel++;
	m_Panels.remove( panel );

	updateScrollSizes();

	if( m_Panels.empty() )
		m_IterSelectedPanel = m_Panels.end();

	updateVisibility();*/
}

void ListBox::setEnabled( bool value )
{
	m_bEnabled = value;
	if( verticalScroll  )
		verticalScroll->setEnabled( value );
	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
		(*iter)->setEnabled( value );
}

void ListBox::setTemporaryDisabled( bool disabled )
{
	m_bTemporaryDisabled = disabled;
	if( verticalScroll  )
		verticalScroll->setTemporaryDisabled( disabled );
	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
		(*iter)->setTemporaryDisabled( disabled );
}

/* Zdedena funkce, ktera musi by aplikovana i na objekty vlastnene */
void ListBox::setAlphaMultiplier( float multiplier )
{
	m_fAlphaMultiplier = multiplier;
	if( verticalScroll  ) 
		verticalScroll->setAlphaMultiplier( m_fAlphaMultiplier );
	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
		(*iter)->setAlphaMultiplier( m_fAlphaMultiplier );
}

//pri zmene velikosti je treba upravit vsechny panely i rolovaci panel
void ListBox::onScissorBoxResize( float w_offset, float h_offset )
{
	updateScrollSizes();
	if( verticalScroll )
	{
		verticalScroll->setSizeOffset( 0, h_offset );	//zmena velikosti pouze do vysky
		verticalScroll->setPosition( m_ScissorBox.x + m_ScissorBox.w , m_ScissorBox.y );
	}

	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
	{
		(*iter)->setSizeOffset( w_offset, 0 );	
		(*iter)->setPositionOffset( 0, h_offset );	
	}
}

//pri posunu se scrollbar i vsechny panely pouye presunou
void ListBox::onScissorBoxTranslate( float offsetX, float offsetY )
{
	if( verticalScroll )
		verticalScroll->setPositionOffset( offsetX, offsetY );
	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
		(*iter)->setPositionOffset( offsetX, offsetY );
}

void ListBox::draw( ScissorBox &scissorBox )
{
	if( !m_bVisible )
		return;
	glPushMatrix();
	glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT  | GL_SCISSOR_BIT);
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	
	glColor4fv( m_ColorBackGround.values );
	

	glBegin( GL_QUADS );
		glVertex2f( m_fGlobalXpos, m_fGlobalYpos );
		glVertex2f( m_fGlobalXpos + m_fWidth, m_fGlobalYpos );
		glVertex2f( m_fGlobalXpos + m_fWidth, m_fGlobalYpos + m_fHeight);
		glVertex2f( m_fGlobalXpos, m_fGlobalYpos + m_fHeight);
	glEnd();
	
	
	glPushAttrib( GL_LINE_BIT );
	glLineWidth( 3 );	//poradnou sirku pro zvyrazneni
	
	//ramecek
	glBegin( GL_LINE_STRIP );
		setColor( GFG_COLOR_SKIN_END_LINE );
		//glVertex2f( m_fGlobalXpos, m_fGlobalYpos );
		glVertex2f( m_fGlobalXpos + m_fWidth, m_fGlobalYpos-1 );
		setColor( GFG_COLOR_SKIN_START_LINE );
		glVertex2f( m_fGlobalXpos + m_fWidth, m_fGlobalYpos + m_fHeight / 2 );
		setColor( GFG_COLOR_SKIN_END_LINE );
		glVertex2f( m_fGlobalXpos + m_fWidth, m_fGlobalYpos + m_fHeight+2);
	glEnd();
	glBegin( GL_LINE_STRIP );
		glVertex2f( m_fGlobalXpos, m_fGlobalYpos + m_fHeight+2);
		setColor( GFG_COLOR_SKIN_START_LINE );
		glVertex2f( m_fGlobalXpos, m_fGlobalYpos + m_fHeight / 2 );
		setColor( GFG_COLOR_SKIN_END_LINE );
		glVertex2f( m_fGlobalXpos, m_fGlobalYpos-1 );
	glEnd();
	glBegin( GL_LINES );
		glVertex2f( m_fGlobalXpos+2, m_fGlobalYpos );
		glVertex2f( m_fGlobalXpos + m_fWidth-1, m_fGlobalYpos );
		glVertex2f( m_fGlobalXpos+2, m_fGlobalYpos + m_fHeight);
		glVertex2f( m_fGlobalXpos + m_fWidth-1, m_fGlobalYpos + m_fHeight);
	glEnd();

	
	glEnable( GL_SCISSOR_TEST );
	glPopAttrib();
	
	if( verticalScroll )
		verticalScroll->draw( scissorBox );

	ScissorBox intersected = m_ScissorBox.intersection( scissorBox );
	glScissor( (int)intersected.x, (int)intersected.y, (int)intersected.w, (int)intersected.h );
	
	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
	{
		(*iter)->draw( intersected );
	}

	glPopAttrib();
	glPopMatrix();
}

//zjisti vyslednou hodnotu rolovaciho panelu a podle ni upravi posun, ktery i vraci
float ListBox::checkScrollTranslation()
{
	float fTranslate = 0;
	if( verticalScroll && m_ScissorBox.h < m_iSize )
		fTranslate = (float)(m_iSize - m_ScissorBox.h) * verticalScroll->getValue();

	return fTranslate;
}

bool ListBox::inputController( int type, float x, float y, int param1, int param2 )
{
	if( !m_bEnabled || !m_bVisible )
		return false;

	if( type == GFG_GOT_FOCUS )
		m_bFocused = true;
	else if( type == GFG_LOST_FOCUS )
		m_bFocused = false;
	else if( verticalScroll->inputController( type, x, y, param1, param2 ) )
	{
		//ovladani rolovaciho panelu
		if( type == GFG_MOUSE_BUTTON && param2 == GFG_DOWN )
		{
			onScrollPanelChange();
		}
		return true;
	}
	else if( type & GFG_MOUSE)
	{
		bool bIsOnArea = checkMouseArea( x, y );;

		if( type == GFG_MOUSE_BUTTON && bIsOnArea )
		{
			//zde se testuje, na ktery panel uzivatel klikl, aby se oznacil
			//jako aktivni
			for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
			{
				if(	(*iter)->inputController( type, x, y, param1, param2 ) && param2 == GFG_DOWN )
				{
					//pak se musi prohodit fokus
					(*m_IterSelectedPanel)->inputController( GFG_LOST_FOCUS, 0, 0, 0, 0 );
					m_IterSelectedPanel = iter;
					(*m_IterSelectedPanel)->inputController( GFG_GOT_FOCUS, 0, 0, 0, 0 );
					m_Callback.callbackFunc( this, m_Callback.callbackData );
					updateVisibility();	//a aktualizovat cilovy posun
					break;
				}
			}
		}
		else if( type == GFG_MOUSE_OVER )
		{
			for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
			{
				(*iter)->inputController( type, x, y, param1, param2 );
			}
		}

		return bIsOnArea;
	}
	else if( type & GFG_KEYBOARD && m_IterSelectedPanel != m_Panels.end() )
	{
		/*if( !(*m_IterSelectedPanel)->inputController( type, x, y, param1, param2 ) && 
			type == GFG_SPECIAL_KEY_DOWN && !m_Panels.empty())
		{*/
		if( type == GFG_SPECIAL_KEY_DOWN && !m_Panels.empty() )
		{
			if( param1 == GFG_KEY_UP )
			{	//zmena aktvniho panelu...presun na horni panel
				if( !m_Panels.empty() && m_IterSelectedPanel != m_Panels.begin() )
				{
					(*m_IterSelectedPanel)->inputController( GFG_LOST_FOCUS, 0, 0, 0, 0 );
					m_IterSelectedPanel--;
					(*m_IterSelectedPanel)->inputController( GFG_GOT_FOCUS, 0, 0, GFG_KEY_UP, 0 );
					m_Callback.callbackFunc( this, m_Callback.callbackData );
					updateVisibility();			
				}
				
			}
			else if( param1 == GFG_KEY_DOWN )
			{
				//zmena aktvniho panelu...presun na dolni panel
				if( (*m_IterSelectedPanel) != m_Panels.back() )
				{
					(*m_IterSelectedPanel)->inputController( GFG_LOST_FOCUS, 0, 0, 0, 0 );
					m_IterSelectedPanel++;
					(*m_IterSelectedPanel)->inputController( GFG_GOT_FOCUS, 0, 0, GFG_KEY_DOWN, 0 );
					m_Callback.callbackFunc( this, m_Callback.callbackData );
					updateVisibility();
				}
				
			}
			
			
		}
		if( type != GFG_SPECIAL_KEY_DOWN && param1 != GFG_KEY_TAB )
				(*m_IterSelectedPanel)->inputController( type, x, y, param1, param2 );
		return true;
		/*}
		else
			return true;*/
	}


	return false;
}

void ListBox::setSelectedPanel( CBasePanel *panel )
{
	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
	{
		if( (*iter) == panel )
		{
			setSelectedPanel( iter );
			return;
		}
	}
}

void ListBox::setSelectedPanel( list<CBasePanel*>::iterator &iter )
{
	if( iter != m_Panels.end() )
	{
		(*m_IterSelectedPanel)->inputController( GFG_LOST_FOCUS, 0, 0, 0, 0 );
		m_IterSelectedPanel = iter;
		(*m_IterSelectedPanel)->inputController( GFG_GOT_FOCUS, 0, 0, 0, 0 );
		m_Callback.callbackFunc( this, m_Callback.callbackData );
		updateVisibility();
	}
}

CBasePanel *ListBox::getSelectedPanel()
{
	if( m_IterSelectedPanel != m_Panels.end() )
		return *m_IterSelectedPanel;
	return NULL;
}

void ListBox::onScrollPanelChange()
{
	m_fTranslate = checkScrollTranslation();
}


void ListBox::changeTranslation( float value )
{
	//posun panelu dle animace
	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
		(*iter)->setPositionOffset( 0, value-m_fAnimatedTranslate );

	m_fAnimatedTranslate = value;

	//aktualizace vysledne hodnoty rolovaciho panelu
	if( verticalScroll  && !verticalScroll->isScrollRailChangingPosition() && m_ScissorBox.h < m_iSize)
		verticalScroll->setValue( m_fAnimatedTranslate / (m_iSize - m_ScissorBox.h ) );
}

//funkce vypocitava cilove posunuti vsech panel
//snazi se, aby aktivni panel byl co nejblize stredu cele komponenty
void ListBox::updateVisibility()
{
	if( m_IterSelectedPanel == m_Panels.end() ) return;
	CBasePanel *panel = (*m_IterSelectedPanel);

	float value = m_fGlobalYpos  - m_fPaddingBottom - panel->getYPosition() + (m_fAnimatedTranslate - m_fTranslate);
	float distance = value + m_fHeight + m_fTranslate ;
	float panelHalfHeight = panel->getHeight() / 2.0f;

	if( distance - panelHalfHeight < m_fHeight / 2.0f )
		value = 0;
	else
	{
		value = value + m_fHeight / 2.0f - panelHalfHeight;
		value = getFromRange( m_fTranslate + value + m_fHeight, m_fHeight, m_iSize );
		value -= m_fHeight;
	}
	m_fTranslate = value;	//nastaveni noveho ciloveho posunu
}


void ListBox::run()
{
	if( verticalScroll )
	{
		verticalScroll->run();
		
		//rolovaci panel meni vyslednou hodnotu
		if( verticalScroll->isArrowButtonPushed() || verticalScroll->isScrollRailChangingPosition() )
			onScrollPanelChange();
	}

	
	//animovany posun
	//snazi se dostat na cilovou hodnotu posunu
	if( m_fAnimatedTranslate > m_fTranslate )
	{
		if( m_fAnimatedTranslate - GFG_LISTBOX_ANIMATE_STEP < m_fTranslate )
			changeTranslation(m_fTranslate);
		else
			changeTranslation( m_fAnimatedTranslate - max(GFG_LISTBOX_ANIMATE_STEP, (m_fAnimatedTranslate-m_fTranslate) / 8 ));
	
	}
	else if( m_fAnimatedTranslate < m_fTranslate )
	{
		if( m_fAnimatedTranslate + GFG_LISTBOX_ANIMATE_STEP > m_fTranslate )
			changeTranslation(m_fTranslate);
		else
			changeTranslation( m_fAnimatedTranslate + max(GFG_LISTBOX_ANIMATE_STEP, (m_fTranslate-m_fAnimatedTranslate) / 8 ));
	}
	
	for( list<CBasePanel*>::iterator iter = m_Panels.begin(); iter != m_Panels.end(); iter++ )
		(*iter)->run();
}

