///////////////////////////////////////////////////////////////////////////////
// Name:        src/aui/tabartgtk.cpp
// Purpose:     implementation of the wxAuiGTKTabArt
// Author:      Jens Lody and Teodor Petrov
// Created:     2012-03-23
// Copyright:   (c) 2012 Jens Lody <jens@codeblocks.org>
//                  and Teodor Petrov
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

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


#if wxUSE_AUI

#ifndef WX_PRECOMP
    #include "wx/dc.h"
    #include "wx/dcclient.h"
    #include "wx/settings.h"
    #include "wx/image.h"
#endif

#include "wx/gtk/dc.h"
#include "wx/gtk/private.h"

#include "wx/aui/auibook.h"
#include "wx/aui/tabartgtk.h"
#include "wx/renderer.h"

namespace
{

static int s_CloseIconSize = 16; // default size

}

wxAuiGtkTabArt::wxAuiGtkTabArt()

{
}

wxAuiTabArt* wxAuiGtkTabArt::Clone()
{
    wxAuiGtkTabArt* clone = new wxAuiGtkTabArt();

    clone->SetNormalFont(m_normalFont);
    clone->SetSelectedFont(m_normalFont);
    clone->SetMeasuringFont(m_normalFont);

    return clone;
}

void wxAuiGtkTabArt::DrawBackground(wxDC& dc, wxWindow* WXUNUSED(wnd), const wxRect& rect)
{
    wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
    GdkWindow* window = impldc->GetGDKWindow();

    gtk_style_apply_default_background(gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget()),
                                       window,
                                       true,
                                       GTK_STATE_NORMAL,
                                       nullptr,
                                       rect.x, rect.y, rect.width, rect.height);
}

void wxAuiGtkTabArt::DrawBorder(wxDC& WXUNUSED(dc), wxWindow* wnd, const wxRect& rect)
{
    int generic_border_width = wxAuiGenericTabArt::GetBorderWidth(wnd);

    if (!wnd) return;
    if (!wnd->m_wxwindow) return;
    if (!gtk_widget_is_drawable(wnd->m_wxwindow)) return;

    GtkStyle *style_notebook = gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget());

    gtk_paint_box(style_notebook, wnd->GTKGetDrawingWindow(), GTK_STATE_NORMAL, GTK_SHADOW_OUT,
                  nullptr, wnd->m_wxwindow,
                  const_cast<char*>("notebook"),
                  rect.x + generic_border_width + 1, rect.y + generic_border_width + 1,
                  rect.width - (generic_border_width + 1), rect.height - (generic_border_width + 1));
}

void ButtonStateAndShadow(int button_state, GtkStateType &state, GtkShadowType &shadow)
{

    if (button_state & wxAUI_BUTTON_STATE_DISABLED)
    {
        state = GTK_STATE_INSENSITIVE;
        shadow = GTK_SHADOW_ETCHED_IN;
    }
    else if (button_state & wxAUI_BUTTON_STATE_HOVER)
    {
        state = GTK_STATE_PRELIGHT;
        shadow = GTK_SHADOW_OUT;
    }
    else if (button_state & wxAUI_BUTTON_STATE_PRESSED)
    {
        state = GTK_STATE_ACTIVE;
        shadow = GTK_SHADOW_IN;
    }
    else
    {
        state = GTK_STATE_NORMAL;
        shadow = GTK_SHADOW_OUT;
    }
}

wxRect DrawCloseButton(wxDC& dc,
                       GtkWidget *widget,
                       int button_state,
                       wxRect const &in_rect,
                       int orientation,
                       GdkRectangle* clipRect)
{
    GtkStyle *style_button = gtk_widget_get_style(wxGTKPrivate::GetButtonWidget());
    int xthickness = style_button->xthickness;
    int ythickness = style_button->ythickness;

    wxBitmap bmp(gtk_widget_render_icon(widget, GTK_STOCK_CLOSE, GTK_ICON_SIZE_SMALL_TOOLBAR, "tab"));

    if(bmp.GetLogicalWidth() != s_CloseIconSize || bmp.GetLogicalHeight() != s_CloseIconSize)
    {
        wxImage img = bmp.ConvertToImage();
        img.Rescale(s_CloseIconSize, s_CloseIconSize);
        bmp = img;
    }

    int button_size = s_CloseIconSize + 2 * xthickness;

    wxRect out_rect;

    if (orientation == wxLEFT)
        out_rect.x = in_rect.x - ythickness;
    else
        out_rect.x = in_rect.x + in_rect.width - button_size - ythickness;

    out_rect.y = in_rect.y + (in_rect.height - button_size) / 2;
    out_rect.width = button_size;
    out_rect.height = button_size;

    wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
    GdkWindow* window = impldc->GetGDKWindow();

    if (button_state == wxAUI_BUTTON_STATE_HOVER)
    {
        gtk_paint_box(style_button, window,
                      GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, clipRect, widget, "button",
                     out_rect.x, out_rect.y, out_rect.width, out_rect.height);
    }
    else if (button_state == wxAUI_BUTTON_STATE_PRESSED)
    {
        gtk_paint_box(style_button, window,
                      GTK_STATE_ACTIVE, GTK_SHADOW_IN, clipRect, widget, "button",
                      out_rect.x, out_rect.y, out_rect.width, out_rect.height);
    }


    dc.DrawBitmap(bmp, out_rect.x + xthickness, out_rect.y + ythickness, true);

    return out_rect;
}

