/*
 * widgets/SliderWidget.cc:
 *
 * Copyright (C) 2000 John Watson, jwatson@tempusmud.com
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 */

#include <iostream>
#include <iomanip>

#include "SliderWidget.h"

SliderWidget::SliderWidget( int w, int h, 
                            enum SliderType type,
                            int maxTick, 
                            int majorInterval, 
                            int startTick ) : Widget( w, h ) {

        _name = "SliderWidget";

        _type = type;
        _maxTick = maxTick;
        _majorTickInterval = majorInterval;
        _curTick = startTick;
        
        int majorAxisLen = ( _type == SLIDER_VERTICAL ) ? h : w;
        int minorAxisLen = ( _type == SLIDER_VERTICAL ) ? w : h;

        //
        // knob is majorAxisLen in major axis, 1/2 majorAxisLen in minor axis
        //
        _railMargin = minorAxisLen / 4;

        if ( _maxTick % _majorTickInterval ) {
                cout << " :: Error: maxTick not evenly divisible by majorInterval" << endl;
        }

        _tickPixelInterval = ( majorAxisLen - 2 * _railMargin ) / _maxTick;

        if ( _tickPixelInterval <= 0 ) {
                cout << " :: Error: maxTick too big for major axis." << endl;
                //                return;
        }
        
        _railLen = _tickPixelInterval * _maxTick;

        // recalculate the margin to center the slider

        _railMargin = ( majorAxisLen - _railLen ) / 2;

        if ( _type == SLIDER_VERTICAL ) {
                _knobWidget = new SliderKnobWidget( minorAxisLen / 2, minorAxisLen / 2, this );
                add( _knobWidget, 
                     0, _railMargin + ( _curTick * _tickPixelInterval ) - _knobWidget->getHeight() / 2 );
        }
        else {
                _knobWidget = new SliderKnobWidget( minorAxisLen / 2, minorAxisLen / 2, this );
                add( _knobWidget, 
                     _railMargin + ( _curTick * _tickPixelInterval ) - _knobWidget->getWidth() / 2, 0 );
        }

        if ( _type == SLIDER_VERTICAL ) {
                _railPos[0][0] = _railPos[1][0] = w / 2;
                _railPos[0][1] = _railMargin;
                _railPos[1][1] = _railMargin + _railLen;
        }
        else if ( _type == SLIDER_HORIZONTAL ) {
                _railPos[0][1] = _railPos[1][1] = h / 2;
                _railPos[0][0] = _railMargin;
                _railPos[1][0] = _railMargin + _railLen;
        }
        else {
                cout << " unhandled type" << endl;
                return;
        }

        _railListIndex = glGenLists( 1 );

        if ( _railListIndex == 0 ) { 
                cout << " Unable to generate list." << endl;
                return;
        }

        glNewList( _railListIndex, GL_COMPILE );
        {
                glColor4fv( getFGColor4fv() );
                glBegin( GL_LINES );
                {
                        glVertex2f( _railPos[0][0], _railPos[0][1] );
                        glVertex2f( _railPos[1][0], _railPos[1][1] );
                        

                        int minorTickVerts[2] = { minorAxisLen / 3, minorAxisLen * 2 / 3 };
                        int majorTickVerts[2] = { minorAxisLen / 4, minorAxisLen * 3 / 4 };
                        
                        if ( _type == SLIDER_HORIZONTAL ) {
                                

                                for ( int i = 0, x = 0 ; i <= _maxTick; ++i, x += _tickPixelInterval ) {
                                        if ( ( i % _majorTickInterval ) == 0 ) {
                                                glVertex2f( _railPos[0][0] + x,
                                                            majorTickVerts[0] );
                                                glVertex2f( _railPos[0][0] + x, 
                                                            majorTickVerts[1] );
                                        }
                                        else {
                                                glVertex2f( _railPos[0][0] + x, 
                                                            minorTickVerts[0] );
                                                glVertex2f( _railPos[0][0] + x, 
                                                            minorTickVerts[1] );
                                        }
                                }
                        }

                        else if ( _type == SLIDER_VERTICAL ) {
                                
                                for ( int i = 0, y = 0 ; i <= _maxTick; ++i, y += _tickPixelInterval ) {
                                        if ( ( i % _majorTickInterval ) == 0 ) {
                                                glVertex2f( majorTickVerts[0],
                                                            _railPos[0][1] + y );
                                                glVertex2f( majorTickVerts[1],
                                                            _railPos[0][1] + y );
                                        }
                                        else {
                                                glVertex2f( minorTickVerts[0],
                                                            _railPos[0][1] + y );
                                                glVertex2f( minorTickVerts[1],
                                                            _railPos[0][1] + y );
                                        }
                                }
                        }
                }
                glEnd();
        }
        glEndList();
        
}

void SliderWidget::paint() {

        Widget::paint();

        glCallList( _railListIndex );
}

/**
 * returns the number of pixels we slid
 */
int SliderWidget::attemptSlide( int amount ) {
        
        int target_tick = _curTick + ( amount / _tickPixelInterval );
        int oldPos = ( _type == SLIDER_VERTICAL ) ? _knobWidget->getPos()[1] : _knobWidget->getPos()[0];
        int newPos = setValue( target_tick );
    
        return newPos - oldPos;
}

