#include <filezilla.h>
#include "splitter.h"

IMPLEMENT_CLASS(CSplitterWindowEx, wxSplitterWindow)

BEGIN_EVENT_TABLE(CSplitterWindowEx, wxSplitterWindow)
EVT_SIZE(CSplitterWindowEx::OnSize)
END_EVENT_TABLE()

CSplitterWindowEx::CSplitterWindowEx()
{
	m_relative_sash_position = 0.5;
	m_soft_min_pane_size = -1;
	m_lastSashPosition = -1;
}

CSplitterWindowEx::CSplitterWindowEx(wxWindow* parent, wxWindowID id, const wxPoint& point /*=wxDefaultPosition*/, const wxSize& size /*=wxDefaultSize*/, long style /*=wxSP_3D*/, const wxString& name /*=_T("splitterWindow")*/)
	: wxSplitterWindow(parent, id, point, size, style, name)
{
	m_relative_sash_position = 0.5;
	m_soft_min_pane_size = -1;
	m_lastSashPosition = -1;
}

bool CSplitterWindowEx::Create(wxWindow* parent, wxWindowID id, const wxPoint& point /*=wxDefaultPosition*/, const wxSize& size /*=wxDefaultSize*/, long style /*=wxSP_3D*/, const wxString& name /*=_T("splitterWindow")*/)
{
	return wxSplitterWindow::Create(parent, id, point, size, style, name);
}

void CSplitterWindowEx::SetSashGravity(double gravity)
{
	// Only support these three for now
	wxASSERT(gravity == 0.0 || gravity == 0.5 || gravity == 1.0);

	wxSplitterWindow::SetSashGravity(gravity);
}

void CSplitterWindowEx::OnSize(wxSizeEvent& event)
{
	// Code copied from wxWidgets and adjusted for better gravity handling

	// only process this message if we're not iconized - otherwise iconizing
	// and restoring a window containing the splitter has a funny side effect
	// of changing the splitter position!
	wxWindow *parent = wxGetTopLevelParent(this);
	bool iconized;

	wxTopLevelWindow *winTop = wxDynamicCast(parent, wxTopLevelWindow);
	if ( winTop )
	{
		iconized = winTop->IsIconized();
	}
	else
	{
		wxFAIL_MSG(wxT("should have a top level parent!"));

		iconized = false;
	}

	if ( iconized )
	{
		m_lastSize = wxSize(0,0);

		event.Skip();

		return;
	}

	if ( m_windowTwo )
	{
		int w, h;
		GetClientSize(&w, &h);

		int size = m_splitMode == wxSPLIT_VERTICAL ? w : h;

		int newPosition = m_sashPosition;

		int old_size = m_splitMode == wxSPLIT_VERTICAL ? m_lastSize.x : m_lastSize.y;
		if ( old_size != 0 )
		{
			if (m_sashGravity == 0.5)
				newPosition = (int)(size * m_relative_sash_position);
			else if (m_sashGravity == 1.0)
			{
				int delta = (int) ( (size - old_size) );
				if ( delta != 0 )
				{
					newPosition = m_sashPosition + delta;
					if( newPosition < m_minimumPaneSize )
						newPosition = m_minimumPaneSize;
				}
			}
			else
			{
				if (newPosition > size - m_minimumPaneSize - GetSashSize())
					newPosition = size - m_minimumPaneSize - GetSashSize();
			}
		}

		if ( newPosition >= size - 5 )
			newPosition = wxMax(10, size - 40);

		newPosition = CalcSoftLimit(newPosition);

		if (newPosition != m_sashPosition)
			SetSashPositionAndNotify(newPosition);

		m_lastSize = wxSize(w,h);
	}

	SizeWindows();
}

void CSplitterWindowEx::SetMinimumPaneSize(int paneSize, int paneSize_soft /*=-1*/)
{
	wxASSERT(paneSize_soft >= paneSize || paneSize_soft == -1);

	wxSplitterWindow::SetMinimumPaneSize(paneSize);

	m_soft_min_pane_size = paneSize_soft;
}

int CSplitterWindowEx::OnSashPositionChanging(int newSashPosition)
{
	newSashPosition = AdjustSashPosition(newSashPosition);
	newSashPosition = CalcSoftLimit(newSashPosition);

	newSashPosition = wxSplitterWindow::OnSashPositionChanging(newSashPosition);

	if (newSashPosition != -1)
	{
		int w, h;
		GetClientSize(&w, &h);

		int size = m_splitMode == wxSPLIT_VERTICAL ? w : h;

		m_relative_sash_position = (double)newSashPosition / size;
	}

	return newSashPosition;
}

