/*
	Rogue Trader.
	Copyright (c) 2007, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################
//
//
// Class Name:   GuiButton
// Purpose:      Simple button widget that responds to mouse over and click events.
//
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "GuiButton.h"

//------------------------------------------------------------------------------
// Constant initialisation
//------------------------------------------------------------------------------

// These constants control the shading of the button

const float GuiButton::INTENSITY_NORMAL_1 = 0.6f;    // normal fill brightness for the button: first shade
const float GuiButton::INTENSITY_NORMAL_2 = 0.8f;    // normal fill brightness for the button: second shade
const float GuiButton::INTENSITY_SHINE_1  = 1.0f;    // fill brightness around the shiny parts of the widget: first shade
const float GuiButton::INTENSITY_SHINE_2  = 0.8f;    // fill brightness around the shiny parts of the widget: second shade
const float GuiButton::INTENSITY_SHADE_1  = 0.3f;    // fill brightness around the shady parts of the widget: first shade
const float GuiButton::INTENSITY_SHADE_2  = 0.5f;    // fill brightness around the shady parts of the widget: second shade

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::GuiButton()
Purpose:  Default constructor for the button. Initialises button settings.
Params:   none
Returns:  n/a

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

GuiButton::GuiButton()
{
    // initialise the button settings to their defaults

    depressed           = false;
    highlighted         = false;
    label               = 0;
    font                = GLFontManager::getFont_main();
    fontSize            = 8;
    fontColorR          = 0.0f;
    fontColorG          = 0.0f;
    fontColorB          = 0.0f;
    highlightColorR     = 0.2f;
    highlightColorG     = 0.3f;
    highlightColorB     = 0.2f;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::lostFocus()
Purpose:  Lost focus event for the button
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GuiButton::lostFocus()
{
    this->depressed     = false;
    this->highlighted   = false;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::mousePressed()
Purpose:  Mouse pressed event for the button
Params:   Position of mouse, button pressed.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GuiButton::mousePressed( int x , int y , GLWindow::MouseButton button )
{
    // Set the button to depressed

    this->depressed = true;

    // If there is an event listener attached to this button then call the mousePressed event on it

    if ( eventListener )
    {
        eventListener->mousePressed(x,y,button);
    }

    // Play the button click sound

    SoundSystem::playSound( SoundManager::getSndButtonClick() );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::mousePressed()
Purpose:  Mouse pressed event for the button
Params:   Position of mouse, button pressed.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GuiButton::mouseReleased( int x , int y , GLWindow::MouseButton button )
{
    // If there is an event listener attached to this button then call the mouseReleased event on it
    // Only do this however if the button is depressed:

    if ( eventListener && this->depressed )
    {
        eventListener->mouseReleased(x,y,button);
    }

    // Set the button to not depressed

    this->depressed = false;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::mouseMoved()
Purpose:  Mouse moved event for the button. Sets button as highlighted:
Params:   Position of mouse, button pressed.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

// No unreferenced param warning

#pragma warning(disable:4100)

void GuiButton::mouseMoved( int x, int y )
{
    // If the button was not previously highlighted then play the button over sound:

    if ( this->highlighted == false )
    {
        SoundSystem::playSound( SoundManager::getSndButtonOver() );
    }

    // Set the button as highlighted:

    this->highlighted = true;
}

// Restore this to normal

#pragma warning(default:4100)

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::setLabel()
Purpose:  Sets the label used for the button
Params:   label
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GuiButton::setLabel( char * label )
{
    // Save label

    this->label = label;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::setFontSize()
Purpose:  Sets the font used for the button
Params:   label
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GuiButton::setFontSize( unsigned size )
{
    // Only set if size 4 or greater:

    if ( size >= 4 ) this->fontSize = size;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::setFontColor()
Purpose:  Sets the font color used for the button
Params:   font color
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GuiButton::setFontColor
(
    float r,
    float g,
    float b
)
{
    // Clamp color to normalised float range

    if ( r < 0 ) r = 0;
    if ( g < 0 ) g = 0;
    if ( b < 0 ) b = 0;
    if ( r > 1 ) r = 1;
    if ( g > 1 ) g = 1;
    if ( b > 1 ) b = 1;

    // Set the font color for the button

    fontColorR = r;
    fontColorG = g;
    fontColorB = b;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::setHighlightColor()
Purpose:  Sets the highlighted color used for the button
Params:   hightlighted color for the button
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GuiButton::setHighlightColor
(
    float r,
    float g,
    float b
)
{
    // Clamp color to normalised float range

    if ( r < 0 ) r = 0;
    if ( g < 0 ) g = 0;
    if ( b < 0 ) b = 0;
    if ( r > 1 ) r = 1;
    if ( g > 1 ) g = 1;
    if ( b > 1 ) b = 1;

    // Set the highlighted color for the button

    highlightColorR = r;
    highlightColorG = g;
    highlightColorB = b;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::setFont()
Purpose:  Sets the font used for the button
Params:   label
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GuiButton::setFont( GLFont * font )
{
    // Save the new font: but only if not null

    if ( font ) this->font = font;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GuiButton::windowRedrawing()
Purpose:  Draws the button.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GuiButton::windowRedrawing()
{
    //--------------------------------------------------------------------------
    // Make sure texturing and blending are disabled:
    //--------------------------------------------------------------------------

    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);

    //--------------------------------------------------------------------------
    // Get the base color for the widget:
    //--------------------------------------------------------------------------

    float baseColor[3] =
    {
        getColorR(),
        getColorG(),
        getColorB()
    };

    //--------------------------------------------------------------------------
    // Draw button differently depending on whether it is depressed or not
    //--------------------------------------------------------------------------

    if ( depressed )
    {
        //----------------------------------------------------------------------
        // Button is depressed
        //----------------------------------------------------------------------

        // Draw the main filled part of the button

        glBegin(GL_QUADS);

        glColor4f
        (
            baseColor[0] * INTENSITY_NORMAL_1,
            baseColor[1] * INTENSITY_NORMAL_1,
            baseColor[2] * INTENSITY_NORMAL_1,
            1.0f
        );

        glVertex2f
        (
            float( getPositionX() ),
            float( getPositionY() )
        );

        glVertex2f
        (
            float( getPositionX() + getSizeX()	),
            float( getPositionY()				)
        );

        glColor4f
        (
            baseColor[0] * INTENSITY_NORMAL_2,
            baseColor[1] * INTENSITY_NORMAL_2,
            baseColor[2] * INTENSITY_NORMAL_2,
            1.0f
        );

        glVertex2f
        (
            float( getPositionX() + getSizeX()	),
            float( getPositionY() + getSizeY()	)
        );

        glVertex2f
        (
            float( getPositionX()				),
            float( getPositionY() + getSizeY()	)
        );

        glEnd();

        // draw the shaded outline of the button: left and top borders

        glColor4f               // set color for first two lines
        (
            baseColor[0] * INTENSITY_SHADE_1,
            baseColor[1] * INTENSITY_SHADE_1,
            baseColor[2] * INTENSITY_SHADE_1,
            1.0f
        );

        glRecti                 // top side
        (
            getPositionX()				,
            getPositionY()				,
            getPositionX() + getSizeX()	,
            getPositionY() + 1
        );

        glRecti                 // left side
        (
            getPositionX()				,
            getPositionY()				,
            getPositionX() + 1			,
            getPositionY() + getSizeY()	
        );

        glColor4f               // set color for second two lines
        (
            baseColor[0] * INTENSITY_SHADE_2,
            baseColor[1] * INTENSITY_SHADE_2,
            baseColor[2] * INTENSITY_SHADE_2,
            1.0f
        );

        glRecti                 // top side
        (
            getPositionX() + 1,
            getPositionY() + 1,
            getPositionX() + getSizeX() - 1,
            getPositionY() + 2
        );

        glRecti                 // left side
        (
            getPositionX() + 1,
            getPositionY() + 1,
            getPositionX() + 2,
            getPositionY() + getSizeY() - 1
        );

        // Draw the shiny outline of the button: bottom and right borders

        glColor4f               // set color for first two lines
        (
            baseColor[0] * INTENSITY_SHINE_1,
            baseColor[1] * INTENSITY_SHINE_1,
            baseColor[2] * INTENSITY_SHINE_1,
            1.0f
        );

        glRecti                 // left side
        (
            getPositionX() + getSizeX() - 1,
            getPositionY(),
            getPositionX() + getSizeX(),
            getPositionY() + getSizeY()
        );

        glRecti                 // bottom side
        (
            getPositionX(),
            getPositionY() + getSizeY() - 1,
            getPositionX() + getSizeX(),
            getPositionY() + getSizeY()
        );

        glColor4f               // set color for second two lines
        (
            baseColor[0] * INTENSITY_SHINE_2,
            baseColor[1] * INTENSITY_SHINE_2,
            baseColor[2] * INTENSITY_SHINE_2,
            1
        );

        glRecti                 // left side
        (
            getPositionX() + getSizeX() - 2,
            getPositionY() + 1,
            getPositionX() + getSizeX() - 1,
            getPositionY() + getSizeY() - 1
        );

        glRecti                 // bottom side
        (
            getPositionX() + 1,
            getPositionY() + getSizeY() - 2,
            getPositionX() + getSizeX() - 1,
            getPositionY() + getSizeY() - 1
        );

    }
    else
    {
        //----------------------------------------------------------------------
        // Button not depressed
        //----------------------------------------------------------------------

        // Draw the main filled part of the button

        glBegin(GL_QUADS);

        glColor4f
        (
            baseColor[0] * INTENSITY_NORMAL_2,
            baseColor[1] * INTENSITY_NORMAL_2,
            baseColor[2] * INTENSITY_NORMAL_2,
            1.0f
        );

        glVertex2f
        (
            float( getPositionX() ),
            float( getPositionY() )
        );

        glVertex2f
        (
            float( getPositionX() + getSizeX()	),
            float( getPositionY()				)
        );

        glColor4f
        (
            baseColor[0] * INTENSITY_NORMAL_1,
            baseColor[1] * INTENSITY_NORMAL_1,
            baseColor[2] * INTENSITY_NORMAL_1,
            1.0f
        );

        glVertex2f
        (
            float( getPositionX() + getSizeX()	),
            float( getPositionY() + getSizeY()	)
        );

        glVertex2f
        (
            float( getPositionX()				),
            float( getPositionY() + getSizeY()	)
        );

        glEnd();

        // draw the shiny outline of the button: left and top borders

        glColor4f               // set color for first two lines
        (
            baseColor[0] * INTENSITY_SHINE_1,
            baseColor[1] * INTENSITY_SHINE_1,
            baseColor[2] * INTENSITY_SHINE_1,
            1.0f
        );

        glRecti                 // top side
        (
            getPositionX(),
            getPositionY(),
            getPositionX() + getSizeX(),
            getPositionY() + 1
        );

        glRecti                 // left side
        (
            getPositionX(),
            getPositionY(),
            getPositionX() + 1,
            getPositionY() + getSizeY()
        );

        glColor4f               // set color for second two lines
        (
            baseColor[0] * INTENSITY_SHINE_2,
            baseColor[1] * INTENSITY_SHINE_2,
            baseColor[2] * INTENSITY_SHINE_2,
            1
        );

        glRecti                 // top side
        (
            getPositionX() + 1,
            getPositionY() + 1,
            getPositionX() + getSizeX() - 1,
            getPositionY() + 2
        );

        glRecti                 // left side
        (
            getPositionX() + 1,
            getPositionY() + 1,
            getPositionX() + 2,
            getPositionY() + getSizeY() - 1
        );

        // Draw the shaded outline of the button: bottom and right borders

        glColor4f               // set color for first two lines
        (
            baseColor[0] * INTENSITY_SHADE_1,
            baseColor[1] * INTENSITY_SHADE_1,
            baseColor[2] * INTENSITY_SHADE_1,
            1
        );

        glRecti                 // left side
        (
            getPositionX() + getSizeX() - 1,
            getPositionY(),
            getPositionX() + getSizeX(),
            getPositionY() + getSizeY()
        );

        glRecti                 // bottom side
        (
            getPositionX(),
            getPositionY() + getSizeY() - 1,
            getPositionX() + getSizeX(),
            getPositionY() + getSizeY()
        );

        glColor4f               // set color for second two lines
        (
            baseColor[0] * INTENSITY_SHADE_2,
            baseColor[1] * INTENSITY_SHADE_2,
            baseColor[2] * INTENSITY_SHADE_2,
            1.0f
        );

        glRecti                 // left side
        (
            getPositionX() + getSizeX() - 2,
            getPositionY() + 1,
            getPositionX() + getSizeX() - 1,
            getPositionY() + getSizeY() - 1
        );

        glRecti                 // bottom side
        (
            getPositionX() + 1,
            getPositionY() + getSizeY() - 2,
            getPositionX() + getSizeX() - 1,
            getPositionY() + getSizeY() - 1
        );
    }

    //--------------------------------------------------------------------------
    // Draw the button highlight if the button is highlighted
    //--------------------------------------------------------------------------

    if ( this->highlighted )
    {
        // Enable blending and texturing:

        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);

        glBlendFunc(GL_ONE,GL_ONE);

        // Bind the button highlight texture:

        GLTextureManager::getTexButtonHighlight()->bind();

        // Draw the highlight:

        glColor3f( highlightColorR, highlightColorG , highlightColorB );

        glBegin(GL_QUADS);

            glTexCoord2f(0.0f,0.0f);  glVertex2f( float( position_x			 ), float( position_y			) );
            glTexCoord2f(1.0f,0.0f);  glVertex2f( float( position_x + size_x ), float( position_y			) );
            glTexCoord2f(1.0f,1.0f);  glVertex2f( float( position_x + size_x ), float( position_y + size_y  ) );
            glTexCoord2f(0.0f,1.0f);  glVertex2f( float( position_x          ), float( position_y + size_y	) );

        glEnd();
    }

    //--------------------------------------------------------------------------
    // Enable the extra gl clipping planes and set them so that text cannot
    // extend beyond the button
    //--------------------------------------------------------------------------

    glEnable(GL_CLIP_PLANE0);
    glEnable(GL_CLIP_PLANE1);
    glEnable(GL_CLIP_PLANE2);
    glEnable(GL_CLIP_PLANE3);

    double planeT[] = {  0 ,  1 , 0 , -getPositionY() -2               };   // top clipping plane equation
    double planeB[] = {  0 , -1 , 0 ,  getPositionY() + getSizeY() - 2 };   // bottom clipping plane equation
    double planeL[] = {  1 ,  0 , 0 , -getPositionX() -2              };    // left clipping plane equation
    double planeR[] = { -1 ,  0 , 0 ,  getPositionX() + getSizeX() - 2 };   // right clipping plane equation

    glClipPlane( GL_CLIP_PLANE0 , planeT );
    glClipPlane( GL_CLIP_PLANE1 , planeB );
    glClipPlane( GL_CLIP_PLANE2 , planeL );
    glClipPlane( GL_CLIP_PLANE3 , planeR );

    //--------------------------------------------------------------------------
    // Draw the button label: adjust its position slightly if the button is depressed
    //--------------------------------------------------------------------------

     if ( depressed )
     {

        font->drawStringCentered
        (
            label,
            float( getPositionX() + 1	),
            float( getPositionY() + 1	),
            float( getSizeX()			),
            float( getSizeY()			),
            float( fontSize				),
            fontColorR,
            fontColorG,
            fontColorB
        );
     }
     else
     {

        font->drawStringCentered
        (
            label,
            float( getPositionX()	),
            float( getPositionY()	),
            float( getSizeX()		),
            float( getSizeY()		),
            float( fontSize			),
            fontColorR,
            fontColorG,
            fontColorB
        );
     }

    //--------------------------------------------------------------------------
    // Disable the gl clipping planes
    //--------------------------------------------------------------------------

    glDisable(GL_CLIP_PLANE0);
    glDisable(GL_CLIP_PLANE1);
    glDisable(GL_CLIP_PLANE2);
    glDisable(GL_CLIP_PLANE3);
}