void wxAuiGtkTabArt::DrawTab(wxDC& dc, wxWindow* wnd, const wxAuiNotebookPage& page,
                             const wxRect& in_rect, int close_button_state, wxRect* out_tab_rect,
                             wxRect* out_button_rect, int* x_extent)
{
    GtkWidget *widget = wnd->GetHandle();
    GtkStyle *style_notebook = gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget());

    wxRect const &window_rect = wnd->GetRect();

    int focus_width = 0;

    gtk_widget_style_get(wxGTKPrivate::GetNotebookWidget(),
                         "focus-line-width", &focus_width,
                         nullptr);

    int tab_pos;
    if (m_flags &wxAUI_NB_BOTTOM)
        tab_pos = wxAUI_NB_BOTTOM;
    else //if (m_flags & wxAUI_NB_TOP) {}
        tab_pos = wxAUI_NB_TOP;

    // TODO: else if (m_flags &wxAUI_NB_LEFT) {}
    // TODO: else if (m_flags &wxAUI_NB_RIGHT) {}

    // figure out the size of the tab
    wxSize tab_size = GetTabSize(dc, wnd, page.caption, page.bitmap,
                                    page.active, close_button_state, x_extent);

    wxRect tab_rect = in_rect;
    tab_rect.width = tab_size.x;
    tab_rect.height = tab_size.y;
    tab_rect.y += 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;

    if (page.active)
        tab_rect.height += 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;

    int gap_rect_height = 10 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
    int gap_rect_x = 1, gap_start = 0, gap_width = 0;
    int gap_rect_y = tab_rect.y - gap_rect_height;
    int gap_rect_width = window_rect.width;

    switch (tab_pos)
    {
        case wxAUI_NB_TOP:
            tab_rect.y -= 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
            if (!page.active)
                tab_rect.y += 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
            gap_rect_y = tab_rect.y + tab_rect.height - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder / 2;
            wxFALLTHROUGH;
        case wxAUI_NB_BOTTOM:
            gap_start = tab_rect.x - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder / 2;
            gap_width = tab_rect.width;
            break;
        // TODO: case wxAUI_NB_LEFT: break;
        // TODO: case wxAUI_NB_RIGHT: break;
    }
    tab_rect.y += GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder / 2;
    gap_rect_y += GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder / 2;

    int padding = focus_width + GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;

    int clip_width = tab_rect.width;
    if (tab_rect.x + tab_rect.width > in_rect.x + in_rect.width)
        clip_width = (in_rect.x + in_rect.width) - tab_rect.x;

    wxDCClipper clipper(dc, tab_rect.x, tab_rect.y - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder, clip_width, tab_rect.height + GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder);

    GdkRectangle area;
    area.x = tab_rect.x - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder;
    area.y = tab_rect.y - 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
    area.width = clip_width + GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder;
    area.height = tab_rect.height + 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;

    wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
    GdkWindow* window = impldc->GetGDKWindow();

    // Before drawing the active tab itself, draw a box without border, because some themes
    // have transparent gaps and a line would be visible at the bottom of the tab
    if (page.active)
        gtk_paint_box(style_notebook, window, GTK_STATE_NORMAL, GTK_SHADOW_NONE,
                      nullptr, widget,
                      const_cast<char*>("notebook"),
                      gap_rect_x, gap_rect_y,
                      gap_rect_width, gap_rect_height);

    if (tab_pos == wxAUI_NB_BOTTOM)
    {
        if (page.active)
        {
            gtk_paint_box_gap(style_notebook, window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
                              nullptr, widget,
                              const_cast<char*>("notebook"),
                              gap_rect_x, gap_rect_y,
                              gap_rect_width, gap_rect_height,
                              GTK_POS_BOTTOM, gap_start , gap_width);
        }
        gtk_paint_extension(style_notebook, window,
                           page.active ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE, GTK_SHADOW_OUT,
                           &area, widget,
                           const_cast<char*>("tab"),
                           tab_rect.x, tab_rect.y,
                           tab_rect.width, tab_rect.height,
                           GTK_POS_TOP);
    }
    else
    {
        if (page.active)
        {
            gtk_paint_box_gap(style_notebook, window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
                              nullptr, widget,
                              const_cast<char*>("notebook"),
                              gap_rect_x, gap_rect_y,
                              gap_rect_width, gap_rect_height,
                              GTK_POS_TOP, gap_start , gap_width);
        }
        gtk_paint_extension(style_notebook, window,
                           page.active ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE, GTK_SHADOW_OUT,
                           &area, widget,
                           const_cast<char*>("tab"),
                           tab_rect.x, tab_rect.y,
                           tab_rect.width, tab_rect.height,
                           GTK_POS_BOTTOM);
    }

    // After drawing the inactive tab itself, draw a box with the same dimensions as the gap-box,
    // otherwise we don't get a gap-box, if the active tab is invisible
    if (!page.active)
        gtk_paint_box(style_notebook, window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
                      nullptr, widget,
                      const_cast<char*>("notebook"),
                      gap_rect_x, gap_rect_y,
                      gap_rect_width, gap_rect_height);

    wxCoord textX = tab_rect.x + padding + style_notebook->xthickness;

    if (page.bitmap.IsOk())
    {
        int bitmap_offset;
        bitmap_offset = textX;

        const wxBitmap bitmap = page.bitmap.GetBitmapFor(wnd);

        // draw bitmap
        int bitmapY = tab_rect.y +(tab_rect.height - bitmap.GetLogicalHeight()) / 2;
        if(!page.active)
        {
            if (tab_pos == wxAUI_NB_TOP)
                bitmapY += style_notebook->ythickness / 2;
            else
                bitmapY -= style_notebook->ythickness / 2;
        }
        dc.DrawBitmap(bitmap,
                      bitmap_offset,
                      bitmapY,
                      true);

        textX += bitmap.GetLogicalWidth() + padding;
    }

    wxCoord textW, textH, textY;

    dc.SetFont(m_normalFont);
    dc.GetTextExtent(page.caption, &textW, &textH);
    textY = tab_rect.y + (tab_rect.height - textH) / 2;
    if(!page.active)
    {
        if (tab_pos == wxAUI_NB_TOP)
            textY += style_notebook->ythickness / 2;
        else
            textY -= style_notebook->ythickness / 2;
    }

    // draw tab text
    GdkColor text_colour = page.active ? style_notebook->fg[GTK_STATE_NORMAL] : style_notebook->fg[GTK_STATE_ACTIVE];
    dc.SetTextForeground(wxColor(text_colour));
    GdkRectangle focus_area;

    int padding_focus = padding - focus_width;
    focus_area.x = tab_rect.x + padding_focus;
    focus_area.y = textY - focus_width;
    focus_area.width = tab_rect.width - 2 * padding_focus;
    focus_area.height = textH + 2 * focus_width;

    if(page.active && (wnd->FindFocus() == wnd) && focus_area.x <= (area.x + area.width))
    {
        // clipping seems not to work here, so we have to recalc the focus-area manually
        if((focus_area.x + focus_area.width) > (area.x + area.width))
            focus_area.width = area.x + area.width - focus_area.x + focus_width - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder;
        gtk_paint_focus (style_notebook, window,
                         GTK_STATE_ACTIVE, nullptr, widget, "tab",
                         focus_area.x, focus_area.y, focus_area.width, focus_area.height);
    }

    dc.DrawText(page.caption, textX, textY);

    // draw close-button on tab (if enabled)
    if (close_button_state != wxAUI_BUTTON_STATE_HIDDEN)
    {
        wxRect rect(tab_rect.x, tab_rect.y, tab_rect.width - style_notebook->xthickness, tab_rect.height);
        if(!page.active)
        {
            if (tab_pos == wxAUI_NB_TOP)
                rect.y += style_notebook->ythickness / 2;
            else
                rect.y -= style_notebook->ythickness / 2;
        }
        *out_button_rect = DrawCloseButton(dc, widget, close_button_state, rect, wxRIGHT, &area);
    }

    if ( clip_width < tab_rect.width )
        tab_rect.width = clip_width;
    *out_tab_rect = tab_rect;
}

