/////////////////////////////////////////////////////////////////////////////
// Name:        src/generic/tabg.cpp
// Purpose:     Generic tabbed dialogs; used by generic wxNotebook
// Author:      Julian Smart
// Created:     01/02/97
// Copyright:   (c)
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

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


#ifndef WX_PRECOMP
    #include "wx/settings.h"
    #include "wx/intl.h"
    #include "wx/dcclient.h"
    #include "wx/math.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include "wx/generic/tabg.h"
#include "wx/listimpl.cpp"

WX_DEFINE_LIST(wxTabLayerList)

// not defined: use old, square tab implementation (fills in tabs)
// defined: use new, rounded tab implementation (doesn't colour in tabs)
// #define wxUSE_NEW_METHOD

wxIMPLEMENT_DYNAMIC_CLASS(wxTabControl, wxObject);

// wxIMPLEMENT_DYNAMIC_CLASS(wxTabLayer, wxList);

wxTabControl::wxTabControl(wxTabView *v)
{
  m_view = v;
  m_isSelected = false;
  m_offsetX = 0;
  m_offsetY = 0;
  m_width = 0;
  m_height = 0;
  m_id = 0;
  m_rowPosition = 0;
  m_colPosition = 0;
}

wxTabControl::~wxTabControl(void)
{
}

