/////////////////////////////////////////////////////////////////////////////
// Name:        barhintspl.h
// Purpose:     Implementation for cbBarHintsPlugin
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     30/11/98 (my 22th birthday :-)
// RCS-ID:      $Id: barhintspl.cpp,v 1.8 2005/09/23 12:47:41 MR Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////
 
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "wx/utils.h"
#include "wx/fl/barhintspl.h"

// fixed settings

#define GROOVE_WIDTH         3  // left shade + middle line + right shade
#define GROOVE_TO_GROOVE_GAP 1  
#define BOX_T_BOX_GAP        2
#define BOX_TO_GROOVE_GAP    3

#define BOXES_IN_HINT        2
#define CLOSE_BOX_IDX        0
#define COLLAPSE_BOX_IDX     1

// used interally 

#define CLOSE_BOX_HITTED    1
#define COLLAPSE_BOX_HITTED 2

/***** Implementation fro class cbBarHintsPlugin *****/

IMPLEMENT_DYNAMIC_CLASS( cbBarHintsPlugin, cbPluginBase )

BEGIN_EVENT_TABLE( cbBarHintsPlugin, cbPluginBase )

    EVT_PL_SIZE_BAR_WND  ( cbBarHintsPlugin::OnSizeBarWindow )
    EVT_PL_DRAW_BAR_DECOR( cbBarHintsPlugin::OnDrawBarDecorations )

    EVT_PL_LEFT_DOWN( cbBarHintsPlugin::OnLeftDown )
    EVT_PL_LEFT_UP  ( cbBarHintsPlugin::OnLeftUp   )
    EVT_PL_MOTION   ( cbBarHintsPlugin::OnMotion   )

END_EVENT_TABLE()

cbBarHintsPlugin::cbBarHintsPlugin(void)

    : mpPane( 0 ),
      mBtnPressed   ( false ),
      mCloseBoxOn   ( true ),
      mCollapseBoxOn( true ),
      mGrooveCount  ( 2 ),
      mHintGap      ( 4 ),
      mXWeight      ( 2 )
{
    mBoxes[CLOSE_BOX_IDX]    = NULL;
    mBoxes[COLLAPSE_BOX_IDX] = NULL;
}

cbBarHintsPlugin::cbBarHintsPlugin( wxFrameLayout* pLayout, int paneMask )

    : cbPluginBase( pLayout, paneMask ),
      mpPane( 0 ),
      mBtnPressed   ( false ),
      mCloseBoxOn   ( true ),
      mCollapseBoxOn( true ),
      mGrooveCount  ( 2 ),
      mHintGap      ( 5 ),
      mXWeight      ( 2 )
{
    mBoxes[CLOSE_BOX_IDX]    = NULL;
    mBoxes[COLLAPSE_BOX_IDX] = NULL;
}


cbBarHintsPlugin::~cbBarHintsPlugin()
{
    if (mBoxes[CLOSE_BOX_IDX])
        delete mBoxes[CLOSE_BOX_IDX];

    if (mBoxes[COLLAPSE_BOX_IDX])
        delete mBoxes[COLLAPSE_BOX_IDX];
}  // cbBarHintsPlugin destructor


void cbBarHintsPlugin::SetGrooveCount( int nGrooves )
{
    mGrooveCount = nGrooves;
}

void cbBarHintsPlugin::CreateBoxes()
{
    cbCloseBox*    box1 = new cbCloseBox();
    cbCollapseBox* box2 = new cbCollapseBox();

    mBoxes[CLOSE_BOX_IDX]    = box1;
    mBoxes[COLLAPSE_BOX_IDX] = box2;

    int i;
    for ( i = 0; i != BOXES_IN_HINT; ++i )
    {
        mBoxes[i]->mpLayout = mpLayout;
        mBoxes[i]->mpPlugin = this;
        mBoxes[i]->mpWnd    = NULL;
    }
}


void cbBarHintsPlugin::Draw3DBox( wxDC& WXUNUSED(dc), const wxPoint& WXUNUSED(pos), bool WXUNUSED(pressed) )
{
}

void cbBarHintsPlugin::DrawCloseBox( wxDC& WXUNUSED(dc), const wxPoint& WXUNUSED(pos), bool WXUNUSED(pressed) )
{
}

void cbBarHintsPlugin::DrawCollapseBox( wxDC& WXUNUSED(dc), const wxPoint& WXUNUSED(pos), 
                                        bool WXUNUSED(atLeft), bool WXUNUSED(disabled), bool WXUNUSED(pressed) )
{
}