wxRect DrawSimpleArrow(wxDC& dc,
                       GtkWidget *widget,
                       int button_state,
                       wxRect const &in_rect,
                       int orientation,
                       GtkArrowType arrow_type)
{
    int scroll_arrow_hlength, scroll_arrow_vlength;
    gtk_widget_style_get(widget,
                         "scroll-arrow-hlength", &scroll_arrow_hlength,
                         "scroll-arrow-vlength", &scroll_arrow_vlength,
                         nullptr);

    GtkStateType state;
    GtkShadowType shadow;
    ButtonStateAndShadow(button_state, state, shadow);

    wxRect out_rect;

    if (orientation == wxLEFT)
        out_rect.x = in_rect.x;
    else
        out_rect.x = in_rect.x + in_rect.width - scroll_arrow_hlength;
    out_rect.y = (in_rect.y + in_rect.height - 3 * gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget())->ythickness - scroll_arrow_vlength) / 2;
    out_rect.width = scroll_arrow_hlength;
    out_rect.height = scroll_arrow_vlength;

    wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
    GdkWindow* window = impldc->GetGDKWindow();
    gtk_paint_arrow (gtk_widget_get_style(wxGTKPrivate::GetButtonWidget()), window, state, shadow, nullptr, widget, "notebook",
                     arrow_type, TRUE, out_rect.x, out_rect.y, out_rect.width, out_rect.height);

    return out_rect;
}