void wxTabControl::OnDraw(wxDC& dc, bool lastInRow)
{
    // Old, but in some ways better (drawing opaque tabs)
#ifndef wxUSE_NEW_METHOD
  if (!m_view)
    return;

  // Top-left of tab view area
  int viewX = m_view->GetViewRect().x;
  int viewY = m_view->GetViewRect().y;

  // Top-left of tab control
  int tabX = GetX() + viewX;
  int tabY = GetY() + viewY;
  int tabHeightInc = 0;
  if (m_isSelected)
  {
    tabHeightInc = (m_view->GetTabSelectionHeight() - m_view->GetTabHeight());
    tabY -= tabHeightInc;
  }

  dc.SetPen(*wxTRANSPARENT_PEN);

  // Draw grey background
  if (m_view->GetTabStyle() & wxTAB_STYLE_COLOUR_INTERIOR)
  {
    if(m_view->GetBackgroundBrush())
      dc.SetBrush(*m_view->GetBackgroundBrush());

    dc.DrawRectangle(tabX, tabY, (GetWidth()+1), (GetHeight() + 1 + tabHeightInc));
  }

  // Draw highlight and shadow
  dc.SetPen(*m_view->GetHighlightPen());

  // Calculate the top of the tab beneath. It's the height of the tab, MINUS
  // a bit if the tab below happens to be selected. Check.
  wxTabControl *tabBeneath = nullptr;
  int subtractThis = 0;
  if (GetColPosition() > 0)
    tabBeneath = m_view->FindTabControlForPosition(GetColPosition() - 1, GetRowPosition());
  if (tabBeneath && tabBeneath->IsSelected())
    subtractThis = (m_view->GetTabSelectionHeight() - m_view->GetTabHeight());

  // Vertical highlight: if first tab, draw to bottom of view
  if (tabX == m_view->GetViewRect().x && (m_view->GetTabStyle() & wxTAB_STYLE_DRAW_BOX))
    dc.DrawLine(tabX, tabY, tabX, (m_view->GetViewRect().y + m_view->GetViewRect().height));
  else if (tabX == m_view->GetViewRect().x)
    // Not box drawing, just to top of view.
    dc.DrawLine(tabX, tabY, tabX, (m_view->GetViewRect().y));
  else
    dc.DrawLine(tabX, tabY, tabX, (tabY + GetHeight() + tabHeightInc - subtractThis));

  dc.DrawLine(tabX, tabY, (tabX + GetWidth()), tabY);
  dc.SetPen(*m_view->GetShadowPen());

  // Test if we're outside the right-hand edge of the view area
  if (((tabX + GetWidth()) >= m_view->GetViewRect().x + m_view->GetViewRect().width) && (m_view->GetTabStyle() & wxTAB_STYLE_DRAW_BOX))
  {
    int bottomY = m_view->GetViewRect().y + m_view->GetViewRect().height + GetY() + m_view->GetTabHeight() + m_view->GetTopMargin();
    // Add a tab height since we wish to draw to the bottom of the view.
    dc.DrawLine((tabX + GetWidth()), tabY,
      (tabX + GetWidth()), bottomY);

    // Calculate the far-right of the view, since we don't wish to
    // draw inside that
    int rightOfView = m_view->GetViewRect().x + m_view->GetViewRect().width + 1;

    // Draw the horizontal bit to connect to the view rectangle
    dc.DrawLine((wxMax((tabX + GetWidth() - m_view->GetHorizontalTabOffset()), rightOfView)), (bottomY-1),
      (tabX + GetWidth()), (bottomY-1));

    // Draw black line to emphasize shadow
    dc.SetPen(*wxBLACK_PEN);
    dc.DrawLine((tabX + GetWidth() + 1), (tabY+1),
      (tabX + GetWidth() + 1), bottomY);

    // Draw the horizontal bit to connect to the view rectangle
    dc.DrawLine((wxMax((tabX + GetWidth() - m_view->GetHorizontalTabOffset()), rightOfView)), (bottomY),
      (tabX + GetWidth() + 1), (bottomY));
  }
  else
  {
    if (lastInRow)
    {
      // 25/5/97 UNLESS it's less than the max number of positions in this row

      int topY = m_view->GetViewRect().y - m_view->GetTopMargin();

      int maxPositions = ((wxTabLayer *)m_view->GetLayers().Item(0)->GetData())->GetCount();

      // Only down to the bottom of the tab, not to the top of the view
      if ( GetRowPosition() < (maxPositions - 1) )
        topY = tabY + GetHeight() + tabHeightInc;

      // Shadow
      dc.DrawLine((tabX + GetWidth()), tabY, (tabX + GetWidth()), topY);
      // Draw black line to emphasize shadow
      dc.SetPen(*wxBLACK_PEN);
      dc.DrawLine((tabX + GetWidth() + 1), (tabY+1), (tabX + GetWidth() + 1),
         topY);
    }
    else
    {
      // Calculate the top of the tab beneath. It's the height of the tab, MINUS
      // a bit if the tab below (and next col along) happens to be selected. Check.
      wxTabControl *tabBeneath = nullptr;
      int subtractThis = 0;
      if (GetColPosition() > 0)
        tabBeneath = m_view->FindTabControlForPosition(GetColPosition() - 1, GetRowPosition() + 1);
      if (tabBeneath && tabBeneath->IsSelected())
        subtractThis = (m_view->GetTabSelectionHeight() - m_view->GetTabHeight());

      // Draw only to next tab down.
      dc.DrawLine((tabX + GetWidth()), tabY,
         (tabX + GetWidth()), (tabY + GetHeight() + tabHeightInc - subtractThis));

      // Draw black line to emphasize shadow
      dc.SetPen(*wxBLACK_PEN);
      dc.DrawLine((tabX + GetWidth() + 1), (tabY+1), (tabX + GetWidth() + 1),
         (tabY + GetHeight() + tabHeightInc - subtractThis));
    }
  }

  // Draw centered text
  int textY = tabY + m_view->GetVerticalTabTextSpacing() + tabHeightInc;

  if (m_isSelected)
    dc.SetFont(* m_view->GetSelectedTabFont());
  else
    dc.SetFont(* GetFont());

  wxColour col(m_view->GetTextColour());
  dc.SetTextForeground(col);
  dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
  wxCoord textWidth, textHeight;
  dc.GetTextExtent(GetLabel(), &textWidth, &textHeight);

  int textX = (int)(tabX + (GetWidth() - textWidth)/2.0);
  if (textX < (tabX + 2))
    textX = (tabX + 2);

  dc.SetClippingRegion(tabX, tabY, GetWidth(), GetHeight());
  dc.DrawText(GetLabel(), textX, textY);
  dc.DestroyClippingRegion();

  if (m_isSelected)
  {
    dc.SetPen(*m_view->GetHighlightPen());

    // Draw white highlight from the tab's left side to the left hand edge of the view
    dc.DrawLine(m_view->GetViewRect().x, (tabY + GetHeight() + tabHeightInc),
     tabX, (tabY + GetHeight() + tabHeightInc));

    // Draw white highlight from the tab's right side to the right hand edge of the view
    dc.DrawLine((tabX + GetWidth()), (tabY + GetHeight() + tabHeightInc),
     m_view->GetViewRect().x + m_view->GetViewRect().width, (tabY + GetHeight() + tabHeightInc));
  }
#else
    // New HEL version with rounder tabs

    if (!m_view) return;

    int tabInc   = 0;
    if (m_isSelected)
    {
        tabInc = m_view->GetTabSelectionHeight() - m_view->GetTabHeight();
    }
    int tabLeft  = GetX() + m_view->GetViewRect().x;
    int tabTop   = GetY() + m_view->GetViewRect().y - tabInc;
    int tabRight = tabLeft + m_view->GetTabWidth();
    int left     = m_view->GetViewRect().x;
    int top      = tabTop + m_view->GetTabHeight() + tabInc;
    int right    = left + m_view->GetViewRect().width;
    int bottom   = top + m_view->GetViewRect().height;

    if (m_isSelected)
    {
        // TAB is selected - draw TAB and the View's full outline

        dc.SetPen(*(m_view->GetHighlightPen()));
        wxPoint pnts[10];
        int n = 0;
        pnts[n].x = left;            pnts[n++].y = bottom;
        pnts[n].x = left;             pnts[n++].y = top;
        pnts[n].x = tabLeft;         pnts[n++].y = top;
        pnts[n].x = tabLeft;            pnts[n++].y = tabTop + 2;
        pnts[n].x = tabLeft + 2;        pnts[n++].y = tabTop;
        pnts[n].x = tabRight - 1;    pnts[n++].y = tabTop;
        dc.DrawLines(n, pnts);
        if (!lastInRow)
        {
            dc.DrawLine(
                    (tabRight + 2),
                    top,
                    right,
                    top
                    );
        }

        dc.SetPen(*(m_view->GetShadowPen()));
        dc.DrawLine(
                tabRight,
                tabTop + 2,
                tabRight,
                top
                );
        dc.DrawLine(
                right,
                top,
                right,
                bottom
                );
        dc.DrawLine(
                right,
                bottom,
                left,
                bottom
                );

        dc.SetPen(*wxBLACK_PEN);
        dc.DrawPoint(
                tabRight,
                tabTop + 1
                );
        dc.DrawPoint(
                tabRight + 1,
                tabTop + 2
                );
        if (lastInRow)
        {
            dc.DrawLine(
                tabRight + 1,
                bottom,
                tabRight + 1,
                tabTop + 1
                );
        }
        else
        {
            dc.DrawLine(
                tabRight + 1,
                tabTop + 2,
                tabRight + 1,
                top
                );
            dc.DrawLine(
                right + 1,
                top,
                right + 1,
                bottom + 1
                );
        }
        dc.DrawLine(
                right + 1,
                bottom + 1,
                left + 1,
                bottom + 1
                );
    }
    else
    {
        // TAB is not selected - just draw TAB outline and RH edge
        // if the TAB is the last in the row

        int maxPositions = ((wxTabLayer*)m_view->GetLayers().Item(0)->GetData())->GetCount();
        wxTabControl* tabBelow = 0;
        wxTabControl* tabBelowRight = 0;
        if (GetColPosition() > 0)
        {
            tabBelow = m_view->FindTabControlForPosition(
                        GetColPosition() - 1,
                        GetRowPosition()
                        );
        }
        if (!lastInRow && GetColPosition() > 0)
        {
            tabBelowRight = m_view->FindTabControlForPosition(
                        GetColPosition() - 1,
                        GetRowPosition() + 1
                        );
        }

        float raisedTop = top - m_view->GetTabSelectionHeight() +
                            m_view->GetTabHeight();

        dc.SetPen(*(m_view->GetHighlightPen()));
        wxPoint pnts[10];
        int n = 0;

        pnts[n].x = tabLeft;

        if (tabBelow && tabBelow->IsSelected())
        {
            pnts[n++].y = (long)raisedTop;
        }
        else
        {
            pnts[n++].y = top;
        }
        pnts[n].x = tabLeft;            pnts[n++].y = tabTop + 2;
        pnts[n].x = tabLeft + 2;        pnts[n++].y = tabTop;
        pnts[n].x = tabRight - 1;    pnts[n++].y = tabTop;
        dc.DrawLines(n, pnts);

        dc.SetPen(*(m_view->GetShadowPen()));
        if (GetRowPosition() >= maxPositions - 1)
        {
            dc.DrawLine(
                    tabRight,
                    (tabTop + 2),
                    tabRight,
                    bottom
                    );
            dc.DrawLine(
                    tabRight,
                    bottom,
                    (tabRight - m_view->GetHorizontalTabOffset()),
                    bottom
                    );
        }
        else
        {
            if (tabBelowRight && tabBelowRight->IsSelected())
            {
                dc.DrawLine(
                        tabRight,
                        (long)raisedTop,
                        tabRight,
                        tabTop + 1
                        );
            }
            else
            {
                dc.DrawLine(
                        tabRight,
                        top - 1,
                        tabRight,
                        tabTop + 1
                        );
            }
        }

        dc.SetPen(*wxBLACK_PEN);
        dc.DrawPoint(
                tabRight,
                tabTop + 1
                );
        dc.DrawPoint(
                tabRight + 1,
                tabTop + 2
                );
        if (GetRowPosition() >= maxPositions - 1)
        {
            // draw right hand edge to bottom of view
            dc.DrawLine(
                    tabRight + 1,
                    bottom + 1,
                    tabRight + 1,
                    tabTop + 2
                    );
            dc.DrawLine(
                    tabRight + 1,
                    bottom + 1,
                    (tabRight - m_view->GetHorizontalTabOffset()),
                    bottom + 1
                    );
        }
        else
        {
            // draw right hand edge of TAB
            if (tabBelowRight && tabBelowRight->IsSelected())
            {
                dc.DrawLine(
                        tabRight + 1,
                        (long)(raisedTop - 1),
                        tabRight + 1,
                        tabTop + 2
                        );
            }
            else
            {
                dc.DrawLine(
                        tabRight + 1,
                        top - 1,
                        tabRight + 1,
                        tabTop + 2
                        );
            }
        }
    }

    // Draw centered text
    dc.SetPen(*wxBLACK_PEN);
    if (m_isSelected)
    {
        dc.SetFont(*(m_view->GetSelectedTabFont()));
    }
    else
    {
        dc.SetFont(*(GetFont()));
    }

    wxColour col(m_view->GetTextColour());
    dc.SetTextForeground(col);
    dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
    long textWidth, textHeight;
    dc.GetTextExtent(GetLabel(), &textWidth, &textHeight);

    float textX = (tabLeft + tabRight - textWidth) / 2;
    float textY = (tabInc + tabTop + m_view->GetVerticalTabTextSpacing());

    dc.DrawText(GetLabel(), (long)textX, (long)textY);
#endif
}

