/////////////////////////////////////////////////////////////////////////////
// Name:        foldpanelbar.cpp
// Purpose:
// Author:      Jorgen Bodde
// Modified by: ABX - 19/12/2004 : possibility of horizontal orientation
//                               : wxWidgets coding standards
// Created:     22/06/2004
// RCS-ID:      $Id: foldpanelbar.cpp,v 1.7 2005/07/28 23:23:27 VZ Exp $
// Copyright:   (c) Jorgen Bodde
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

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

#include "wx/foldbar/foldpanelbar.h"
#include "icon_collapsed.xpm"
#include "icon_expanded.xpm"
#include "icon_theresmore.xpm"

//----------------------------------------------------------------------------
// wxFoldPanelBar
//----------------------------------------------------------------------------

IMPLEMENT_DYNAMIC_CLASS( wxFoldPanelBar, wxPanel )

BEGIN_EVENT_TABLE(wxFoldPanelBar,wxPanel)
    EVT_SIZE(wxFoldPanelBar::OnSizePanel)
    //EVT_PAINT(wxFoldPanelBar::OnPaint)
    EVT_CAPTIONBAR(wxID_ANY, wxFoldPanelBar::OnPressCaption)
END_EVENT_TABLE()

wxFoldPanelBar::wxFoldPanelBar()
    : wxPanel()
    , m_foldPanel(NULL)
    , m_bottomPanel(NULL)
    , m_controlCreated(false)
{

}

wxFoldPanelBar::wxFoldPanelBar( wxWindow *parent, wxWindowID id, const wxPoint &position,
                                const wxSize& size, long style, long extraStyle)
    : wxPanel()
    , m_foldPanel(NULL)
    , m_bottomPanel(NULL)
    , m_controlCreated(false)
{
    Create( parent, id, position, size, style, extraStyle);
}

void wxFoldPanelBar::Create( wxWindow *parent, wxWindowID id, const wxPoint &position,
                             const wxSize& size, long style, long extraStyle )
{

    m_extraStyle = extraStyle;

    // make sure there is any orientation
    if ( ( style & wxFPB_HORIZONTAL ) != wxFPB_HORIZONTAL )
        style |= wxFPB_VERTICAL;

    // create the panel (duh!). This causes a size event, which we are going
    // to skip when we are not initialised

    wxPanel::Create(parent, id, position, size, style);

    // the fold panel area

    m_foldPanel = new wxPanel(this, wxID_ANY, position, size, wxNO_BORDER|wxTAB_TRAVERSAL);

    // the extra area for some icons / context menu etc

#if 0
    m_bottomPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,22), wxNO_BORDER|wxTAB_TRAVERSAL);
    m_bottomPanel->SetBackgroundColour(*wxWHITE);
#endif

    // create the fold icons to be used in the captions

    m_images = new wxImageList(16, 16);

    wxBitmap *bmp = new wxBitmap(icon_expanded);
    m_images->Add(*bmp);
    delete bmp;

    bmp = new wxBitmap(icon_collapsed);
    m_images->Add(*bmp);
    delete bmp;

    m_moreBmp = new wxBitmap(icon_theresmore);

    // do this as last, to check if create is already called

    m_controlCreated = true;
}

wxFoldPanelBar::~wxFoldPanelBar()
{
    delete m_images;
    delete m_moreBmp;
}

wxFoldPanel wxFoldPanelBar::AddFoldPanel(const wxString &caption, bool collapsedInitially, const wxCaptionBarStyle &style)
{
    wxASSERT(m_controlCreated);

    // create a fold panel item, which is first only the caption.
    // the user can now add a panel area which will be folded in
    // when pressed.

    wxFoldPanelItem *item = new wxFoldPanelItem(m_foldPanel, caption, m_images, collapsedInitially, style);

    // look at the last added one and reposition this one
    int pos = 0;
    if(m_panels.GetCount() > 0)
        pos = m_panels.Last()->GetItemPos() + m_panels.Last()->GetPanelLength();

    item->Reposition(pos);
    m_panels.Add(item);

    //return wxFoldPanel(item);
    return wxFoldPanel(item);
}

int wxFoldPanelBar::AddFoldPanelWindow(const wxFoldPanel &panel, wxWindow *window, int flags, int ySpacing, int leftSpacing,
                                       int rightSpacing)
{
    wxCHECK(panel.IsOk(), -1);
    panel.GetItem()->AddWindow(window, flags, ySpacing, leftSpacing, rightSpacing);

    // TODO: Take old and new height, and if difference, reposition all the lower panels
    // this is because the user can add new wxWindow controls somewhere in between
    // when other panels are already present.

    return 0;
}

int wxFoldPanelBar::AddFoldPanelSeperator(const wxFoldPanel &panel, const wxColour &color, int ySpacing, int leftSpacing,
                                          int rightSpacing)
{
    wxCHECK(panel.IsOk(), -1);
    panel.GetItem()->AddSeparator(color, ySpacing, leftSpacing, rightSpacing);

    return 0;
}

