/*********************************************************************
*	SubWindow
*	SOURCE FILE
*	Autor:	Michal Jirouš
*	Datum: 2.7.2008
*	Soubor: subwindow.cpp
*	Popis: Implementace okna. Obsahuje horni panel se dvema ovladacimi
*			tlacitky. Muze ovladat jednu komponentu a umoznuje menit
*			svoji velikost
**********************************************************************/

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


//Padding je u okna unikatni, protoze se musi vzdy k hornimu pricist
//jeste vyska panelu
void CSubWindow::setPadding( float padding )
{
	m_fPaddingBottom = padding;
	m_fPaddingLeft = padding;
	m_fPaddingTop = GFG_SUBWINDOW_TOP_PANEL_HEIGHT + padding;
	m_fPaddingRight = padding;
	updateScissorBox();
}
CSubWindow::CSubWindow()
{
	m_pComponent = NULL;
	m_pMainWindow = NULL;
	m_iPackingButtonOffset = GFG_SUBWINDOW_PACKING_BUTTON_OFFSET;
	cancelButton = NULL;
	packButton = NULL;
	m_iButtonParm = GFG_SUBWINDOW_CLOSE | GFG_SUBWINDOW_PACKING;
}

void CSubWindow::subWindowInit( float x, float y, float w, float h )
{
	m_pComponent = NULL;
	m_pMainWindow = NULL;
	
	//vytvoreni ovladacich tlacitek okna
	if( m_iButtonParm & GFG_SUBWINDOW_CLOSE )
		cancelButton = GFG_newButton( GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE, GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE );
	if( m_iButtonParm & GFG_SUBWINDOW_PACKING )
	{
		packButton = new SwitchedButton( GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE, GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE );
		packButton->init();
	}
	baseInit( w, h );
	
	m_iTopPanelHeight = GFG_SUBWINDOW_TOP_PANEL_HEIGHT;
	
	setPadding( GFG_SUBWINDOW_PADDING );
		
	//inicializace promennych
	m_fGlobalXpos = x;
	m_fGlobalYpos = y;
	updateScissorBox();
	m_bVisible = false;
	m_bEnabled = true;
	m_fAlphaMultiplier = 0.0f;
	m_iResizeType = 0;
	m_bResizable = false;
	
	m_iPackingStep = GFG_SUBWINDOW_PACKING_SPEED;
	m_bPacked = false;
	m_fPackOffset = 0;

	m_bChangingPosition = false;

	//nacteni textur
	TextureElement *tmp = textureLibrary.applyTexture( "GFG_MASK" );
	if( tmp != NULL )
		g_maskId = tmp->texture_id;

	tmp = textureLibrary.applyTexture( "GFG_PANEL" );
	if( tmp != NULL )
		g_panelID = tmp->texture_id;
	
	if( cancelButton )
	{
		//umisteni ovladacich tlacitek
		cancelButton->setPosition( m_fGlobalXpos + m_fWidth - (m_iTopPanelHeight - GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE)/2 - GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE,
			m_fGlobalYpos + m_fHeight - (m_iTopPanelHeight - GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE)/2 - GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE );
		cancelButton->setTextureIdle("GFG_CROSS");
		cancelButton->setTexturePushed("GFG_CROSS_PUSHED");
	}

	if( packButton )
	{
		float offset = GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE;
		if( cancelButton )
			offset += GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE + m_iPackingButtonOffset;

		packButton->setPosition( m_fGlobalXpos + m_fWidth - (m_iTopPanelHeight - GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE)/2 - offset,
			m_fGlobalYpos + m_fHeight - (m_iTopPanelHeight - GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE)/2 - GFG_SUBWINDOW_TOP_PANEL_BUTTON_SIZE );

		packButton->setTextureIdle("GFG_ARROW_UP_IDLE");
		packButton->setTexturePushed("GFG_ARROW_DOWN_IDLE");
		packButton->setColorPushed( GFG_COLOR_BUTTON_IDLE );
		packButton->setColorPushedMouseOver( Color4I(GFG_COLOR_BUTTON_MOUSEOVER) );
	}


	m_iObjectType = GFG_WINDOW;
	windowCorrection();
}


CSubWindow::CSubWindow( float x, float y, float w, float h, int buttonparm )
{
	cancelButton = NULL;
	packButton = NULL;
	m_iButtonParm = buttonparm;
	m_iPackingButtonOffset = GFG_SUBWINDOW_PACKING_BUTTON_OFFSET;
	subWindowInit( x, y, w, h );

}