bool wxTabControl::HitTest(int x, int y) const
{
  // Top-left of tab control
  int tabX1 = GetX() + m_view->GetViewRect().x;
  int tabY1 = GetY() + m_view->GetViewRect().y;

  // Bottom-right
  int tabX2 = tabX1 + GetWidth();
  int tabY2 = tabY1 + GetHeight();

  if (x >= tabX1 && y >= tabY1 && x <= tabX2 && y <= tabY2)
    return true;
  else
    return false;
}

wxIMPLEMENT_DYNAMIC_CLASS(wxTabView, wxObject);

wxTabView::wxTabView(long style)
{
  m_noTabs = 0;
  m_tabStyle = style;
  m_tabSelection = -1;
  m_tabHeight = 20;
  m_tabSelectionHeight = m_tabHeight + 2;
  m_tabWidth = 80;
  m_tabHorizontalOffset = 10;
  m_tabHorizontalSpacing = 2;
  m_tabVerticalTextSpacing = 3;
  m_topMargin = 5;
  m_tabViewRect.x = 20;
  m_tabViewRect.y = 20;
  m_tabViewRect.width = 300;
  m_tabViewRect.x = 300;
  m_highlightColour = *wxWHITE;
  m_shadowColour = wxColour(128, 128, 128);
  // m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
  m_textColour = *wxBLACK;
  m_highlightPen = wxWHITE_PEN;
  m_shadowPen = wxGREY_PEN;
  // SetBackgroundColour(m_backgroundColour);
  m_tabFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
  m_tabSelectedFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
  m_window = nullptr;
}