bool SliderWidget::mousePressed( SDL_MouseButtonEvent * evt ) {

        if ( Widget::mousePressed( evt ) )
                return true;
        
        int targetPos = _type == SLIDER_VERTICAL ? evt->y : evt->x;
        int curTickPos = _railMargin + _curTick * _tickPixelInterval;

        if ( abs( targetPos - curTickPos ) < _tickPixelInterval )
                return false;

        int retval = 0;

        //
        // left button
        //
        if ( evt->button == 1 ) {
                retval = attemptSlide( ( targetPos > curTickPos ) ? _tickPixelInterval : - _tickPixelInterval );
        }
        
        //
        // middle button
        //
        if ( evt->button == 2 ) {
                retval = attemptSlide( targetPos - curTickPos );
                
        }

        return retval;
}

int SliderWidget::setValue( int target_tick ) {

        target_tick = max( 0, target_tick );
        target_tick = min( _maxTick, target_tick );
        
        if ( target_tick == _curTick ) {
                return ( _type == SLIDER_VERTICAL ) ? _knobWidget->getY(): _knobWidget->getX();
        }
               
        int newPos = target_tick * _tickPixelInterval + _railMargin -
                ( ( _type == SLIDER_VERTICAL ) ? 
                  ( _knobWidget->getHeight() / 2 ) :
                  ( _knobWidget->getWidth() / 2 ) );

        
        if ( _type == SLIDER_VERTICAL ) {
                _knobWidget->setPosition( 0, newPos );
        }
        else if ( _type == SLIDER_HORIZONTAL ) {
                _knobWidget->setPosition( newPos, 0 );
        }
        else {
                cout << " bogus type in attemptSlide" << endl;
                return ( _type == SLIDER_VERTICAL ) ? _knobWidget->getY(): _knobWidget->getX();

        }
        
        _curTick = target_tick;
        
        notifyActionListeners( this, "value changed" );
        
        return newPos;
}

SliderKnobWidget::SliderKnobWidget( int w, int h, class SliderWidget * slider ) : ButtonWidget( w, h ) {
        
        _name = "SliderKnobWidget";

        setButtonColors( getLookAndFeel()->getPrimaryColor( COLOR_TWO ), 0, 0 );

        _grabbedAtRelative = -1;
        _slider = slider;
        
        int minorAxisLen = ( _slider->_type == SLIDER_VERTICAL ) ? w : h;
        
        static float horizVerts[5][2] = { {                 0,    0 },
                                          {      minorAxisLen - 1,    0 },
                                          {      minorAxisLen - 1, minorAxisLen / 2 },
                                          {  minorAxisLen / 2, minorAxisLen - 1 },
                                          {                 0, minorAxisLen / 2 } };

        static float vertVerts[5][2] = { {                 0,                 0 },
                                         {  minorAxisLen / 2,                 0 },
                                         {      minorAxisLen,  minorAxisLen / 2 },
                                         {  minorAxisLen / 2,      minorAxisLen },
                                         {                 0,      minorAxisLen } };
        
        _displayListIndex = glGenLists( 1 );
        
        if ( _displayListIndex == 0 ) { 
                cout << " Unable to generate list for SliderKnobWidget." << endl;
                return;
        }

        glNewList( _displayListIndex, GL_COMPILE );
        {
                glBegin( GL_POLYGON );
                {
                        for ( int i = 0; i < 5; ++i ) {
                                if ( _slider->_type == SLIDER_VERTICAL ) {
                                        glVertex2fv( vertVerts[i] );
                                }
                                else {
                                        glVertex2fv( horizVerts[i] );
                                }
                        }
                }
                glEnd();

        }
        glEndList();
}

void SliderKnobWidget::paint() {
        
        glColor4fv( getBGColor4fv() );

        glCallList( _displayListIndex );

        if ( isFocused() ) {
                glColor4fv( getLookAndFeel()->getHighlightColor() );
        }
        else {
                glColor4fv( _borderColor );
        }
        
        glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
        glCallList( _displayListIndex );
        glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

}

bool SliderKnobWidget::mousePressed( SDL_MouseButtonEvent * evt ) {
        _grabbedAtRelative = _slider->_type == SLIDER_VERTICAL ? evt->y : evt->x;
        setInputControllingWidget( this );
        return ButtonWidget::mousePressed( evt );
}

bool SliderKnobWidget::mouseReleased( SDL_MouseButtonEvent * evt ) { 
        _grabbedAtRelative = -1;
        unsetInputControllingWidget( this );
        return ButtonWidget::mouseReleased( evt );
}

bool SliderKnobWidget::mouseMoved( SDL_MouseMotionEvent * evt ) { 

        if ( _grabbedAtRelative >= 0 ) {
                int pos = ( _slider->_type == SLIDER_VERTICAL ) ? 
                        ( (Sint16) evt->y ) : 
                        ( (Sint16) evt->x );
                
                pos -= _grabbedAtRelative;
                
                _slider->attemptSlide( pos );

                return true;
        }

        return false;
}