CSubWindow::~CSubWindow()
{
	delete m_pComponent;
	delete cancelButton;
	delete packButton;
	textureLibrary.unloadTexture( "GFG_MASK" );
	textureLibrary.unloadTexture( "GFG_PANEL" );
}

void CSubWindow::setCaption( string text )
{
	m_dsCaption = fontLibrary::DString( text, "SERIF", FONT_ALIGN_CENTER );
}

void CSubWindow::setCaption( string text, string font, int align )
{
	m_dsCaption = fontLibrary::DString( text, font, align );
}

//reakce na zmenu pozice, velikosti orezavaciho boxu a cele komponenty
void CSubWindow::onComponentTranslate(float offsetX, float offsetY)
{
	if( cancelButton )
		cancelButton->setPositionOffset( offsetX, offsetY );
	if( packButton )
		packButton->setPositionOffset( offsetX, offsetY );
	windowCorrection();
}

void CSubWindow::onComponentResize(float w_offset, float h_offset)
{
	if( cancelButton )
		cancelButton->setPositionOffset( w_offset, h_offset );
	if( packButton )
		packButton->setPositionOffset( w_offset, h_offset );
}

void CSubWindow::onScissorBoxTranslate(float offsetX, float offsetY)
{
	if( m_pComponent )
		m_pComponent->setPositionOffset( offsetX, offsetY );
}

void CSubWindow::onScissorBoxResize( float w_offset, float h_offset )
{
	if( m_pComponent )
		m_pComponent->setSizeOffset( w_offset, h_offset );
}

//ovladani
bool CSubWindow::inputController(int type, float x, float y, int param1, int param2)
{
	if( type == GFG_LOST_FOCUS  || type == GFG_GOT_FOCUS )
	{
		m_bFocused = (type == GFG_GOT_FOCUS);
		if( m_pComponent )
			m_pComponent->inputController( type, x, y, param1, param2 );
	}

	if( !m_bVisible || !m_bEnabled || m_bTemporaryDisabled)
		return false;

	//indikatory obsluhy udalosti komponentou a oknem
	bool bComponent = false, window = false;;

	if( type & GFG_KEYBOARD )
	{
		if( !m_pComponent || !m_pComponent->inputController( type, x, y, param1, param2 ) )
			m_bVisible = !onKeyboardDown( param1 );
	}
	else if( type & GFG_MOUSE )
	{
		bool onArea = x > m_fGlobalXpos && x < m_fGlobalXpos + m_fWidth && y > m_fGlobalYpos + m_fPackOffset && y < m_fGlobalYpos + m_fHeight;
		bool onScissorBoxArea = checkScissorBoxArea( x, y ) && !m_bPacked;
		onArea = onArea && y > m_fGlobalYpos + m_fPackOffset;
		if( m_pComponent  )
		{
			//ovladani komponenty
			if( type == GFG_MOUSE_OVER )
			{
				if( m_pComponent->inputController( type, x, y, param1, param2 ) )
				{
					if( onScissorBoxArea )
					bComponent = true;
				}
			}
			else if( type == GFG_MOUSE_BUTTON && onScissorBoxArea || type == GFG_MOUSE_MOTION )
				bComponent = m_pComponent->inputController( type, x, y, param1, param2 );
		}
		
		if( onArea && type == GFG_MOUSE_BUTTON && param2 == GFG_DOWN )
		{
			//sbaleni / rozbaleni okna
			if( packButton && packButton->inputController( GFG_MOUSE_BUTTON, x, y, param1, param2 ) )
			{
				m_bPacked ^= 1;
			}
			else if( !cancelButton || !cancelButton->inputController( GFG_MOUSE_BUTTON, x, y, param1, param2 ) )
				checkTopPanelAndResizes( x, y );
			window = true;
		}
		else if( type == GFG_MOUSE_OVER )
		{
			if( cancelButton )
				cancelButton->inputController( type, x, y, param1, param2 );
			if( packButton )
				packButton->inputController( type, x, y, param1, param2 );
			window = onArea;
		}
		else if( type == GFG_MOUSE_BUTTON && param2 == GFG_UP )
		{
			//zavreni okna
			if( cancelButton && cancelButton->inputController( GFG_MOUSE_BUTTON, x, y, param1, param2 ) && m_bFocused)
			{
				onCancelButtonClick();
			
				return false;
			}

			if( m_bChangingPosition  )
			{
				m_bChangingPosition = false;
				windowCorrection();
			}
			else if( m_iResizeType )
			{
				m_iResizeType = 0;
			}
			
			window = true;
		}
		else if( type == GFG_MOUSE_MOTION  )
		{
			if( m_bChangingPosition ) //zmena pozice
			{
				//CHANGE
				//setPositionOffset( (int)x - (float)m_iXstartMove, (int)y - m_iYstartMove );
				setPositionOffset( x - (float)m_iXstartMove, y - (float)m_iYstartMove );
				m_iXstartMove = (int)x;
				m_iYstartMove = (int)y;
			}
			else if( m_iResizeType )	//pokud se meni okraje (velikost)
			{
				resizeByMouse( (int)x, (int)y );
			}
		}
	}
	return (window || bComponent);
}