wxTabView::~wxTabView()
{
  ClearTabs(true);
}

// Automatically positions tabs
// TODO: this should just add the tab to a list, and then
// a layout function (e.g. Realize) should be called when all tabs have been added.
// The view rect could easily change as the view window is resized.
wxTabControl *wxTabView::AddTab(int id, const wxString& label, wxTabControl *existingTab)
{
  // First, find which layer we should be adding to.
  wxTabLayerList::compatibility_iterator node = m_layers.GetLast();
  if (!node)
  {
    wxTabLayer *newLayer = new wxTabLayer;
    node = m_layers.Append(newLayer);
  }
  // Check if adding another tab control would go off the
  // right-hand edge of the layer.
  wxTabLayer *tabLayer = (wxTabLayer *)node->GetData();
  wxList::compatibility_iterator lastTabNode = tabLayer->GetLast();
  if (lastTabNode)
  {
    wxTabControl *lastTab = (wxTabControl *)lastTabNode->GetData();
    // Start another layer (row).
    // Tricky choice: can't just check if will be overlapping the edge, because
    // this happens anyway for 2nd and subsequent rows.
    // Should check this for 1st row, and then subsequent rows should not exceed 1st
    // in length.
    if (((tabLayer == m_layers.GetFirst()->GetData()) && ((lastTab->GetX() + 2*lastTab->GetWidth() + GetHorizontalTabSpacing())
              > GetViewRect().width)) ||
        ((tabLayer != m_layers.GetFirst()->GetData()) && (tabLayer->GetCount() == ((wxTabLayer *)m_layers.GetFirst()->GetData())->GetCount())))
    {
      tabLayer = new wxTabLayer;
      m_layers.Append(tabLayer);
      lastTabNode = wxList::compatibility_iterator();
    }
  }
  int layer = m_layers.GetCount() - 1;

  wxTabControl *tabControl = existingTab;
  if (!existingTab)
    tabControl = OnCreateTabControl();
  tabControl->SetRowPosition(tabLayer->GetCount());
  tabControl->SetColPosition(layer);

  wxTabControl *lastTab = nullptr;
  if (lastTabNode)
    lastTab = (wxTabControl *)lastTabNode->GetData();

  // Top of new tab
  int verticalOffset = (- GetTopMargin()) - ((layer+1)*GetTabHeight());
  // Offset from view top-left
  int horizontalOffset = 0;
  if (!lastTab)
    horizontalOffset = layer*GetHorizontalTabOffset();
  else
    horizontalOffset = lastTab->GetX() + GetTabWidth() + GetHorizontalTabSpacing();

  tabControl->SetPosition(horizontalOffset, verticalOffset);
  tabControl->SetSize(GetTabWidth(), GetTabHeight());
  tabControl->SetId(id);
  tabControl->SetLabel(label);
  tabControl->SetFont(* GetTabFont());

  tabLayer->Append(tabControl);
  m_noTabs ++;

  return tabControl;
}

