/*
 * pawscrollbar.h - Author: Andrew Craig
 *
 * Copyright (C) 2003 Atomic Blue (info@planeshift.it, http://www.atomicblue.org)
 *
 *
 * 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 (version 2 of the License)
 * 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.
 *
 */
// pawscrollbar.cpp: implementation of the pawscrollbar class.
//
//////////////////////////////////////////////////////////////////////

#include <psconfig.h>
#include <iutil/virtclk.h>
#include "pawscrollbar.h"
#include "pawsmanager.h"
#include "pawsbutton.h"

#define SCROLL_TICKS         150

#define THUMB_IMAGE          "ScrollBar Thumb"
#define THUMB_MOVING_IMAGE   "ScrollBar Thumb Moving"
#define THUMB_MARGIN         2
#define SCROLL_UP_SND        "gui.scrollup"
#define SCROLL_DOWN_SND      "gui.scrolldown"

class pawsThumb : public pawsWidget
{
public:
    bool OnMouseUp( int button, int modifiers, int x, int y )
    {
        if ( parent )
            return parent->OnMouseUp( button, modifiers, x, y );
        else
            return false;
    }
};

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
pawsScrollBar::pawsScrollBar()
{
    currentValue    = 0;
    maxValue        = 0;
    minValue        = 0;
    tickValue       = 1;
    horizontal      = false;
    reversed        = false;
    limited         = true;

    // Used when the mouse button is held down
    clock = CS_QUERY_REGISTRY( PawsManager::GetSingleton().GetObjectRegistry(), iVirtualClock );

    scrollTicks = clock->GetCurrentTicks();
    mouseDown = false;
    
    upButton = downButton = NULL;
    
    thumb = NULL;
    mouseIsDraggingThumb = false;
}


pawsScrollBar::~pawsScrollBar()
{

}

bool pawsScrollBar::Setup( iDocumentNode* node )
{
    // Check for direction 
    csRef<iDocumentAttribute> directionAttribute = node->GetAttribute("direction");
    if ( directionAttribute )
    {
        csString value( directionAttribute->GetValue() );
        if ( value == "horizontal" )  
            SetHorizontal(true);
        else
            SetHorizontal(false);
    }

    return true;
}

void pawsScrollBar::SetMaxValue( float value )
{
    maxValue = value;
    if ( limited )
        LimitCurrentValue();
    if (parent != NULL)
        parent->OnScroll( SCROLL_AUTO, this );
    SetThumbLayout();
}

void pawsScrollBar::SetMinValue( float value )
{
    minValue = value;
    if ( limited )
        LimitCurrentValue();
    if (parent != NULL)
        parent->OnScroll( SCROLL_AUTO, this );
    SetThumbLayout();
}

void pawsScrollBar::SetCurrentValue( float value, bool triggerEvent )
{
    if (reversed)
        currentValue = maxValue-value;
    else
        currentValue = value;
    if ( limited )
        LimitCurrentValue();
    SetThumbLayout();
    
    if (triggerEvent)
        parent->OnScroll( SCROLL_SET, this );
}

void pawsScrollBar::SetHorizontal (bool value)
{
    horizontal = value;
    SetThumbLayout();
}

bool pawsScrollBar::PostSetup()
{
    // Create the scroll up/left button
    upButton = (pawsButton*)PawsManager::GetSingleton().CreateWidget( "pawsButton" );
    upButton->SetParent( this );
    int flags;
    if (!horizontal)
    {
        flags = ATTACH_TOP | ATTACH_RIGHT;
        upButton->SetBackground("Up Arrow");
    }
    else
    {
        flags = ATTACH_LEFT | ATTACH_BOTTOM;
        upButton->SetBackground("Left Arrow");
    }
    
    //Add the sound
    upButton->SetSound(SCROLL_UP_SND);

    upButton->SetAttachFlags( flags );
    upButton->SetID( SCROLL_UP );
    AddChild( upButton );

    // Create the scroll down button.
    downButton  = ( pawsButton *) PawsManager::GetSingleton().CreateWidget( "pawsButton" );
    downButton->SetParent( this );
    if (!horizontal)
    {
        flags = ATTACH_BOTTOM | ATTACH_RIGHT;
        downButton->SetBackground("Down Arrow");
    }
    else
    {
        flags = ATTACH_RIGHT | ATTACH_BOTTOM;
        downButton->SetBackground("Right Arrow");
    }

    //Add the sound
    downButton->SetSound(SCROLL_DOWN_SND);

    downButton->SetAttachFlags( flags );
    downButton->SetID( SCROLL_DOWN );
    AddChild( downButton );
    
    SetButtonLayout();
    
    
    thumb = new pawsThumb();
    AddChild(thumb);
    thumb->SetBackground(THUMB_IMAGE);
    thumb->SetID(SCROLL_THUMB);
    
    SetThumbVisibility();
    if (thumb->IsVisible())
        SetThumbLayout();
    return true;
}

