#include "eauibook.h"
#include <wx/log.h>

BEGIN_EVENT_TABLE(eAuiNotebook, wxAuiNotebook)
	EVT_MOUSEWHEEL(eAuiNotebook::OnMouseWheel)
END_EVENT_TABLE()

// This class redefined here as a workaround for not having
// access to the original wxTabFrame definition in auibook.cpp
// Doing it like this is a fragile hack, but it lets us
// avoid patching wxWidgets.
class wxTabFrame : public wxWindow
{
public:
    wxTabFrame();
    ~wxTabFrame();
    void SetTabCtrlHeight(int h)
    {
        m_tab_ctrl_height = h;
    }
    void DoSetSize(int x, int y,
                   int width, int height,
                   int WXUNUSED(sizeFlags = wxSIZE_AUTO));
    void DoGetClientSize(int* x, int* y) const;
    bool Show( bool WXUNUSED(show = true) );
    void DoSizing();
    void DoGetSize(int* x, int* y) const;
    void Update();

public:
    wxRect m_rect;
    wxRect m_tab_rect;
    wxAuiTabCtrl* m_tabs;
    int m_tab_ctrl_height;
};

// Constructor
eAuiNotebook::eAuiNotebook(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style):
	wxAuiNotebook(parent, id, pos, size, style) {}

// Convert page index to tab position
int eAuiNotebook::PageToTab(size_t page_idx) {
    wxWindow* page = m_tabs.GetWindowFromIdx(page_idx);

    wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
    const size_t pane_count = all_panes.GetCount();
	size_t offset = 0;

    for (size_t i = 0; i < pane_count; ++i)
    {
		if (all_panes.Item(i).name == wxT("dummy"))
            continue;

        wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
		
		const int page_idx = tabframe->m_tabs->GetIdxFromWindow(page);
        if (page_idx != -1)
			return offset + page_idx;

		offset += tabframe->m_tabs->GetPageCount();
	}

	wxASSERT(false); // invalid page_idx
	return -1;
}

// Convert tab position to page index
int eAuiNotebook::TabToPage(size_t tab_idx) {
	wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
    const size_t pane_count = all_panes.GetCount();
	size_t offset = 0;

    for (size_t i = 0; i < pane_count; ++i)
    {
		if (all_panes.Item(i).name == wxT("dummy"))
            continue;

        wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
		const size_t tab_count = tabframe->m_tabs->GetPageCount();

		if (offset + tab_count <= tab_idx) offset += tab_count;
		else {
			wxWindow* page =  tabframe->m_tabs->GetWindowFromIdx(tab_idx-offset);
			return m_tabs.GetIdxFromWindow(page);
		}
	}

	wxASSERT(false); // we should never reach here
	return -1;
}

wxString eAuiNotebook::SavePerspective() {
	// Build list of panes/tabs
	wxString tabs;
	wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
    const size_t pane_count = all_panes.GetCount();

    for (size_t i = 0; i < pane_count; ++i)
    {
		wxAuiPaneInfo& pane = all_panes.Item(i);
		if (pane.name == wxT("dummy"))
            continue;

        wxTabFrame* tabframe = (wxTabFrame*)pane.window;
		
		if (!tabs.empty()) tabs += wxT("|");
		tabs += pane.name;
		tabs += wxT("=");
		
		// add tab id's
		size_t page_count = tabframe->m_tabs->GetPageCount();
		for (size_t p = 0; p < page_count; ++p)
		{
			wxAuiNotebookPage& page = tabframe->m_tabs->GetPage(p);
			const size_t page_idx = m_tabs.GetIdxFromWindow(page.window);
			
			if (p) tabs += wxT(",");

			if ((int)page_idx == m_curpage) tabs += wxT("*");
			else if ((int)p == tabframe->m_tabs->GetActivePage()) tabs += wxT("+");
			tabs += wxString::Format(wxT("%u"), page_idx);
		}
	}
	tabs += wxT("@");

	// Add frame perspective
	tabs += m_mgr.SavePerspective();

	return tabs;
}