// Remove the tab without deleting the window
bool wxTabView::RemoveTab(int id)
{
  wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
  while (layerNode)
  {
    wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
    wxList::compatibility_iterator tabNode = layer->GetFirst();
    while (tabNode)
    {
      wxTabControl *tab = (wxTabControl *)tabNode->GetData();
      if (tab->GetId() == id)
      {
        if (id == m_tabSelection)
          m_tabSelection = -1;
        delete tab;
        layer->Erase(tabNode);
        m_noTabs --;

        // The layout has changed
        LayoutTabs();
        return true;
      }
      tabNode = tabNode->GetNext();
    }
    layerNode = layerNode->GetNext();
  }
  return false;
}

bool wxTabView::SetTabText(int id, const wxString& label)
{
    wxTabControl* control = FindTabControlForId(id);
    if (!control)
      return false;
    control->SetLabel(label);
    return true;
}

wxString wxTabView::GetTabText(int id) const
{
    wxTabControl* control = FindTabControlForId(id);
    if (!control)
      return wxEmptyString;
    else
      return control->GetLabel();
}

// Returns the total height of the tabs component -- this may be several
// times the height of a tab, if there are several tab layers (rows).
int wxTabView::GetTotalTabHeight()
{
  int minY = 0;

  wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
  while (layerNode)
  {
    wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
    wxList::compatibility_iterator tabNode = layer->GetFirst();
    while (tabNode)
    {
      wxTabControl *tab = (wxTabControl *)tabNode->GetData();

      if (tab->GetY() < minY)
        minY = tab->GetY();

      tabNode = tabNode->GetNext();
    }
    layerNode = layerNode->GetNext();
  }

  return - minY;
}

void wxTabView::ClearTabs(bool deleteTabs)
{
  wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
  while (layerNode)
  {
    wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
    wxList::compatibility_iterator tabNode = layer->GetFirst();
    while (tabNode)
    {
      wxTabControl *tab = (wxTabControl *)tabNode->GetData();
      if (deleteTabs)
        delete tab;
      wxList::compatibility_iterator next = tabNode->GetNext();
      layer->Erase(tabNode);
      tabNode = next;
    }
    wxTabLayerList::compatibility_iterator nextLayerNode = layerNode->GetNext();
    delete layer;
    m_layers.Erase(layerNode);
    layerNode = nextLayerNode;
  }
  m_noTabs = 0;
  m_tabSelection = -1;
}


// Layout tabs (optional, e.g. if resizing window)
void wxTabView::LayoutTabs(void)
{
  // Make a list of the tab controls, deleting the wxTabLayers.
  wxList controls;

  wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
  while (layerNode)
  {
    wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
    wxList::compatibility_iterator tabNode = layer->GetFirst();
    while (tabNode)
    {
      wxTabControl *tab = (wxTabControl *)tabNode->GetData();
      controls.Append(tab);
      wxList::compatibility_iterator next = tabNode->GetNext();
      layer->Erase(tabNode);
      tabNode = next;
    }
    wxTabLayerList::compatibility_iterator nextLayerNode = layerNode->GetNext();
    delete layer;
    m_layers.Erase(layerNode);
    layerNode = nextLayerNode;
  }

  wxTabControl *lastTab = nullptr;

  wxTabLayer *currentLayer = new wxTabLayer;
  m_layers.Append(currentLayer);

  wxList::compatibility_iterator node = controls.GetFirst();
  while (node)
  {
    wxTabControl *tabControl = (wxTabControl *)node->GetData();
    if (lastTab)
    {
      // Start another layer (row).
      // Tricky choice: can't just check if will be overlapping the edge, because
      // this happens anyway for 2nd and subsequent rows.
      // Should check this for 1st row, and then subsequent rows should not exceed 1st
      // in length.
      if (((currentLayer == m_layers.GetFirst()->GetData()) && ((lastTab->GetX() + 2*lastTab->GetWidth() + GetHorizontalTabSpacing())
                > GetViewRect().width)) ||
          ((currentLayer != m_layers.GetFirst()->GetData()) && (currentLayer->GetCount() == ((wxTabLayer *)m_layers.GetFirst()->GetData())->GetCount())))
     {
       currentLayer = new wxTabLayer;
       m_layers.Append(currentLayer);
       lastTab = nullptr;
     }
    }

    int layer = m_layers.GetCount() - 1;

    tabControl->SetRowPosition(currentLayer->GetCount());
    tabControl->SetColPosition(layer);

    // Top of new tab
    int verticalOffset = (- GetTopMargin()) - ((layer+1)*GetTabHeight());
    // Offset from view top-left
    int horizontalOffset = 0;
    if (!lastTab)
      horizontalOffset = layer*GetHorizontalTabOffset();
    else
      horizontalOffset = lastTab->GetX() + GetTabWidth() + GetHorizontalTabSpacing();

    tabControl->SetPosition(horizontalOffset, verticalOffset);
    tabControl->SetSize(GetTabWidth(), GetTabHeight());

    currentLayer->Append(tabControl);
    lastTab = tabControl;

    node = node->GetNext();
  }

  // Move the selected tab to the bottom
  wxTabControl *control = FindTabControlForId(m_tabSelection);
  if (control)
    MoveSelectionTab(control);

}