void wxFoldPanelBar::OnSizePanel(wxSizeEvent &event)
{
    // skip all stuff when we are not initialised yet

    if(!m_controlCreated)
    {
        event.Skip();
        return;
    }

    // now size the fold panel area and the
    // lower bar in such a way that the bar is always
    // visible

    wxRect foldrect = GetRect();

    // fold panel itself. If too little space,
    // don't show it

#if 0
    if(foldrect.GetHeight() < 23)
        foldrect.SetHeight(0);
    else
        foldrect.SetHeight(foldrect.GetHeight() - 22);
#endif

    foldrect.SetX(0);
    foldrect.SetY(0);
    m_foldPanel->SetSize(foldrect);

    if(m_extraStyle & wxFPB_COLLAPSE_TO_BOTTOM)
    {
        wxRect rect = RepositionCollapsedToBottom();
        bool vertical = IsVertical();
        if((vertical && rect.GetHeight() > 0) || (!vertical && rect.GetWidth() > 0))
            RefreshRect(rect);
    }

    // TODO: A smart way to check wether the old - new width of the
    // panel changed, if so no need to resize the fold panel items

    RedisplayFoldPanelItems();

    // tool panel for icons and other stuff

#if 0
    wxRect bottomrect = GetRect();
    if(bottomrect.GetHeight() < 22)
        bottomrect.SetY(0);
    else
        bottomrect.SetY(bottomrect.GetHeight() - 22);

    bottomrect.SetHeight(22);
    bottomrect.SetX(0);
    m_bottomPanel->SetSize(bottomrect);

    // TODO: redraw the bitmap properly
    // use the captionbar algorithm for that

    m_bottomPanel->Refresh();
#endif
}

void wxFoldPanelBar::OnPaint(wxPaintEvent &event)
{
    if(!m_controlCreated)
        return;
#if 0
    // paint the bottom panel only, where the
    // arrow is shown when there is more to show the user
    // just as informative icon

    wxPaintDC dc(m_bottomPanel);

    wxSize size = m_bottomPanel->GetSize();
    int offset = (size.GetHeight() - m_moreBmp->GetHeight()) / 2;

    dc.DrawBitmap(*m_moreBmp, size.GetWidth() - m_moreBmp->GetWidth() - 2, offset, true);
#endif

    event.Skip();
}

void wxFoldPanelBar::OnPressCaption(wxCaptionBarEvent &event)
{
    // act upon the folding or expanding status of the bar
    // to expand or collapse the panel(s)

    if(event.GetFoldStatus())
        Collapse(wxFoldPanel((wxFoldPanelItem *)event.GetTag()));
    else
        Expand(wxFoldPanel((wxFoldPanelItem *)event.GetTag()));
}

void wxFoldPanelBar::RefreshPanelsFrom(wxFoldPanelItem *item)
{
    wxASSERT(item);

    int i = m_panels.Index(item);
    if(i != wxNOT_FOUND)
        RefreshPanelsFrom(i);
}

void wxFoldPanelBar::RefreshPanelsFrom(size_t i)
{
    Freeze();

    // if collapse to bottom is on, the panels that are not expanded
    // should be drawn at the bottom. All panels that are expanded
    // are drawn on top. The last expanded panel gets all the extra space

    if(m_extraStyle & wxFPB_COLLAPSE_TO_BOTTOM)
    {
        int offset = 0;

        for(size_t j = 0; j < m_panels.GetCount(); j++)
        {
            if(m_panels.Item(j)->IsExpanded())
                offset += m_panels.Item(j)->Reposition(offset);
        }

        // put all non collapsed panels at the bottom where there is space, else
        // put them right behind the expanded ones

        RepositionCollapsedToBottom();
    }
    else
    {
        int pos = m_panels.Item(i)->GetItemPos() + m_panels.Item(i)->GetPanelLength();
        for(i++; i < m_panels.GetCount(); i++)
            pos += m_panels.Item(i)->Reposition(pos);
    }
    Thaw();
}

void wxFoldPanelBar::RedisplayFoldPanelItems()
{
    // resize them all. No need to reposition

    wxFoldPanelItem *item;
    for(size_t i = 0; i < m_panels.GetCount(); i++)
    {
        item = m_panels.Item(i);
        wxASSERT(item);

        item->ResizePanel();
    }
}

wxRect wxFoldPanelBar::RepositionCollapsedToBottom()
{
    wxRect value(0,0,0,0);
    bool vertical = IsVertical();

    // determine wether the number of panels left
    // times the size of their captions is enough
    // to be placed in the left over space

    int expanded = 0, collapsed = 0, offset;
    GetPanelsLength(collapsed, expanded);

    // if no room stick them behind the normal ones, else
    // at the bottom

    if(((vertical ? GetSize().GetHeight() : GetSize().GetWidth()) - expanded - collapsed) < 0)
        offset = expanded;
    else
    {
        // value is the region which is left unpainted
        // I will send it back as 'slack' so it does not need to
        // be recalulated.

        value.SetHeight(GetSize().GetHeight());
        value.SetWidth(GetSize().GetWidth());

        if(vertical)
        {
            value.SetY(expanded);
            value.SetHeight(value.GetHeight() - expanded);
        }
        else
        {
            value.SetX(expanded);
            value.SetWidth(value.GetWidth() - expanded);
        }

        offset = (vertical ? GetSize().GetHeight() : GetSize().GetWidth()) - collapsed;
    }

    // go reposition

    for(size_t i = 0; i < m_panels.GetCount(); i++)
    {
        if(!m_panels.Item(i)->IsExpanded())
            offset += m_panels.Item(i)->Reposition(offset);
    }

    return value;
}

int wxFoldPanelBar::GetPanelsLength(int &collapsed, int &expanded)
{
    int value = 0;

    // assumed here that all the panels that are expanded
    // are positioned after each other from 0,0 to end.

    for(size_t j = 0; j < m_panels.GetCount(); j++)
    {
        int offset = m_panels.Item(j)->GetPanelLength();
        value += offset;
        if(m_panels.Item(j)->IsExpanded())
            expanded += offset;
        else
            collapsed += offset;
    }

    return value;
}