void cbBarHintsPlugin::DrawGrooves( wxDC& dc, const wxPoint& pos, int length )
{
    int ofs = 0;

    int i;
    for ( i = 0; i != mGrooveCount; ++i, ofs += ( GROOVE_WIDTH + GROOVE_TO_GROOVE_GAP ) )
    {
        if ( mpPane->IsHorizontal() )
        {
            dc.SetPen( mpLayout->mLightPen );
            dc.DrawLine( pos.x + ofs, pos.y, pos.x + ofs, pos.y + length - 1 );
            dc.DrawPoint( pos.x + ofs + 1, pos.y );

            dc.SetPen( mpLayout->mDarkPen );
            dc.DrawLine( pos.x + ofs + 2, pos.y, pos.x + ofs + 2, pos.y + length );
            dc.DrawPoint( pos.x + ofs + 1, pos.y + length - 1 );
            dc.DrawPoint( pos.x + ofs,     pos.y + length - 1 );
        }
        else
        {
            dc.SetPen( mpLayout->mLightPen );
            dc.DrawLine( pos.x, pos.y + ofs, pos.x + length - 1, pos.y + ofs );
            dc.DrawPoint( pos.x, pos.y + ofs + 1 );

            dc.SetPen( mpLayout->mDarkPen );
            dc.DrawLine( pos.x, pos.y + ofs + 2, pos.x + length, pos.y + ofs + 2 );
            dc.DrawPoint( pos.x + length - 1, pos.y + ofs + 1 );
            dc.DrawPoint( pos.x + length - 1, pos.y + ofs );
        }
    }
}

void cbBarHintsPlugin::ExcludeHints( wxRect& rect, cbBarInfo& info )
{
    int boxHeight = BTN_BOX_HEIGHT;

    // collapse and close box are not placed on fixed bars

    if ( info.IsFixed() || ( !mCloseBoxOn && !mCollapseBoxOn ) )

        boxHeight = 0;

    int height = wxMax( mGrooveCount*(GROOVE_WIDTH + GROOVE_TO_GROOVE_GAP) 
                        - GROOVE_TO_GROOVE_GAP, 
                        boxHeight 
                      );

    if ( mpPane->IsHorizontal() )
    {
        rect.x     += ( mHintGap*2 + height );
        rect.width -= (height + 2*mHintGap);

        rect.x     -= info.mDimInfo.mHorizGap + 2;
        rect.width += info.mDimInfo.mHorizGap + 2;
    }
    else
    {
        rect.y      += (mHintGap*2 + height);
        rect.height -= (height + 2*mHintGap);

        rect.y      -= info.mDimInfo.mVertGap + 2;
        rect.height += info.mDimInfo.mVertGap + 2;
    }
}

void cbBarHintsPlugin::DoDrawHint( wxDC& dc, wxRect& rect, 
                                   int pos, int WXUNUSED(boxOfs), int grooveOfs,
                                   bool isFixed )
{
    if ( !isFixed )
    {
        if ( mpPane->IsHorizontal() )
        {
            if ( mCloseBoxOn )

                mBoxes[CLOSE_BOX_IDX]->Draw( dc );

            if ( mCollapseBoxOn )
            
                mBoxes[COLLAPSE_BOX_IDX]->Draw( dc );
        }
        else
        {
            if ( mCloseBoxOn )
            
                mBoxes[CLOSE_BOX_IDX]->Draw( dc );

            if ( mCollapseBoxOn )
            
                mBoxes[COLLAPSE_BOX_IDX]->Draw( dc );
        }
    }

    if ( mpPane->IsHorizontal() )
    
        DrawGrooves( dc, wxPoint( rect.x + mHintGap + grooveOfs, pos ), 
                     rect.height - (pos - rect.y) - mHintGap );
    else
        DrawGrooves( dc, wxPoint( rect.x + mHintGap, rect.y + mHintGap + grooveOfs ), 
                     (pos - rect.x) - mHintGap );
}