// Draw all tabs
void wxTabView::Draw(wxDC& dc)
{
        // Don't draw anything if there are no tabs.
        if (GetNumberOfTabs() == 0)
          return;

    // Draw top margin area (beneath tabs and above view area)
    if (GetTabStyle() & wxTAB_STYLE_COLOUR_INTERIOR)
    {
        dc.SetPen(*wxTRANSPARENT_PEN);
        if(GetBackgroundBrush())
            dc.SetBrush(*GetBackgroundBrush());

        // Add 1 because the pen is transparent.
        dc.DrawRectangle(
                m_tabViewRect.x,
                (m_tabViewRect.y - m_topMargin),
                (m_tabViewRect.width + 1),
                (m_topMargin + 1)
                );
    }

    // Draw layers in reverse order
    wxTabLayerList::compatibility_iterator node = m_layers.GetLast();
    while (node)
    {
        wxTabLayer *layer = (wxTabLayer *)node->GetData();
        wxList::compatibility_iterator node2 = layer->GetFirst();
        while (node2)
        {
            wxTabControl *control = (wxTabControl *)node2->GetData();
            control->OnDraw(dc, (!node2->GetNext()));
            node2 = node2->GetNext();
        }

        node = node->GetPrevious();
    }


#ifndef wxUSE_NEW_METHOD
    if (GetTabStyle() & wxTAB_STYLE_DRAW_BOX)
    {
        dc.SetPen(* GetShadowPen());

        // Draw bottom line
        dc.DrawLine(
                (GetViewRect().x + 1),
                (GetViewRect().y + GetViewRect().height),
                (GetViewRect().x + GetViewRect().width + 1),
                (GetViewRect().y + GetViewRect().height)
                );

        // Draw right line
        dc.DrawLine(
                (GetViewRect().x + GetViewRect().width),
                (GetViewRect().y - GetTopMargin() + 1),
                (GetViewRect().x + GetViewRect().width),
                (GetViewRect().y + GetViewRect().height)
                );

        dc.SetPen(* wxBLACK_PEN);

        // Draw bottom line
        dc.DrawLine(
                (GetViewRect().x),
                (GetViewRect().y + GetViewRect().height + 1),
                (GetViewRect().x + GetViewRect().width + 2),
                (GetViewRect().y + GetViewRect().height + 1)
                );

        // Draw right line
        dc.DrawLine(
                (GetViewRect().x + GetViewRect().width + 1),
                (GetViewRect().y - GetTopMargin()),
                (GetViewRect().x + GetViewRect().width + 1),
                (GetViewRect().y + GetViewRect().height + 1)
                );
    }
#endif
}

// Process mouse event, return false if we didn't process it
bool wxTabView::OnEvent(wxMouseEvent& event)
{
  if (!event.LeftDown())
    return false;

  wxCoord x, y;
  event.GetPosition(&x, &y);

  wxTabControl *hitControl = nullptr;

  wxTabLayerList::compatibility_iterator node = m_layers.GetFirst();
  while (node)
  {
    wxTabLayer *layer = (wxTabLayer *)node->GetData();
    wxList::compatibility_iterator node2 = layer->GetFirst();
    while (node2)
    {
      wxTabControl *control = (wxTabControl *)node2->GetData();
      if (control->HitTest((int)x, (int)y))
      {
        hitControl = control;
        node = wxTabLayerList::compatibility_iterator();
        node2 = wxList::compatibility_iterator();
      }
      else
        node2 = node2->GetNext();
    }

    if (node)
      node = node->GetNext();
  }

  if (!hitControl)
    return false;

  wxTabControl *currentTab = FindTabControlForId(m_tabSelection);

  if (hitControl == currentTab)
    return false;

  ChangeTab(hitControl);

  return true;
}

bool wxTabView::ChangeTab(wxTabControl *control)
{
  wxTabControl *currentTab = FindTabControlForId(m_tabSelection);
  int oldTab = -1;
  if (currentTab)
    oldTab = currentTab->GetId();

  if (control == currentTab)
    return true;

  if (m_layers.GetCount() == 0)
    return false;

  if (!OnTabPreActivate(control->GetId(), oldTab))
    return false;

  // Move the tab to the bottom
  MoveSelectionTab(control);

  if (currentTab)
    currentTab->SetSelected(false);

  control->SetSelected(true);
  m_tabSelection = control->GetId();

  OnTabActivate(control->GetId(), oldTab);

  // Leave window refresh for the implementing window

  return true;
}

