/////////////////////////////////////////////////////////////////////////////
// Name:        wxCustomButton based on wxCustomToggleCtrl.cpp
// Purpose:     a toggle button
// Author:      Bruce Phillips
// Modified by: John Labenski
// Created:     11/05/2002
// RCS-ID:
// Copyright:   (c) Bruce Phillips, John Labenki
// Licence:     wxWidgets licence
/////////////////////////////////////////////////////////////////////////////

#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
    #pragma implementation "toggle.h"
#endif

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

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/control.h"
    #include "wx/settings.h"
    #include "wx/bitmap.h"
    #include "wx/timer.h"
    #include "wx/dc.h"
    #include "wx/dcclient.h"
#endif // WX_PRECOMP

#include "wx/tglbtn.h"
#include "wx/image.h"
#include "wx/things/toggle.h"

// ==========================================================================
// wxCustomButton
// ==========================================================================
IMPLEMENT_DYNAMIC_CLASS( wxCustomButton, wxControl )

BEGIN_EVENT_TABLE(wxCustomButton,wxControl)
    EVT_MOUSE_EVENTS ( wxCustomButton::OnMouseEvents )
    EVT_PAINT        ( wxCustomButton::OnPaint )
    EVT_TIMER        ( wxID_ANY, wxCustomButton::OnTimer)
    EVT_SIZE         ( wxCustomButton::OnSize )
END_EVENT_TABLE()

wxCustomButton::~wxCustomButton()
{
    if (HasCapture()) ReleaseMouse();
    if (m_timer) delete m_timer;
}

void wxCustomButton::Init()
{
    m_focused = false;
    m_labelMargin = wxSize(4,4);
    m_bitmapMargin = wxSize(2,2);
    m_down = 0;
    m_timer = NULL;
    m_eventType = 0;
    m_button_style = wxCUSTBUT_TOGGLE|wxCUSTBUT_BOTTOM;
}

bool wxCustomButton::Create(wxWindow* parent, wxWindowID id,
                            const wxString& label, const wxBitmap &bitmap,
                            const wxPoint& pos, const wxSize& size,
                            long style, const wxValidator& val,
                            const wxString& name)
{
    if (!wxControl::Create(parent,id,pos,size,wxNO_BORDER|wxCLIP_CHILDREN,val,name))
        return false;

    wxControl::SetLabel(label);
    wxControl::SetBackgroundColour(parent->GetBackgroundColour());
    wxControl::SetForegroundColour(parent->GetForegroundColour());
    wxControl::SetFont(parent->GetFont());

    if (bitmap.Ok()) m_bmpLabel = bitmap;

    if (!SetButtonStyle(style)) return false;

    wxSize bestSize = DoGetBestSize();
    SetSize(wxSize(size.x<0 ? bestSize.x:size.x, size.y<0 ? bestSize.y:size.y));
    SetBestSize(GetSize());

    CalcLayout(true);
    return true;
}

void wxCustomButton::SetValue(bool depressed)
{
    wxCHECK_RET(!(m_button_style & wxCUSTBUT_NOTOGGLE), wxT("can't set button state"));
    m_down = depressed ? 1 : 0;
    Refresh(false);
}

bool wxCustomButton::SetButtonStyle(long style)
{
    int n_styles = 0;
    if ((style & wxCUSTBUT_LEFT) != 0)   n_styles++;
    if ((style & wxCUSTBUT_RIGHT) != 0)  n_styles++;
    if ((style & wxCUSTBUT_TOP) != 0)    n_styles++;
    if ((style & wxCUSTBUT_BOTTOM) != 0) n_styles++;
    wxCHECK_MSG(n_styles < 2, false, wxT("Only one wxCustomButton label position allowed"));

    n_styles = 0;
    if ((style & wxCUSTBUT_NOTOGGLE) != 0)       n_styles++;
    if ((style & wxCUSTBUT_BUTTON) != 0)         n_styles++;
    if ((style & wxCUSTBUT_TOGGLE) != 0)         n_styles++;
    if ((style & wxCUSTBUT_BUT_DCLICK_TOG) != 0) n_styles++;
    if ((style & wxCUSTBUT_TOG_DCLICK_BUT) != 0) n_styles++;
    wxCHECK_MSG(n_styles < 2, false, wxT("Only one wxCustomButton style allowed"));

    m_button_style = style;

    if ((m_button_style & wxCUSTBUT_BUTTON) != 0)
        m_down = 0;

    CalcLayout(true);
    return true;
}