void cbBarHintsPlugin::GetHintsLayout( wxRect& rect, cbBarInfo& info, 
                                       int& boxOfs, int& grooveOfs, int& pos )
{
    int boxHeight = BTN_BOX_HEIGHT;
//  int boxWidth  = BTN_BOX_WIDTH + BOX_TO_GROOVE_GAP + BTN_BOX_WIDTH;

    // collapse and close box are not placed on fixed bars

    if ( info.IsFixed() || ( !mCloseBoxOn && !mCollapseBoxOn ) )
    {
        boxHeight = 0;
//      boxWidth = 0;
    }
/*
    else
    if ( !mCloseBoxOn || !mCollapseBoxOn )

        boxWidth = BTN_BOX_WIDTH;
*/
    int grooveHeight = mGrooveCount*(GROOVE_WIDTH + GROOVE_TO_GROOVE_GAP) 
                       - GROOVE_TO_GROOVE_GAP;

    int height = wxMax( grooveHeight, boxHeight );

    // center boxs and groves with respect to each other

    boxOfs    = ( height - boxHeight    ) / 2;
    grooveOfs = ( height - grooveHeight ) / 2;

    pos = ( mpPane->IsHorizontal() ) ? rect.y + mHintGap
                                     : rect.x + rect.width - mHintGap;

    // setup positions for boxes

    if ( !info.IsFixed() )
    {
        // what direction "collapse-triangle" should look at?

        bool& isAtLeft = ((cbCollapseBox*)(mBoxes[COLLAPSE_BOX_IDX]))->mIsAtLeft;
         
        isAtLeft= info.mBounds.x <= mpPane->mPaneWidth - ( info.mBounds.x + info.mBounds.width );

        if ( info.IsExpanded() )
        {
            isAtLeft = false;

            cbBarInfo* pCur = info.mpPrev;

            while( pCur )
            {
                if ( !pCur->IsFixed() )
                {
                    isAtLeft = true; break;
                }

                pCur = pCur->mpPrev;
            }
        }

        // collapse/expand works only when more not-fixed bars are present in the same row

        mBoxes[COLLAPSE_BOX_IDX]->Enable( info.mpRow->mNotFixedBarsCnt > 1 );

        int i;
        for ( i = 0; i != BOXES_IN_HINT; ++i )
        {
            mBoxes[i]->mpPane = mpPane;
        }

        if ( mpPane->IsHorizontal() )
        {
            if ( mCloseBoxOn )
            {
                mBoxes[CLOSE_BOX_IDX]->mPos = wxPoint( rect.x + mHintGap + boxOfs, pos );

                pos += BTN_BOX_HEIGHT;
            }

            if ( mCollapseBoxOn )
            {
                if ( mCloseBoxOn ) pos += BOX_T_BOX_GAP;

                mBoxes[COLLAPSE_BOX_IDX]->mPos = wxPoint( rect.x + mHintGap + boxOfs, pos );

                pos += BTN_BOX_HEIGHT;

                pos += BOX_TO_GROOVE_GAP;
            }
        }
        else
        {
            if ( mCloseBoxOn )
            {
                pos -= BTN_BOX_WIDTH;

                mBoxes[CLOSE_BOX_IDX]->mPos = wxPoint( pos , rect.y + mHintGap + boxOfs );
            }

            if ( mCollapseBoxOn )
            {
                if ( mCloseBoxOn ) pos -= BOX_T_BOX_GAP;

                pos -= BTN_BOX_WIDTH;

                mBoxes[COLLAPSE_BOX_IDX]->mPos = wxPoint( pos, rect.y + mHintGap + boxOfs );

                pos -= BOX_TO_GROOVE_GAP;
            }
        }
    }
}

static inline bool is_in_box( const wxPoint& rectPos, const wxPoint& mousePos )
{
    return ( mousePos.x >= rectPos.x &&
             mousePos.y >= rectPos.y &&
             mousePos.x < rectPos.x + BTN_BOX_WIDTH &&
             mousePos.y < rectPos.y + BTN_BOX_HEIGHT );
}

int cbBarHintsPlugin::HitTestHints( cbBarInfo& info, const wxPoint& pos )
{
    wxPoint inPane = pos;
    mpPane->PaneToFrame( &inPane.x, &inPane.y );

    wxRect& rect = info.mBoundsInParent;

    if ( info.IsFixed() ) return false;

    int boxOfs, grooveOfs, coord;

    GetHintsLayout( rect, info, boxOfs, grooveOfs, coord );

    if ( mpPane->IsHorizontal() )
    {
        if ( mCloseBoxOn )
        {
            if ( is_in_box( wxPoint( rect.x + mHintGap + boxOfs, coord ), inPane ) )

                return CLOSE_BOX_HITTED;

            coord += BTN_BOX_HEIGHT;
        }

        if ( mCollapseBoxOn )
        {
            if ( mCloseBoxOn ) coord += BOX_T_BOX_GAP;

            if ( is_in_box( wxPoint( rect.x + mHintGap + boxOfs, coord ), inPane ) )

                return COLLAPSE_BOX_HITTED;

            coord += BTN_BOX_HEIGHT;
        }
    }
    else
    {
        if ( mCloseBoxOn )
        {
            coord -= BTN_BOX_WIDTH;

            if ( is_in_box( wxPoint( coord , rect.y + mHintGap + boxOfs ), inPane ) )

                return CLOSE_BOX_HITTED;
        }

        if ( mCollapseBoxOn )
        {
            if ( mCloseBoxOn ) coord -= BOX_T_BOX_GAP;
            coord -= BTN_BOX_WIDTH;

            if ( is_in_box( wxPoint( coord, rect.y + mHintGap + boxOfs ), inPane ) )

                return COLLAPSE_BOX_HITTED;
        }
    }

    return false;
}