// Move the selected tab to the bottom layer, if necessary,
// without calling app activation code
bool wxTabView::MoveSelectionTab(wxTabControl *control)
{
  if (m_layers.GetCount() == 0)
    return false;

  wxTabLayer *firstLayer = (wxTabLayer *)m_layers.GetFirst()->GetData();

  // Find what column this tab is at, so we can swap with the one at the bottom.
  // If we're on the bottom layer, then no need to swap.
  if (!firstLayer->Member(control))
  {
    // Do a swap
    int col = 0;
    wxList::compatibility_iterator thisNode = FindTabNodeAndColumn(control, &col);
    if (!thisNode)
      return false;
    wxList::compatibility_iterator otherNode = firstLayer->Item(col);
    if (!otherNode)
      return false;

    // If this is already in the bottom layer, return now
    if (otherNode == thisNode)
      return true;

    wxTabControl *otherTab = (wxTabControl *)otherNode->GetData();

    // We now have pointers to the tab to be changed to,
    // and the tab on the first layer. Swap tab structures and
    // position details.

    int thisX = control->GetX();
    int thisY = control->GetY();
    int thisColPos = control->GetColPosition();
    int otherX = otherTab->GetX();
    int otherY = otherTab->GetY();
    int otherColPos = otherTab->GetColPosition();

    control->SetPosition(otherX, otherY);
    control->SetColPosition(otherColPos);
    otherTab->SetPosition(thisX, thisY);
    otherTab->SetColPosition(thisColPos);

    // Swap the data for the nodes
    thisNode->SetData(otherTab);
    otherNode->SetData(control);
  }
  return true;
}

// Called when a tab is activated
void wxTabView::OnTabActivate(int /*activateId*/, int /*deactivateId*/)
{
}

void wxTabView::SetHighlightColour(const wxColour& col)
{
  m_highlightColour = col;
  m_highlightPen = wxThePenList->FindOrCreatePen(col);
}

void wxTabView::SetShadowColour(const wxColour& col)
{
  m_shadowColour = col;
  m_shadowPen = wxThePenList->FindOrCreatePen(col);
}

void wxTabView::SetBackgroundColour(const wxColour& col)
{
  m_backgroundColour = col;
  m_backgroundPen = wxThePenList->FindOrCreatePen(col);
  m_backgroundBrush = wxTheBrushList->FindOrCreateBrush(col);
}

// this may be called with sel = zero (which doesn't match any page)
// when deleting a page
// so return the first tab...

void wxTabView::SetTabSelection(int sel, bool activateTool)
{
  if ( sel==m_tabSelection )
    return;

  int oldSel = m_tabSelection;
  wxTabControl *control = FindTabControlForId(sel);
  if (sel == 0) sel=control->GetId();
  wxTabControl *oldControl = FindTabControlForId(m_tabSelection);

  if (!OnTabPreActivate(sel, oldSel))
    return;

  if (control)
    control->SetSelected((sel != -1)); // TODO ??
  else if (sel != -1)
  {
    wxFAIL_MSG(_("Could not find tab for id"));
    return;
  }

  if (oldControl)
    oldControl->SetSelected(false);

  m_tabSelection = sel;

  if (control)
    MoveSelectionTab(control);

  if (activateTool)
    OnTabActivate(sel, oldSel);
}

// Find tab control for id
// this may be called with zero (which doesn't match any page)
// so return the first control...
wxTabControl *wxTabView::FindTabControlForId(int id) const
{
  wxTabLayerList::compatibility_iterator node1 = m_layers.GetFirst();
  while (node1)
  {
    wxTabLayer *layer = (wxTabLayer *)node1->GetData();
    wxList::compatibility_iterator node2 = layer->GetFirst();
    while (node2)
    {
      wxTabControl *control = (wxTabControl *)node2->GetData();
      if (control->GetId() == id || id == 0)
        return control;
      node2 = node2->GetNext();
    }
    node1 = node1->GetNext();
  }
  return nullptr;
}

// Find tab control for layer, position (starting from zero)
wxTabControl *wxTabView::FindTabControlForPosition(int layer, int position) const
{
  wxTabLayerList::compatibility_iterator node1 = m_layers.Item(layer);
  if (!node1)
    return nullptr;
  wxTabLayer *tabLayer = (wxTabLayer *)node1->GetData();
  wxList::compatibility_iterator node2 = tabLayer->Item(position);
  if (!node2)
    return nullptr;
  return (wxTabControl *)node2->GetData();
}

// Find the node and the column at which this control is positioned.
wxList::compatibility_iterator wxTabView::FindTabNodeAndColumn(wxTabControl *control, int *col) const
{
  wxTabLayerList::compatibility_iterator node1 = m_layers.GetFirst();
  while (node1)
  {
    wxTabLayer *layer = (wxTabLayer *)node1->GetData();
    int c = 0;
    wxList::compatibility_iterator node2 = layer->GetFirst();
    while (node2)
    {
      wxTabControl *cnt = (wxTabControl *)node2->GetData();
      if (cnt == control)
      {
        *col = c;
        return node2;
      }
      node2 = node2->GetNext();
      c ++;
    }
    node1 = node1->GetNext();
  }
  return wxList::compatibility_iterator();
}

int wxTabView::CalculateTabWidth(int noTabs, bool adjustView)
{
  m_tabWidth = (int)((m_tabViewRect.width - ((noTabs - 1)*GetHorizontalTabSpacing()))/noTabs);
  if (adjustView)
  {
    m_tabViewRect.width = noTabs*m_tabWidth + ((noTabs-1)*GetHorizontalTabSpacing());
  }
  return m_tabWidth;
}

