#include "filezilla.h"
#include "splitter.h"

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

CSplitterWindowEx::CSplitterWindowEx()
{
}

CSplitterWindowEx::CSplitterWindowEx(wxWindow* parent, wxWindowID id, wxPoint const& point, wxSize const& size, long style, wxString const& name)
	: wxSplitterWindow(parent, id, point, size, style, name)
{
}

bool CSplitterWindowEx::Create(wxWindow* parent, wxWindowID id, wxPoint const& point, wxSize const& size, long style, wxString const& name)
{
	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;

	auto winTop = dynamic_cast<wxTopLevelWindow*>(parent);
	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 const old_size = (m_splitMode == wxSPLIT_VERTICAL) ? m_lastSize.x : m_lastSize.y;
		if (old_size != 0 ) {
			if (m_sashGravity == 0.5) {
				newPosition = static_cast<int>(size * m_relative_sash_position);
			}
			else if (m_sashGravity == 1.0) {
				int delta = 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)
{
	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 = static_cast<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 = static_cast<double>(sash_position) / size;
}

bool CSplitterWindowEx::Unsplit(wxWindow* toRemove)
{
	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();
}