void wxAuiGtkTabArt::DrawButton(wxDC& dc, wxWindow* wnd,
                            const wxRect& in_rect,
                            int bitmap_id,
                            int button_state,
                            int orientation,
                            wxRect* out_rect)
{
    GtkWidget *widget = wnd->GetHandle();
    wxRect rect = in_rect;
    if (m_flags &wxAUI_NB_BOTTOM)
        rect.y += 2 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness;

    switch (bitmap_id)
    {
        case wxAUI_BUTTON_CLOSE:
            rect.y -= 2 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness;
            rect = DrawCloseButton(dc, widget, button_state, rect, orientation, nullptr);
            break;

        case wxAUI_BUTTON_LEFT:
            rect = DrawSimpleArrow(dc, widget, button_state, rect, orientation, GTK_ARROW_LEFT);
            break;

        case wxAUI_BUTTON_RIGHT:
            rect = DrawSimpleArrow(dc, widget, button_state, rect, orientation, GTK_ARROW_RIGHT);
            break;

        case wxAUI_BUTTON_WINDOWLIST:
            {
                rect.height -= 4 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness;
                rect.width = rect.height;
                rect.x = in_rect.x + in_rect.width - rect.width;

                if (button_state == wxAUI_BUTTON_STATE_HOVER)
                    wxRendererNative::Get().DrawComboBoxDropButton(wnd, dc, rect, wxCONTROL_CURRENT);
                else if (button_state == wxAUI_BUTTON_STATE_PRESSED)
                    wxRendererNative::Get().DrawComboBoxDropButton(wnd, dc, rect, wxCONTROL_PRESSED);
                else
                    wxRendererNative::Get().DrawDropArrow(wnd, dc, rect);
            }
            break;
    }

    *out_rect = rect;
}


int wxAuiGtkTabArt::GetBestTabCtrlSize(wxWindow* wnd,
                                   const wxAuiNotebookPageArray& pages,
                                   const wxSize& required_bmp_size)
{
    SetMeasuringFont(m_normalFont);
    SetSelectedFont(m_normalFont);
    int tab_height = 3 * gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget())->ythickness + wxAuiGenericTabArt::GetBestTabCtrlSize(wnd, pages, required_bmp_size);
    return tab_height;
}

int wxAuiGtkTabArt::GetBorderWidth(wxWindow* wnd)
{
    return wxAuiGenericTabArt::GetBorderWidth(wnd) + wxMax(GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder,
                                                           GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder);
}

int wxAuiGtkTabArt::GetAdditionalBorderSpace(wxWindow* wnd)
{
    return 2 * GetBorderWidth(wnd);
}

wxSize wxAuiGtkTabArt::GetTabSize(wxReadOnlyDC& dc,
                              wxWindow* wnd,
                              const wxString& caption,
                              const wxBitmapBundle& bitmap,
                              bool active,
                              int close_button_state,
                              int* x_extent)
{
    wxSize s = wxAuiGenericTabArt::GetTabSize(dc, wnd, caption, bitmap, active, close_button_state, x_extent);

    int overlap = 0;
    gtk_widget_style_get (wnd->GetHandle(),
        "focus-line-width", &overlap,
        nullptr);
    *x_extent -= overlap;
    return s;
}
#endif  // wxUSE_AUI