/*
 * wxTabbedDialog
 */

wxIMPLEMENT_CLASS(wxTabbedDialog, wxDialog);

wxBEGIN_EVENT_TABLE(wxTabbedDialog, wxDialog)
    EVT_CLOSE(wxTabbedDialog::OnCloseWindow)
    EVT_MOUSE_EVENTS(wxTabbedDialog::OnMouseEvent)
    EVT_PAINT(wxTabbedDialog::OnPaint)
wxEND_EVENT_TABLE()

wxTabbedDialog::wxTabbedDialog(wxWindow *parent, wxWindowID id,
    const wxString& title,
    const wxPoint& pos, const wxSize& size,
    long windowStyle, const wxString& name):
   wxDialog(parent, id, title, pos, size, windowStyle, name)
{
  m_tabView = nullptr;
}

wxTabbedDialog::~wxTabbedDialog(void)
{
  if (m_tabView)
    delete m_tabView;
}

void wxTabbedDialog::OnCloseWindow(wxCloseEvent& WXUNUSED(event) )
{
  Destroy();
}

void wxTabbedDialog::OnMouseEvent(wxMouseEvent& event )
{
  if (m_tabView)
    m_tabView->OnEvent(event);
}

void wxTabbedDialog::OnPaint(wxPaintEvent& WXUNUSED(event) )
{
    wxPaintDC dc(this);
    if (m_tabView)
        m_tabView->Draw(dc);
}

/*
 * wxTabbedPanel
 */

wxIMPLEMENT_CLASS(wxTabbedPanel, wxPanel);

wxBEGIN_EVENT_TABLE(wxTabbedPanel, wxPanel)
    EVT_MOUSE_EVENTS(wxTabbedPanel::OnMouseEvent)
    EVT_PAINT(wxTabbedPanel::OnPaint)
wxEND_EVENT_TABLE()

wxTabbedPanel::wxTabbedPanel(wxWindow *parent, wxWindowID id, const wxPoint& pos,
   const wxSize& size, long windowStyle, const wxString& name):
   wxPanel(parent, id, pos, size, windowStyle, name)
{
  m_tabView = nullptr;
}

wxTabbedPanel::~wxTabbedPanel(void)
{
  delete m_tabView;
}

void wxTabbedPanel::OnMouseEvent(wxMouseEvent& event)
{
  if (m_tabView)
    m_tabView->OnEvent(event);
}

void wxTabbedPanel::OnPaint(wxPaintEvent& WXUNUSED(event) )
{
    wxPaintDC dc(this);
    if (m_tabView)
        m_tabView->Draw(dc);
}

/*
 * wxPanelTabView
 */

wxIMPLEMENT_CLASS(wxPanelTabView, wxTabView);

wxPanelTabView::wxPanelTabView(wxPanel *pan, long style)
    : wxTabView(style)
{
  m_panel = pan;
  m_currentWindow = nullptr;

  if (m_panel->IsKindOf(wxCLASSINFO(wxTabbedDialog)))
    ((wxTabbedDialog *)m_panel)->SetTabView(this);
  else if (m_panel->IsKindOf(wxCLASSINFO(wxTabbedPanel)))
    ((wxTabbedPanel *)m_panel)->SetTabView(this);

  SetWindow(m_panel);
}

wxPanelTabView::~wxPanelTabView(void)
{
  ClearWindows(true);
}

// Called when a tab is activated
void wxPanelTabView::OnTabActivate(int activateId, int deactivateId)
{
  if (!m_panel)
    return;

  wxWindow *oldWindow = ((deactivateId == -1) ? 0 : GetTabWindow(deactivateId));
  wxWindow *newWindow = GetTabWindow(activateId);

  if (oldWindow)
    oldWindow->Show(false);
  if (newWindow)
    newWindow->Show(true);

  m_panel->Refresh();
}


void wxPanelTabView::AddTabWindow(int id, wxWindow *window)
{
  wxASSERT(m_tabWindows.find(id) == m_tabWindows.end());
  m_tabWindows[id] = window;
  window->Show(false);
}

wxWindow *wxPanelTabView::GetTabWindow(int id) const
{
  wxIntToWindowHashMap::const_iterator it = m_tabWindows.find(id);
  return it == m_tabWindows.end() ? nullptr : it->second;
}

void wxPanelTabView::ClearWindows(bool deleteWindows)
{
  if (deleteWindows)
    WX_CLEAR_HASH_MAP(wxIntToWindowHashMap, m_tabWindows);
  m_tabWindows.clear();
}

void wxPanelTabView::ShowWindowForTab(int id)
{
  wxWindow *newWindow = GetTabWindow(id);
  if (newWindow == m_currentWindow)
    return;
  if (m_currentWindow)
    m_currentWindow->Show(false);
  newWindow->Show(true);
  newWindow->Refresh();
}