void CSubWindow::onCancelButtonClick()
{
	this->m_bVisible = false;
}


//funkce pro zmenu velikosti okna
void CSubWindow::resizeByMouse( int x, int y)
{
	if( !m_pMainWindow )
		return;
	
	//offset X, offset Y - zmena sirky a vysky
	//posun v ose X, posun v ose Y - bod 0,0
	float x_off = 0, y_off = 0, x_trans = 0, y_trans = 0;
	
	bool bTranslate = false, bResize = false;;
	float fBorder = getPadding();

	//existuji 4 moznosti posunu okraje
	if( m_iResizeType & GFG_RESIZE_RIGHT && x - m_iXstartMove + m_fWidth > fBorder*2 && x < m_pMainWindow->getWidth())
	{
		x_off = (float)(x - m_iXstartMove);
		m_iXstartMove = x;
		bResize |= 1;	//meni se velikost
	}
	if( m_iResizeType & GFG_RESIZE_DOWN && y > 0 && m_iYstartMove - y + m_fHeight > fBorder + m_iTopPanelHeight)
	{
		y_off = (float)(m_iYstartMove - y);
		y_trans = (float)(y - m_iYstartMove);
		m_iYstartMove = y;
		bTranslate |= 1;	//meni se i pozice
		bResize |= 1;
	}
	if( m_iResizeType & GFG_RESIZE_UP && y < m_pMainWindow->getHeight() && y - m_iYstartMove + m_fHeight > fBorder + m_iTopPanelHeight)
	{
		y_off = (float)(y - m_iYstartMove);
		bResize |= 1;
		m_iYstartMove = y;
	}
	if( m_iResizeType & GFG_RESIZE_LEFT && x > 0 && m_iXstartMove - x+ m_fWidth > fBorder*2 )
	{
		x_off = (float)(m_iXstartMove - x);
		x_trans = (float)(x - m_iXstartMove);
		bTranslate |= 1;
		m_iXstartMove = x;
		bResize |= 1;
	}

	if( bTranslate )
		setPositionOffset( x_trans, y_trans );
	if( bResize )
		setSizeOffset( x_off, y_off );
}

//funkce upravuje pozici okna v hlavnim okne, pokud neni videt cely horni panel
void CSubWindow::windowCorrection()
{
	if( m_pMainWindow )
	{
		//horni panel musi byt vzdy videt cely, takze se testuje
		//zda nekde nepresahuje okraj hlavniho okna a pokud ano
		//tak se posune vice dovnitr
		int iXpos = (int)m_fGlobalXpos, iYpos = (int)m_fGlobalYpos;
		if( m_fGlobalYpos + m_fHeight > m_pMainWindow->getHeight() )	//nahoru
			iYpos = (int)(m_pMainWindow->getHeight() - m_fHeight);
		if( m_fGlobalYpos + m_fHeight - m_iTopPanelHeight < 0 )	//dolu
			iYpos =  -(int)(m_fHeight - m_iTopPanelHeight);
		if( m_fGlobalXpos < 0 )	//vlevo
			iXpos = 0;
		if( m_fGlobalXpos + m_fWidth > m_pMainWindow->getWidth() )	//vpravo
			iXpos = (int)(m_pMainWindow->getWidth() - m_fWidth);

		//pokud je potreba, tak se posune
		if( iXpos != (int)m_fGlobalXpos || iYpos != (int)m_fGlobalYpos )
			setPositionOffset( iXpos - m_fGlobalXpos, iYpos - m_fGlobalYpos );
	}
}

void CSubWindow::setEnabled( bool value )
{
	if( m_bTemporaryDisabled )
		return;
	m_bEnabled = value;
	if( cancelButton )
		cancelButton->setEnabled( value );
	if( packButton )
		packButton->setEnabled( value );

	if( m_pComponent )
		m_pComponent->setTemporaryDisabled( !value );
}