void pawsScrollBar::SetThumbVisibility()
{
    if (thumb != NULL)
    {
        if (GetScrollBarSize()*1 < GetThumbScaleLength())
            thumb->Show();
        else
            thumb->Hide();
    }
}

void pawsScrollBar::OnResize()
{
    SetThumbVisibility();
    if (thumb!=NULL  &&  thumb->IsVisible())
        SetThumbLayout();

    SetButtonLayout();
}

void pawsScrollBar::SetThumbLayout()
{
    int scrollBarSize = GetScrollBarSize();
    
    if (thumb == NULL)
        return;

    thumb->SetSize(scrollBarSize-2*THUMB_MARGIN, scrollBarSize-2*THUMB_MARGIN);
    
    if (maxValue == 0)
    {
        if (horizontal)
            thumb->MoveTo(screenFrame.xmin+scrollBarSize, screenFrame.ymin+THUMB_MARGIN);
        else
            thumb->MoveTo(screenFrame.xmin+THUMB_MARGIN, screenFrame.ymin+scrollBarSize);
    }
    else
    {
        if (horizontal)
            thumb->MoveTo(screenFrame.xmin+scrollBarSize
                             + 
                          int((currentValue-minValue)/(maxValue-minValue)*GetThumbScaleLength()),
                          screenFrame.ymin+THUMB_MARGIN);
        else
        {            
            thumb->MoveTo(screenFrame.xmin+THUMB_MARGIN,
                          screenFrame.ymin+scrollBarSize
                             + 
                          int((currentValue-minValue)/(maxValue-minValue)*GetThumbScaleLength()));
        }                             
    }
}

bool pawsScrollBar::OnMouseDown( int button, int modifiers, int x, int y )
{
    if (WidgetAt(x, y) == thumb)
    {
        if (horizontal)
            thumbDragPoint = x - thumb->ScreenFrame().xmin;
        else
            thumbDragPoint = y - thumb->ScreenFrame().ymin;
        mouseIsDraggingThumb = true;
        thumb->SetBackground(THUMB_MOVING_IMAGE);
    }
    else
        MoveThumbToMouse();
    
    return true;
}

bool pawsScrollBar::OnMouseUp( int button, int modifiers, int x, int y )
{
    mouseIsDraggingThumb = false;
    thumb->SetBackground(THUMB_IMAGE);

    return true;
}

bool pawsScrollBar::OnMouseExit()
{
    psPoint pos = PawsManager::GetSingleton().GetMouse()->GetPosition();
    pawsWidget* widget = WidgetAt( pos.x, pos.y );
    if(widget == thumb)
        return false;
    return true;
}

float pawsScrollBar::GetCurrentValue() 
{ 
    if (reversed)
        return maxValue-currentValue;
    else
        return currentValue; 
}

void pawsScrollBar::SetTickValue( float tick )
{ 
    tickValue = tick; 
}

void pawsScrollBar::LimitCurrentValue()
{
    if ( currentValue > maxValue )
        currentValue = maxValue;
    if ( currentValue < minValue )
        currentValue = minValue;
}