int CSplitterWindowEx::CalcSoftLimit(int newSashPosition)
{
	if (m_soft_min_pane_size != -1)
	{
		int w, h;
		GetClientSize(&w, &h);

		int size = m_splitMode == wxSPLIT_VERTICAL ? w : h;

		int limit = size / 2;
		if (limit > m_soft_min_pane_size)
			limit = m_soft_min_pane_size;
		if (newSashPosition < limit)
			newSashPosition = limit;
		else if (newSashPosition > size - limit - GetSashSize())
			newSashPosition = wxMax(limit, size - limit - GetSashSize());
	}

	return newSashPosition;
}

void CSplitterWindowEx::SetRelativeSashPosition(double relative_sash_position)
{
	wxASSERT(relative_sash_position >= 0 && relative_sash_position <= 1);

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

	int size = m_splitMode == wxSPLIT_VERTICAL ? w : h;

	wxSplitterWindow::SetSashPosition((int)(size * relative_sash_position));

	m_relative_sash_position = relative_sash_position;
}

void CSplitterWindowEx::SetSashPosition(int sash_position)
{
	if (!m_windowTwo)
	{
		m_lastSashPosition = sash_position;
		return;
	}

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

	int size = m_splitMode == wxSPLIT_VERTICAL ? w : h;

	if (!sash_position)
		sash_position = size / 2;
	if (sash_position < 0 && m_sashGravity == 1.0)
		sash_position = size + sash_position - GetSashSize();

	wxSplitterWindow::SetSashPosition(sash_position);

	m_relative_sash_position = (double)sash_position / size;
}

bool CSplitterWindowEx::Unsplit(wxWindow* toRemove /*=NULL*/)
{
	if (m_sashGravity == 1)
	{
		int w, h;
		GetClientSize(&w, &h);

		int size = m_splitMode == wxSPLIT_VERTICAL ? w : h;

		m_lastSashPosition = m_sashPosition + GetSashSize() - size;
	}
	else
		m_lastSashPosition = m_sashPosition;

	return wxSplitterWindow::Unsplit(toRemove);
}

void CSplitterWindowEx::PrepareSplit(wxWindow* window1, wxWindow* window2, int & sashPosition, bool horizontal)
{
	int w, h;
	GetClientSize(&w, &h);

	int size = horizontal ? h : w;

	if (sashPosition == 0) {
		if (m_sashGravity == 0.5) {
			sashPosition = (int)(size * m_relative_sash_position);
		}
		else if (m_lastSashPosition != -1) {
			if (m_lastSashPosition < 0)
				sashPosition = size + m_lastSashPosition - GetSashSize();
			else
				sashPosition = m_lastSashPosition;
		}
	}

	// Needs to be set to avoid resizing oddity:
	// Maximize window -> Unsplit -> Restore -> Split -> Resize window
	m_lastSize = wxSize(w, h);

	if (window1 && window2) {
		window1->MoveBeforeInTabOrder(window2);
	}
}

bool CSplitterWindowEx::SplitHorizontally(wxWindow* window1, wxWindow* window2, int sashPosition /*=0*/)
{
	PrepareSplit(window1, window2, sashPosition, true);
	return wxSplitterWindow::SplitHorizontally(window1, window2, sashPosition);
}

bool CSplitterWindowEx::SplitVertically(wxWindow* window1, wxWindow* window2, int sashPosition /*=0*/)
{
	PrepareSplit(window1, window2, sashPosition, false);
	return wxSplitterWindow::SplitVertically(window1, window2, sashPosition);
}

int CSplitterWindowEx::GetSashPosition() const
{
	if (m_windowTwo || m_lastSashPosition == -1)
	{
		int sashPosition = wxSplitterWindow::GetSashPosition();

		if (m_sashGravity == 1.0)
		{
			int w, h;
			GetClientSize(&w, &h);

			int size = m_splitMode == wxSPLIT_VERTICAL ? w : h;

			sashPosition = sashPosition + GetSashSize() - size;
		}

		return sashPosition;
	}

	return m_lastSashPosition;
}

void CSplitterWindowEx::Initialize(wxWindow *window)
{
	wxSplitterWindow::Initialize(window);

	SizeWindows();
}