void CSubWindow::setTemporaryDisabled( bool disabled )
{
	m_bTemporaryDisabled = disabled;
	if( !m_bEnabled )
		return;
	
	if( cancelButton )
		cancelButton->setTemporaryDisabled( disabled );
	if( packButton )
		packButton->setTemporaryDisabled( disabled );
	if( m_pComponent )
		m_pComponent->setTemporaryDisabled( disabled );
}


bool CSubWindow::onKeyboardDown( int key )
{
	return ( key == GFG_KEY_ESC );
}

//test kliknuti na horni panel
void CSubWindow::checkTopPanelAndResizes( float x, float y )
{
	if( !m_bPacked && m_bResizable)
	{
		if( x > m_fGlobalXpos + m_fWidth - m_fPaddingRight )	//resize width
		{
			m_iResizeType |= GFG_RESIZE_RIGHT;
		}
		if( x < m_fGlobalXpos + m_fPaddingLeft )
		{
			m_iResizeType |= GFG_RESIZE_LEFT;
		}
		if( y < m_fGlobalYpos + m_fPaddingBottom )
			m_iResizeType |= GFG_RESIZE_DOWN;
		if( y > m_fGlobalYpos + m_fHeight - GFG_SUBWINDOW_TOP_PANEL_HEIGHT * GFG_SUBWINDOW_TOP_PANEL_RESIZING_PADDING )
			m_iResizeType |= GFG_RESIZE_UP;
	}



	if( !m_iResizeType && y > m_fGlobalYpos + m_fHeight - m_iTopPanelHeight )
		m_bChangingPosition = true;

	if( m_iResizeType || m_bChangingPosition )
	{
		m_iXstartMove = (int)x;
		m_iYstartMove = (int)y;
	}

}





void CSubWindow::draw( ScissorBox &scissorBox )
{
	if( m_fAlphaMultiplier < GFG_SUBWINDOW_ALPHA_MULTIPLIER_STEP ) return;

	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_SCISSOR_BIT);
	
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND );
	glEnable(GL_TEXTURE_2D);
	glBindTexture( GL_TEXTURE_2D, g_maskId );
	glScissor( (int)m_fGlobalXpos, (int)(m_fGlobalYpos + m_fPackOffset), (int)m_fWidth, (int)(m_fHeight - m_fPackOffset) );
	glEnable(GL_SCISSOR_TEST );
	
	
	windowBasicDraw( scissorBox );

	ScissorBox intersected = m_ScissorBox.intersection( scissorBox );
	intersected.y += m_fPackOffset;
	intersected.h -= min((float)m_fPackOffset, intersected.h);
	glScissor( (int)intersected.x, (int)intersected.y, (int)intersected.w, (int)intersected.h );
	glEnable(GL_SCISSOR_TEST );

	if( m_pComponent )
		m_pComponent->draw( intersected );

	glPopAttrib();
}

void CSubWindow::windowBasicDraw( ScissorBox &scissorBox )
{
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND );
	glEnable(GL_TEXTURE_2D);

	glBindTexture( GL_TEXTURE_2D, g_maskId );
	glScissor( (int)m_fGlobalXpos, (int)(m_fGlobalYpos + m_fPackOffset), (int)m_fWidth, (int)(m_fHeight - m_fPackOffset) );
	glEnable(GL_SCISSOR_TEST );

	glBegin( GL_QUADS );
		setColor( GFG_COLOR_WINDOW_FIELD_HARDBLEND );

		glTexCoord2i( 0,1 );
		glVertex2f( m_fGlobalXpos, m_fGlobalYpos + m_fHeight - m_iTopPanelHeight);
		setColor( GFG_COLOR_WINDOW_FIELD_LIGHTBLEND );
		glTexCoord2i( 0,0 );
		glVertex2f( m_fGlobalXpos, m_fGlobalYpos );
		glTexCoord2i( 1,0 );
		glVertex2f( m_fGlobalXpos + m_fWidth, m_fGlobalYpos );
		setColor( GFG_COLOR_WINDOW_FIELD_HARDBLEND );
		glTexCoord2i( 1,1 );
		glVertex2f( m_fGlobalXpos + m_fWidth, m_fGlobalYpos + m_fHeight- m_iTopPanelHeight);
	glEnd();
	
	
	windowTopPanelDraw( scissorBox );


	if( cancelButton )
		cancelButton->draw( scissorBox );
	if( packButton )
		packButton->draw( scissorBox );
	glDisable(GL_TEXTURE_2D);
	setColor( GFG_COLOR_WINDOW_CAPTION );

	if( m_dsCaption.m_iAlign == FONT_ALIGN_CENTER )
		m_dsCaption.draw( m_fGlobalXpos + m_fWidth/2, m_fGlobalYpos + m_fHeight - GFG_SUBWINDOW_TOP_PANEL_HEIGHT/2 );
	else if( m_dsCaption.m_iAlign == FONT_ALIGN_LEFT )
		m_dsCaption.draw( m_fGlobalXpos, m_fGlobalYpos + m_fHeight - GFG_SUBWINDOW_TOP_PANEL_HEIGHT/2 );
	else if( m_dsCaption.m_iAlign == FONT_ALIGN_RIGHT )
		m_dsCaption.draw( m_fGlobalXpos + m_fWidth, m_fGlobalYpos + m_fHeight - GFG_SUBWINDOW_TOP_PANEL_HEIGHT/2 );
}