bool pawsScrollBar::OnButtonPressed( int button, int keyModifier, pawsWidget* widget )
{
    mouseDown = true;
    scrollTicks = clock->GetCurrentTicks();

    lastButton = button;
    lastModifiers = keyModifier;
    lastWidget = widget;

    switch( widget->GetID() )
    {
        case SCROLL_DOWN:
            if ( currentValue < maxValue  ||  !limited )
            {
                currentValue += tickValue;
                if ( limited )
                    LimitCurrentValue();
                SetThumbLayout();
                return parent->OnScroll( widget->GetID(), this );
            }
            break;

        case SCROLL_UP: 
            if ( currentValue > minValue  ||  !limited )
            {
                currentValue -= tickValue;
                if ( limited )
                    LimitCurrentValue();
                SetThumbLayout();
                return parent->OnScroll( widget->GetID(), this );
            }
            break;
    }

    return false;
}

bool pawsScrollBar::OnButtonReleased( int button, pawsWidget* widget )
{
    mouseDown = false;
    return true;
}

void pawsScrollBar::MoveThumbToMouse()
{
    psPoint mousePos;
    int relMouseCoord,     // coordinate of mouse cursor relative to the rectangle that constraints thumb movement
        rectBegin,         // coordinate of the constraint rectangle
        rectSize,          // size of the constraint rectangle
        thumbPos;          // coordinate of thumb relative to the constraint rectangle
        
    int scrollBarSize = GetScrollBarSize();

    mousePos = PawsManager::GetSingleton().GetMouse()->GetPosition();
    if (horizontal)
    {
        rectBegin = screenFrame.xmin + scrollBarSize;
        relMouseCoord = mousePos.x - rectBegin;
    }
    else
    {
        rectBegin = screenFrame.ymin + scrollBarSize;
        relMouseCoord = mousePos.y - rectBegin;
    }

    rectSize = GetThumbScaleLength();
    
    if (mouseIsDraggingThumb)
        thumbPos = relMouseCoord - thumbDragPoint;
    else
        thumbPos = relMouseCoord - (scrollBarSize - 2*THUMB_MARGIN) / 2;
    thumbPos = MAX(thumbPos, 0);
    thumbPos = MIN(thumbPos, rectSize);

    if (horizontal)
        thumb->MoveTo(rectBegin+thumbPos, thumb->ScreenFrame().ymin);
    else
        thumb->MoveTo(thumb->ScreenFrame().xmin, rectBegin+thumbPos);

    currentValue = minValue + (float(thumbPos) / rectSize * (maxValue-minValue));
    if ( limited )
        LimitCurrentValue();

    if (parent != NULL)
        parent->OnScroll( SCROLL_THUMB, this );
}

void pawsScrollBar::Draw()
{
    if (mouseIsDraggingThumb  &&  maxValue>0)
        MoveThumbToMouse();

    pawsWidget::Draw();  

    if ( mouseDown && (clock->GetCurrentTicks() - scrollTicks > SCROLL_TICKS ))
        OnButtonPressed(lastButton, lastModifiers, lastWidget);           
}

void pawsScrollBar::EnableValueLimit( bool limited )
{
    this->limited = limited;
}

int pawsScrollBar::GetScrollBarSize()
{
    return horizontal ? screenFrame.Height() : screenFrame.Width();
}

void pawsScrollBar::SetButtonLayout()
{
    int scrollBarSize = GetScrollBarSize();

    if (upButton != NULL)
    {
        if (!horizontal)
            upButton->SetRelativeFrame( 0,0, scrollBarSize,scrollBarSize );
        else
            upButton->SetRelativeFrame(0,0,scrollBarSize,scrollBarSize);
    }
    
    if (downButton != NULL)
    {
        if (!horizontal)
            downButton->SetRelativeFrame( 0, defaultFrame.Height()-scrollBarSize, scrollBarSize,scrollBarSize );
        else
            downButton->SetRelativeFrame(defaultFrame.Width()-scrollBarSize,0,scrollBarSize,scrollBarSize);
    }
}

int pawsScrollBar::GetThumbScaleLength()
{
    int scrollBarSize = GetScrollBarSize();
    
    if (horizontal)
        return screenFrame.Width()  - 3*scrollBarSize + 2*THUMB_MARGIN;
    else
        return screenFrame.Height() - 3*scrollBarSize + 2*THUMB_MARGIN;
}