void wxCustomButton::SetLabel( const wxString &label )
{
    wxControl::SetLabel(label);
    CalcLayout(true);
}

// sequence of events in GTK is up, dclick, up.

void wxCustomButton::OnMouseEvents(wxMouseEvent& event)
{
    if (m_button_style & wxCUSTBUT_NOTOGGLE) return;

    if (event.LeftDown() || event.RightDown())
    {
        if (!HasCapture())
            CaptureMouse(); // keep depressed until up

        m_down++;
        Redraw();
    }
    else if (event.LeftDClick() || event.RightDClick())
    {
        m_down++; // GTK eats second down event
        Redraw();
    }
    else if (event.LeftUp())
    {
        if (HasCapture())
            ReleaseMouse();

        m_eventType = wxEVT_LEFT_UP;

        if (wxRect(wxPoint(0,0), GetSize()).Inside(event.GetPosition()))
        {
            if ((m_button_style & wxCUSTBUT_BUTTON) && (m_down > 0))
            {
                m_down = 0;
                Redraw();
                SendEvent();
                return;
            }
            else
            {
                if (!m_timer)
                {
                    m_timer = new wxTimer(this, m_down+1);
                    m_timer->Start(200, true);
                }
                else
                {
                    m_eventType = wxEVT_LEFT_DCLICK;
                }

                if ((m_button_style & wxCUSTBUT_TOGGLE) &&
                    (m_button_style & wxCUSTBUT_TOG_DCLICK_BUT)) m_down++;
            }
        }

        Redraw();
    }
    else if (event.RightUp())
    {
        if (HasCapture())
            ReleaseMouse();

        m_eventType = wxEVT_RIGHT_UP;

        if (wxRect(wxPoint(0,0), GetSize()).Inside(event.GetPosition()))
        {
            if ((m_button_style & wxCUSTBUT_BUTTON) && (m_down > 0))
            {
                m_down = 0;
                Redraw();
                SendEvent();
                return;
            }
            else
            {
                m_down++;

                if (!m_timer)
                {
                    m_timer = new wxTimer(this, m_down);
                    m_timer->Start(250, true);
                }
                else
                {
                    m_eventType = wxEVT_RIGHT_DCLICK;
                }
            }
        }

        Redraw();
    }
    else if (event.Entering())
    {
        m_focused = true;
        if ((event.LeftIsDown() || event.RightIsDown()) && HasCapture())
            m_down++;

        Redraw();
    }
    else if (event.Leaving())
    {
        m_focused = false;
        if ((event.LeftIsDown() || event.RightIsDown()) && HasCapture())
            m_down--;

        Redraw();
    }
}

void wxCustomButton::OnTimer( wxTimerEvent &event )
{
    m_timer->Stop();
    delete m_timer;
    m_timer = NULL;

    // Clean up the button presses
    // FIXME - GTK eats second left down for a DClick, who know about the others?
    if (m_button_style & wxCUSTBUT_BUTTON)
    {
        m_down = 0;
    }
    else if (m_button_style & wxCUSTBUT_TOGGLE)
    {
        if (m_eventType == wxEVT_LEFT_UP)
            m_down = event.GetId()%2 ? 0 : 1;
        else
            m_down = event.GetId()%2 ? 1 : 0;
    }
    else if (m_button_style & wxCUSTBUT_BUT_DCLICK_TOG)
    {
        if (m_eventType == wxEVT_LEFT_DCLICK)
            m_down = event.GetId()%2 ? 0 : 1;
        else
            m_down = event.GetId()%2 ? 1 : 0;
    }
    else if (m_button_style & wxCUSTBUT_TOG_DCLICK_BUT)
    {
        if (m_eventType == wxEVT_LEFT_UP)
            m_down = event.GetId()%2 ? 0 : 1;
        else
            m_down = event.GetId()%2 ? 1 : 0;
    }

    Refresh(false);
    SendEvent();
}

