/*
	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:   GuiSlider
// Purpose:      A slider widget that can be used to change settings.
//
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "GuiSlider.h"

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

// Width of the actual moveable slider part (the bar I call it)

const int GuiSlider::BAR_WIDTH = 13;

// These constants control the shading of the slider

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

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

Name:     GuiSlider::GuiSlider
Purpose:  Default constructor for the GuiSlider
Params:   none
Returns:  n/a

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

GuiSlider::GuiSlider()
{
    // Set default slider size:

    setSize( 150, 15 );

    // Set the offset (how many pixels along the slider) of the moveable bar part

    barOffset = 0;

    // Not dragging the bar

    draggingBar = false;

    // Set the range of values that can be set with the slider

    sliderMinValue = 0;
    sliderMaxValue = 1;
}

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

Name:     GuiSlider::getValue
Purpose:  Gets the value represented by the slider position. The value depends
          on the slider bar's current position and the range of values the
          slider can scroll through.
Params:   none
Returns:  value of setting represented by the slider bar position

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

float GuiSlider::getValue()
{
    // Get the linear amount of mixing between the minimum value and the maximum value
    // representable by the slider, based on the slider bar position.

    float mix = float(barOffset) / float(getSizeX() - BAR_WIDTH);

    // Now return the value represented by the slider bar position

    return ( 1.0f - mix ) * sliderMinValue + mix * sliderMaxValue;
}

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

Name:     GuiSlider::setValue
Purpose:  Sets the value being represented by the slider and automatically moves
          the slider bar.
Params:   Value to represent with the slider bar position.
Returns:  none

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

void GuiSlider::setValue( float value )
{
    // If the value is less than the minimum or maximum range of the slider then clamp

    if ( value < sliderMinValue ) value = sliderMinValue;
    if ( value > sliderMaxValue ) value = sliderMaxValue;

    // Get how much the value is a mix between the minimum and maximum values

    float mixAmount = ( value - sliderMinValue ) / ( sliderMaxValue - sliderMinValue );

    // Now figure out the maximum offset of the slider bar

    int maxBarOffset = getSizeX() - BAR_WIDTH;

    // Figure out the offset the bar should be at to represent (or at least as close as possible) the value

    barOffset = int( maxBarOffset * mixAmount );
}

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

Name:     GuiSlider::setRange
Purpose:  Sets the range of values that the slider can scroll through
Params:   Minimum and maximum value representable by slider.
Returns:  none

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

void GuiSlider::setRange( float minValue , float maxValue )
{
    // Don't set if the maximum value is less than the minimum value

    if ( maxValue < minValue ) return;

    // Otherwise set:

    sliderMinValue = minValue;
    sliderMaxValue = maxValue;
}

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

Name:     GuiSlider::mouseMoved
Purpose:  Changes slider position if left mouse button is down and mouse is over
          the slider control.
Params:   Mouse position
Returns:  none

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

void GuiSlider::mouseMoved( int x , int y )
{
    // If the bar is being dragged then move it:

    if ( draggingBar )
    {
        // Move bar by amount the mouse has moved since dragging started

        barOffset  = barOffsetDragStart;
        barOffset += x - mouseDragStartX;

        // If the bar has moved past the bounding box of this widget then put it back

        if ( barOffset + BAR_WIDTH > getSizeX() )
        {
            barOffset = getSizeX() - BAR_WIDTH;
        }

        if ( barOffset < 0 )
        {
            barOffset = 0;
        }

        // If there is an event listener attached to the slider, notify the
        // listener that the bar has moved by sending a mouse moved event:

        if ( eventListener ) eventListener->mouseMoved(x,y);
    }
}

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

Name:     GuiSlider::mousePressed
Purpose:  Mouse pressed event. Sees if the left mouse button was pressed over
          the bar part of the slider. Enables dragging of the bar if this is
          true.
Params:   Mouse position, button that was pressed
Returns:  none

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

// No unreferenced param warning

#pragma warning(disable:4100)   

void GuiSlider::mousePressed(int x,int y,GLWindow::MouseButton button)
{
    // See if the mouse button is the left mouse button

    if ( button == GLWindow::MOUSE_BUTTON_LEFT )
    {
        // LMB: see if it is over the bar part of the slider

        if (
              x >= getPositionX() + barOffset
              &&
              x <  getPositionX() + barOffset + BAR_WIDTH
            )
        {
            // Mouse clicked over the bar: enable dragging

            draggingBar = true;

            // Set at what mouse pos we started dragging the widget from

            mouseDragStartX = x;

            // Set the offset of the bar when we first started dragging it

            barOffsetDragStart = barOffset;
        }
    }

}

// Restore this to normal

#pragma warning(default:4100)

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

Name:     GuiSlider::mouseReleased
Purpose:  Mouse released event. Stops dragging the bar if the left mouse button
          was released.
Params:   Mouse position, button that was released
Returns:  none

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

// No unreferenced param warning

#pragma warning(disable:4100)

void GuiSlider::mouseReleased(int x,int y,GLWindow::MouseButton button)
{
    // See if the mouse button is the left mouse button

    if ( button == GLWindow::MOUSE_BUTTON_LEFT )
    {
        // Mouse released: stop dragging the bar

        draggingBar = false;
    }
}

// Restore this to normal

#pragma warning(default:4100)

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

Name:     GuiSlider::lostFocus
Purpose:  Lost focus event. Stops the dragging of the bar.
Params:   none
Returns:  none

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

void GuiSlider::lostFocus()
{
    // Stop dragging the bar

    draggingBar = false;
}


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

Name:     GuiSlider::windowRedrawing
Purpose:  Draws the slider
Params:   none
Returns:  none

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

void GuiSlider::windowRedrawing()
{
    //--------------------------------------------------------------------------
    // Draw the central crevice part of the slider
    //--------------------------------------------------------------------------

    // Setup opengl state first

    glPushAttrib(GL_ENABLE_BIT);

    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);
    glDisable(GL_LIGHTING);
    glDisable(GL_CULL_FACE);

    // Figure out where the center y value is from the height of the slider

    int centerY = getPositionY() + getSizeY() / 2;

    // Draw first pair of left and top lines

    glColor4f
    (
        getColorR() * INTENSITY_SHADE_1,
        getColorG() * INTENSITY_SHADE_1,
        getColorB() * INTENSITY_SHADE_1,
        1
    );

    glRecti
    (
        getPositionX(),
        centerY - 2,
        getPositionX() + getSizeX(),
        centerY - 1
    );

    glRecti
    (
        getPositionX(),
        centerY - 2,
        getPositionX() + 1,
        centerY + 3
    );

    // Draw the second pair of left and top lines

    glColor4f
    (
        getColorR() * INTENSITY_SHADE_2,
        getColorG() * INTENSITY_SHADE_2,
        getColorB() * INTENSITY_SHADE_2,
        1
    );

    glRecti
    (
        getPositionX() + 1,
        centerY - 1,
        getPositionX() + getSizeX() - 1,
        centerY
    );

    glRecti
    (
        getPositionX() + 1,
        centerY - 1,
        getPositionX() + 2,
        centerY + 1
    );

    // Draw first pair of right and bottom lines

    glColor4f
    (
        getColorR() * INTENSITY_SHINE_1,
        getColorG() * INTENSITY_SHINE_1,
        getColorB() * INTENSITY_SHINE_1,
        1
    );

    glRecti
    (
        getPositionX() + getSizeX() - 1,
        centerY - 2,
        getPositionX() + getSizeX(),
        centerY + 3
    );

    glRecti
    (
        getPositionX() + 2,
        centerY + 1,
        getPositionX() + getSizeX(),
        centerY + 2
    );

    // Draw second pair of right and bottom lines

    glColor4f
    (
        getColorR() * INTENSITY_SHINE_2,
        getColorG() * INTENSITY_SHINE_2,
        getColorB() * INTENSITY_SHINE_2,
        1
    );

    glRecti
    (
        getPositionX() + getSizeX() - 2,
        centerY - 1,
        getPositionX() + getSizeX() - 1,
        centerY + 1
    );

    glRecti
    (
        getPositionX() + 1,
        centerY + 2,
        getPositionX() + getSizeX(),
        centerY + 3
    );

    // Draw the black middle line

    glColor4f
    (
        0,
        0,
        0,
        1
    );

    glRecti
    (
        getPositionX() + 2,
        centerY,
        getPositionX() + getSizeX() - 2,
        centerY + 1
    );

    //--------------------------------------------------------------------------
    // Draw the movable slider part (the bar)
    //--------------------------------------------------------------------------

    // Draw the main filled part of the bar

    glBegin(GL_QUADS);

        glColor4f
        (
            getColorR() * INTENSITY_NORMAL_1,
            getColorG() * INTENSITY_NORMAL_1,
            getColorB() * INTENSITY_NORMAL_1,
            1
        );

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

        glVertex2f
        (
            float( getPositionX() + barOffset + BAR_WIDTH	),
            float( getPositionY()							)
        );

        glColor4f
        (
            getColorR() * INTENSITY_NORMAL_2,
            getColorG() * INTENSITY_NORMAL_2,
            getColorB() * INTENSITY_NORMAL_2,
            1
        );

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

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

    glEnd();

    // Draw the shiny outline of the bar

    glColor4f           // set color for first pair of top and left lines
    (
        getColorR() * INTENSITY_SHINE_1,
        getColorG() * INTENSITY_SHINE_1,
        getColorB() * INTENSITY_SHINE_1,
        1
    );

    glRecti
    (
        getPositionX() + barOffset,
        getPositionY(),
        getPositionX() + barOffset + BAR_WIDTH,
        getPositionY() + 1
    );

    glRecti
    (
        getPositionX() + barOffset,
        getPositionY(),
        getPositionX() + barOffset + 1,
        getPositionY() + getSizeY()
    );

    glColor4f           // set color for second pair of top and left lines
    (
        getColorR() * INTENSITY_SHINE_2,
        getColorG() * INTENSITY_SHINE_2,
        getColorB() * INTENSITY_SHINE_2,
        1
    );

    glRecti
    (
        getPositionX() + barOffset + 1,
        getPositionY() + 1,
        getPositionX() + barOffset + BAR_WIDTH - 1,
        getPositionY() + 2
    );

    glRecti
    (
        getPositionX() + barOffset + 1,
        getPositionY() + 1,
        getPositionX() + barOffset + 2,
        getPositionY() + getSizeY() - 1
    );

    glColor4f           // set color for first pair of right and bottom lines
    (
        getColorR() * INTENSITY_SHADE_1,
        getColorG() * INTENSITY_SHADE_1,
        getColorB() * INTENSITY_SHADE_1,
        1
    );

    glRecti
    (
        getPositionX() + barOffset + BAR_WIDTH - 1,
        getPositionY(),
        getPositionX() + barOffset + BAR_WIDTH,
        getPositionY() + getSizeY()
    );

    glRecti
    (
        getPositionX() + barOffset,
        getPositionY() + getSizeY() -1,
        getPositionX() + barOffset + BAR_WIDTH,
        getPositionY() + getSizeY()
    );

    glColor4f           // set color for second pair of right and bottom lines
    (
        getColorR() * INTENSITY_SHADE_2,
        getColorG() * INTENSITY_SHADE_2,
        getColorB() * INTENSITY_SHADE_2,
        1
    );

    glRecti
    (
        getPositionX() + barOffset + BAR_WIDTH - 2,
        getPositionY() + 1,
        getPositionX() + barOffset + BAR_WIDTH - 1,
        getPositionY() + getSizeY() - 1
    );

    glRecti
    (
        getPositionX() + barOffset + 1,
        getPositionY() + getSizeY() - 2,
        getPositionX() + barOffset + BAR_WIDTH - 1,
        getPositionY() + getSizeY() - 1
    );

    // Restore prior gl state

    glPopAttrib();
}