void CSubWindow::windowTopPanelDraw( ScissorBox &scissorBox )
{
	glBindTexture(GL_TEXTURE_2D, g_panelID );
	glBegin( GL_TRIANGLE_FAN );
		if( m_bFocused )
			setColor( GFG_COLOR_WINDOW_TOP_LIGHT );
		else
			setColor(  GFG_COLOR_WINDOW_TOP_LIGHTUNFOCUSED );
		
		glTexCoord2f( 0.5, 1 );
		glVertex2f( m_fGlobalXpos + m_fWidth/2, m_fGlobalYpos + m_fHeight );
		setColor( GFG_COLOR_WINDOW_TOP_DARK );

		glTexCoord2i( 0, 1 );	glVertex2f( m_fGlobalXpos, m_fGlobalYpos + m_fHeight  );

		setColor( GFG_COLOR_WINDOW_TOP_AVERAGE );
		glTexCoord2i( 0, 0 );	glVertex2f( m_fGlobalXpos, m_fGlobalYpos + m_fHeight - m_iTopPanelHeight );
		glTexCoord2i( 1, 0 );	glVertex2f( m_fGlobalXpos + m_fWidth, m_fGlobalYpos + m_fHeight - m_iTopPanelHeight );
		setColor( GFG_COLOR_WINDOW_TOP_DARK );
		glTexCoord2i( 1, 1 ); glVertex2f( m_fGlobalXpos + m_fWidth, m_fGlobalYpos + m_fHeight );
		
	glEnd();
}


//prirazeni komponenty
void CSubWindow::setComponent( CBaseComponent *component )
{
	if( component != NULL )
	{
		delete m_pComponent;	
		m_pComponent = NULL;

		m_pComponent = component;
		m_pComponent->setParent( this );
		
		m_pComponent->inputController( GFG_GOT_FOCUS, 0, 0, 0, 0 );
		component->setPosition( m_ScissorBox.x, m_ScissorBox.y );
		component->setSize( m_ScissorBox.w, m_ScissorBox.h );
	}
}

void CSubWindow::run()
{
	bool bChangeAlpha = false;
	
	//animace zavirani / otevirani okna
	if( m_bVisible && m_fAlphaMultiplier < 1.0f - GFG_SUBWINDOW_ALPHA_MULTIPLIER_STEP)
	{
		bChangeAlpha = true;
		m_fAlphaMultiplier += GFG_SUBWINDOW_ALPHA_MULTIPLIER_STEP;
	}
	else if( !m_bVisible && m_fAlphaMultiplier > 0.0f )
	{
		bChangeAlpha = true;
		m_fAlphaMultiplier -= GFG_SUBWINDOW_ALPHA_MULTIPLIER_STEP;
	}
	
	int value = (int)(m_fHeight - GFG_SUBWINDOW_TOP_PANEL_HEIGHT);
	
	//animace zabaleni / rozbaleni okna
	if( !m_bPacked && m_fPackOffset > 0 )
		m_fPackOffset-= min( m_iPackingStep, m_fPackOffset );
	else if( m_bPacked && m_fPackOffset < value )
		m_fPackOffset+=getFromRange( m_iPackingStep, 0.0f, value - m_fPackOffset );


	if( bChangeAlpha )
	{
		setAlphaMultiplier( m_fAlphaMultiplier );
	}

	if( m_pComponent )
		m_pComponent->run();
}

void CSubWindow::setAlphaMultiplier( float multiplier )
{ 
	if( multiplier > m_fAlphaMultiplier && !m_bVisible )
		return;

	m_fAlphaMultiplier = multiplier;	
	if( m_pComponent )
		m_pComponent->setAlphaMultiplier( m_fAlphaMultiplier );
	if( cancelButton != NULL )
		cancelButton->setAlphaMultiplier( m_fAlphaMultiplier );
	if( packButton != NULL )
		packButton->setAlphaMultiplier( m_fAlphaMultiplier );
}