void wxCustomButton::SendEvent()
{
    if (((m_button_style & wxCUSTBUT_TOGGLE) && (m_eventType == wxEVT_LEFT_UP)) ||
        ((m_button_style & wxCUSTBUT_BUT_DCLICK_TOG) && (m_eventType == wxEVT_LEFT_DCLICK)) ||
        ((m_button_style & wxCUSTBUT_TOG_DCLICK_BUT) && (m_eventType == wxEVT_LEFT_UP)))
    {
        wxCommandEvent eventOut(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, GetId());
        eventOut.SetInt(m_down%2 ? 1 : 0);
        eventOut.SetExtraLong(m_eventType);
        eventOut.SetEventObject(this);
        GetEventHandler()->ProcessEvent(eventOut);
    }
    else
    {
        wxCommandEvent eventOut(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
        eventOut.SetInt(0);
        eventOut.SetExtraLong(m_eventType);
        eventOut.SetEventObject(this);
        GetEventHandler()->ProcessEvent(eventOut);
    }
}

wxBitmap wxCustomButton::CreateBitmapDisabled(const wxBitmap &bitmap) const
{
    wxCHECK_MSG(bitmap.Ok(), wxNullBitmap, wxT("invalid bitmap"));

    unsigned char br = GetBackgroundColour().Red();
    unsigned char bg = GetBackgroundColour().Green();
    unsigned char bb = GetBackgroundColour().Blue();

    wxImage image = bitmap.ConvertToImage();
    int pos, width = image.GetWidth(), height = image.GetHeight();
    unsigned char *img_data = image.GetData();

    for (int j=0; j<height; j++)
    {
        for (int i=j%2; i<width; i+=2)
        {
            pos = (j*width+i)*3;
            img_data[pos  ] = br;
            img_data[pos+1] = bg;
            img_data[pos+2] = bb;
        }
    }

    return wxBitmap(image);

/*      // FIXME why bother creating focused wxCustomButton's bitmap
        wxImage imgFoc = bitmap.ConvertToImage();

        bool mask = false;
        unsigned char mr=0, mg=0, mb=0;
        if (img.HasMask())
        {
            mask = true;
            mr = imgDis.GetMaskRed();
            mg = imgDis.GetMaskGreen();
            mb = imgDis.GetMaskBlue();
        }
        unsigned char *r, *g, *b;
        unsigned char *focData = imgFoc.GetData();
        r = imgFoc.GetData();
        g = imgFoc.GetData() + 1;
        b = imgFoc.GetData() + 2;
        for (int j=0; j<h; j++)
        {
            for (int i=0; i<w; i++)
            {
                if ((!mask || ((*r!=mr)&&(*b!=mb)&&(*g!=mg))) &&
                    ((*r<236)&&(*b<236)&&(*g<236)))
                {
                    *r += 20; *g += 20; *b += 20;
                }
                r += 3; g += 3; b += 3;
            }
        }
        m_bmpFocus = wxBitmap(imgFoc);
*/

}

void wxCustomButton::SetBitmapLabel(const wxBitmap& bitmap)
{
    m_bmpLabel = bitmap;
    CalcLayout(true);
}

void wxCustomButton::OnPaint(wxPaintEvent& WXUNUSED(event))
{
    wxPaintDC dc(this);
    Paint(dc);
}

void wxCustomButton::Redraw()
{
    wxClientDC dc(this);
    Paint(dc);
}

void wxCustomButton::Paint( wxDC &dc )
{
    dc.BeginDrawing();

    int w, h;
    GetSize(&w,&h);

    wxColour foreColour = GetForegroundColour();
    wxColour backColour = GetBackgroundColour();

    if (m_focused)
    {
        backColour.Set( wxMin(backColour.Red()   + 20, 255),
                        wxMin(backColour.Green() + 20, 255),
                        wxMin(backColour.Blue()  + 20, 255) );
    }

    wxBitmap bitmap;

    if (IsEnabled())
    {
        if (GetValue() && m_bmpSelected.Ok())
            bitmap = m_bmpSelected;
        else if (m_focused && m_bmpFocus.Ok())
            bitmap = m_bmpFocus;
        else if (m_bmpLabel.Ok())
            bitmap = m_bmpLabel;
    }
    else
    {
        // try to create disabled if it doesn't exist
        if (!m_bmpDisabled.Ok() && m_bmpLabel.Ok())
            m_bmpDisabled = CreateBitmapDisabled(m_bmpLabel);

        if (m_bmpDisabled.Ok())
            bitmap = m_bmpDisabled;
        else if (m_bmpLabel.Ok())
            bitmap = m_bmpLabel;

        foreColour = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
    }

    wxBrush brush(backColour, wxSOLID);
    dc.SetBackground(brush);
    dc.SetBrush(brush);
    dc.SetPen(*wxTRANSPARENT_PEN);

    dc.DrawRectangle(0, 0, w, h);

    if (bitmap.Ok())
        dc.DrawBitmap(bitmap, m_bitmapPos.x, m_bitmapPos.y, true );

    if (!GetLabel().IsEmpty())
    {
        dc.SetFont(GetFont());
        dc.SetTextBackground(backColour);
        dc.SetTextForeground(foreColour);
        dc.DrawText(GetLabel(), m_labelPos.x, m_labelPos.y);
    }

    if (GetValue())                                        // draw sunken border
    {
        dc.SetPen(*wxGREY_PEN);
        dc.DrawLine(0,h-1,0,0);     dc.DrawLine(0,0,w,0);
        dc.SetPen(*wxWHITE_PEN);
        dc.DrawLine(w-1,1,w-1,h-1); dc.DrawLine(w-1,h-1,0,h-1);
        dc.SetPen(*wxBLACK_PEN);
        dc.DrawLine(1,h-2,1,1);     dc.DrawLine(1,1,w-1,1);
    }
    else if (((m_button_style & wxCUSTBUT_FLAT) == 0) || m_focused) // draw raised border
    {
        dc.SetPen(*wxWHITE_PEN);
        dc.DrawLine(0,h-2,0,0);     dc.DrawLine(0,0,w-1,0);
        dc.SetPen(*wxBLACK_PEN);
        dc.DrawLine(w-1,0,w-1,h-1); dc.DrawLine(w-1,h-1,-1,h-1);
        dc.SetPen(*wxGREY_PEN);
        dc.DrawLine(2,h-2,w-2,h-2); dc.DrawLine(w-2,h-2,w-2,1);
    }

    dc.SetBackground(wxNullBrush);
    dc.SetBrush(wxNullBrush);
    dc.SetPen(wxNullPen);
    dc.EndDrawing();
}

void wxCustomButton::OnSize( wxSizeEvent &event )
{
    CalcLayout(true);
    event.Skip();
}

void wxCustomButton::SetMargins(const wxSize &margin, bool fit)
{
    m_labelMargin = margin;
    m_bitmapMargin = margin;
    CalcLayout(true);
    if (fit) SetSize(DoGetBestSize());
}
void wxCustomButton::SetLabelMargin(const wxSize &margin, bool fit)
{
    m_labelMargin = margin;
    CalcLayout(true);
    if (fit) SetSize(DoGetBestSize());
}
void wxCustomButton::SetBitmapMargin(const wxSize &margin, bool fit)
{
    m_bitmapMargin = margin;
    CalcLayout(true);
    if (fit) SetSize(DoGetBestSize());
}

wxSize wxCustomButton::DoGetBestSize() const
{
    int lw=0, lh=0;
    int bw=0, bh=0;
    bool has_bitmap = false;
    bool has_label = false;

    if (!GetLabel().IsEmpty())
    {
        GetTextExtent(GetLabel(), &lw, &lh);
        lw += 2*m_labelMargin.x;
        lh += 2*m_labelMargin.y;
        has_label = true;
    }
    if (m_bmpLabel.Ok())
    {
        bw = m_bmpLabel.GetWidth() + 2*m_bitmapMargin.x;
        bh = m_bmpLabel.GetHeight() + 2*m_bitmapMargin.y;
        has_bitmap = true;
    }

    if ((m_button_style & wxCUSTBUT_LEFT) || (m_button_style & wxCUSTBUT_RIGHT))
    {
        int h = bh > lh ? bh : lh;
        if (has_bitmap && has_label) lw -= wxMin(m_labelMargin.x, m_bitmapMargin.x);
        return wxSize(lw+bw, h);
    }

    int w = bw > lw ? bw : lw;
    if (has_bitmap && has_label) lh -= wxMin(m_labelMargin.y, m_bitmapMargin.y);
    return wxSize(w, lh+bh);
}

void wxCustomButton::CalcLayout(bool refresh)
{
    int w, h;
    GetSize(&w,&h);

    int bw = 0, bh = 0;
    int lw = 0, lh = 0;

    if (m_bmpLabel.Ok()) // assume they're all the same size
    {
        bw = m_bmpLabel.GetWidth();
        bh = m_bmpLabel.GetHeight();
    }
    wxString label = GetLabel();
    if (!label.IsEmpty())
    {
        GetTextExtent(label, &lw, &lh);
    }

    // Center the label or bitmap if only one or the other
    if (!m_bmpLabel.Ok())
    {
        m_bitmapPos = wxPoint(0,0);
        m_labelPos = wxPoint((w-lw)/2, (h-lh)/2);
    }
    else if (label.IsEmpty())
    {
        m_bitmapPos = wxPoint((w-bw)/2, (h-bh)/2);
        m_labelPos = wxPoint(0,0);
    }
    else if (m_button_style & wxCUSTBUT_LEFT)
    {
        int mid_margin = wxMax(m_labelMargin.x, m_bitmapMargin.x);
        m_labelPos  = wxPoint((w - (bw+lw+m_labelMargin.x+m_bitmapMargin.x+mid_margin))/2 + m_labelMargin.x, (h - lh)/2);
        m_bitmapPos = wxPoint(m_labelPos.x + lw + mid_margin,         (h - bh)/2);
    }
    else if (m_button_style & wxCUSTBUT_RIGHT)
    {
        int mid_margin = wxMax(m_labelMargin.x, m_bitmapMargin.x);
        m_bitmapPos = wxPoint((w - (bw+lw+m_labelMargin.x+m_bitmapMargin.x+mid_margin))/2 + m_bitmapMargin.x, (h - bh)/2);
        m_labelPos  = wxPoint(m_bitmapPos.x + bw + mid_margin,        (h - lh)/2);
    }
    else if (m_button_style & wxCUSTBUT_TOP)
    {
        int mid_margin = wxMax(m_labelMargin.y, m_bitmapMargin.y);
        m_labelPos  = wxPoint((w - lw)/2, (h - (bh+lh+m_labelMargin.y+m_bitmapMargin.y+mid_margin))/2 + m_labelMargin.y);
        m_bitmapPos = wxPoint((w - bw)/2, m_labelPos.y + lh + mid_margin);
    }
    else // if (m_button_style & wxCUSTBUT_BOTTOM)  DEFAULT
    {
        int mid_margin = wxMax(m_labelMargin.y, m_bitmapMargin.y);
        m_bitmapPos = wxPoint((w - bw)/2, (h - (bh+lh+m_labelMargin.y+m_bitmapMargin.y+mid_margin))/2 + m_bitmapMargin.y);
        m_labelPos  = wxPoint((w - lw)/2, m_bitmapPos.y + bh + mid_margin);
    }

    if (refresh) Refresh(false);
}