bool eAuiNotebook::LoadPerspective(const wxString& layout) {
	// Remove all tab ctrls (but still keep them in main index)
	const size_t tab_count = m_tabs.GetPageCount();
	for (size_t i = 0; i < tab_count; ++i) {
		wxWindow* wnd = m_tabs.GetWindowFromIdx(i);

		// find out which onscreen tab ctrl owns this tab
		wxAuiTabCtrl* ctrl;
		int ctrl_idx;
		if (!FindTab(wnd, &ctrl, &ctrl_idx))
			return false;

		// remove the tab from ctrl
		if (!ctrl->RemovePage(wnd))
			return false;
	}
	RemoveEmptyTabFrames();

	size_t sel_page = 0;
	
	wxString tabs = layout.BeforeFirst(wxT('@'));
	while (1)
    {
		const wxString tab_part = tabs.BeforeFirst(wxT('|'));
		
		// if the string is empty, we're done parsing
        if (tab_part.empty())
            break;

		// Get pane name
		const wxString pane_name = tab_part.BeforeFirst(wxT('='));

		// create a new tab frame
		wxTabFrame* new_tabs = new wxTabFrame;
		new_tabs->m_tabs = new wxAuiTabCtrl(this,
											m_tab_id_counter++,
											wxDefaultPosition,
											wxDefaultSize,
											wxNO_BORDER|wxWANTS_CHARS);
		new_tabs->m_tabs->SetArtProvider(m_tabs.GetArtProvider()->Clone());
		new_tabs->SetTabCtrlHeight(m_tab_ctrl_height);
		new_tabs->m_tabs->SetFlags(m_flags);
		wxAuiTabCtrl *dest_tabs = new_tabs->m_tabs;

		// create a pane info structure with the information
		// about where the pane should be added
		wxAuiPaneInfo pane_info = wxAuiPaneInfo().Name(pane_name).Bottom().CaptionVisible(false);
		m_mgr.AddPane(new_tabs, pane_info);

		// Get list of tab id's and move them to pane
		wxString tab_list = tab_part.AfterFirst(wxT('='));
		while(1) {
			wxString tab = tab_list.BeforeFirst(wxT(','));
			if (tab.empty()) break;
			tab_list = tab_list.AfterFirst(wxT(','));

			// Check if this page has an 'active' marker
			const wxChar c = tab[0];
			if (c == wxT('+') || c == wxT('*')) {
				tab = tab.Mid(1); 
			}

			const size_t tab_idx = wxAtoi(tab.c_str());
			if (tab_idx >= GetPageCount()) continue;

			// Move tab to pane
			wxAuiNotebookPage& page = m_tabs.GetPage(tab_idx);
			const size_t newpage_idx = dest_tabs->GetPageCount();
			dest_tabs->InsertPage(page.window, page, newpage_idx);

			if (c == wxT('+')) dest_tabs->SetActivePage(newpage_idx);
			else if ( c == wxT('*')) sel_page = tab_idx;
		}
		dest_tabs->DoShowHide();

		tabs = tabs.AfterFirst(wxT('|'));
	}
	
	// Load the frame perspective
	const wxString frames = layout.AfterFirst(wxT('@'));
	m_mgr.LoadPerspective(frames);

	// Force refresh of selection
	m_curpage = -1;
	SetSelection(sel_page);

	return true;
}

// This code is based largely on the following patch, but modified to work with the wrap_around parameter
// http://trac.wxwidgets.org/attachment/ticket/10848/wxAuiNotebook.patch
void eAuiNotebook::ChangeTab(bool forward, bool wrap_around) {
	if (GetPageCount() <= 1) return;

	int currentSelection = this->GetSelection();
	wxAuiTabCtrl* tabCtrl = 0; 
	int idx = -1; 

	if(!FindTab(GetPage(currentSelection), &tabCtrl, &idx)) return; 
	if(!tabCtrl || idx < 0) return;

	wxWindow* page = 0; 
	int pageCount = tabCtrl->GetPageCount(); 

	forward ? idx++ : idx--;

	if(idx < 0) {
		idx = wrap_around ? pageCount - 1 : 0;
	} else if(idx >= pageCount) {
		idx = wrap_around ? 0 : pageCount-1;
	}

	page = tabCtrl->GetPage(idx).window;
	if(page) { 
		currentSelection = GetPageIndex(page); 
		SetSelection(currentSelection); 
	} 
}

void eAuiNotebook::SelectNextTab(bool wrap_around) {
	ChangeTab(true, wrap_around);
}

void eAuiNotebook::SelectPrevTab(bool wrap_around) {
	ChangeTab(false, wrap_around);
}

void eAuiNotebook::OnMouseWheel(wxMouseEvent& event) {
	const wxCoord x = event.GetX();
	const wxCoord y = event.GetY();

	bool hit = ((0 <= y) && (y < this->m_tab_ctrl_height) &&
					(0 <= x) && (x < this->GetSize().GetWidth()));

	if (!hit) {
		event.Skip(true);
		return;
	}

	const int rotation = event.GetWheelRotation();
	wxLogDebug(_("Rotation: %d"), rotation);

	// Don't use wrap-around tab selection when mouse-wheeling
	if (rotation < 0) SelectNextTab(false);
	else if (rotation > 0) SelectPrevTab(false);
}