// handlers for plugin-events

void cbBarHintsPlugin::OnSizeBarWindow( cbSizeBarWndEvent& event )
{
    wxRect& rect     = event.mBoundsInParent;
    mpPane           = event.mpPane;

    ExcludeHints( rect, *event.mpBar );

    event.Skip(); // pass event to the next plugin in the chain
}

void cbBarHintsPlugin::OnDrawBarDecorations( cbDrawBarDecorEvent& event )
{
    wxRect& rect     = event.mBoundsInParent;
    mpPane           = event.mpPane;

    int boxOfs, grooveOfs, pos;

    GetHintsLayout( rect, *event.mpBar, boxOfs, grooveOfs, pos );

    DoDrawHint( *event.mpDc, rect, pos, boxOfs, grooveOfs, event.mpBar->IsFixed() );

    // let other plugins add on their decorations

    event.Skip();
}

void cbBarHintsPlugin::OnLeftDown( cbLeftDownEvent& event )
{
    mpPane           = event.mpPane;
    wxPoint inFrame = event.mPos;

    mpPane->PaneToFrame( &inFrame.x, &inFrame.y );

    wxBarIterator iter( mpPane->GetRowList() );

    mpClickedBar = NULL;

    while ( iter.Next() )
    {
        cbBarInfo& bar = iter.BarInfo();

        int boxOfs, grooveOfs, pos;

        GetHintsLayout( bar.mBoundsInParent, bar, boxOfs, grooveOfs, pos );

        if ( !bar.IsFixed() )
        {
            int i;
            for ( i = 0; i != BOXES_IN_HINT; ++i )
            {
                mBoxes[i]->mPressed = false;
                mBoxes[i]->mWasClicked = false;
            }
            for ( i = 0; i != BOXES_IN_HINT; ++i )
            {
                mBoxes[i]->OnLeftDown( inFrame );

                if ( mBoxes[i]->mPressed )
                {
                    mBtnPressed = true;
                    mpClickedBar = &bar;

                    return; // event handled
                }
            }
        }
    }

    event.Skip();
}

void cbBarHintsPlugin::OnLeftUp( cbLeftUpEvent&   event )
{
    if ( mBtnPressed )
    {
        wxPoint inFrame = event.mPos;
        mpPane->PaneToFrame( &inFrame.x, &inFrame.y );

        int boxOfs, grooveOfs, pos;

        GetHintsLayout( mpClickedBar->mBoundsInParent, *mpClickedBar, boxOfs, grooveOfs, pos );

        HitTestHints( *mpClickedBar, event.mPos );

        int i;
        for ( i = 0; i != BOXES_IN_HINT; ++i )
        {
            mBoxes[i]->OnLeftUp( inFrame );

            if ( mBoxes[i]->WasClicked() )
            {
                if ( i == 0 )
                {
                    mpLayout->SetBarState( mpClickedBar, wxCBAR_HIDDEN, true );
                    // Notify bar child window of close event:
                    if(mpClickedBar->mpBarWnd!=NULL)
                        mpClickedBar->mpBarWnd->Close();
                }
                else
                {
                    if ( mpClickedBar->IsExpanded() )
                        mpPane->ContractBar( mpClickedBar );
                    else
                        mpPane->ExpandBar( mpClickedBar );
                }
            }
        }

        mBtnPressed = false;
        return;
    }
    else
        event.Skip();
}

void cbBarHintsPlugin::OnMotion( cbMotionEvent&   event )
{
    if ( mBtnPressed )
    {
        wxPoint inFrame = event.mPos;
        mpPane->PaneToFrame( &inFrame.x, &inFrame.y );

        mpPane = event.mpPane;

        int i;
        for ( i = 0; i != BOXES_IN_HINT; ++i )
        {
            mBoxes[i]->OnMotion( inFrame );
        }
    }
    else
        event.Skip();
}

void cbBarHintsPlugin::OnInitPlugin()
{
    cbPluginBase::OnInitPlugin();

    cbDockPane** panes = mpLayout->GetPanesArray();

    int i;
    for ( i = 0; i != MAX_PANES; ++i )
    {    
        if ( panes[i]->MatchesMask( mPaneMask ) )
        {
            panes[i]->mProps.mMinCBarDim.x = 25;
            panes[i]->mProps.mMinCBarDim.y = 16;
        }
    }
    CreateBoxes();
}
