#include <filezilla.h>
#include "listingcomparison.h"
#include "filter.h"
#include "Options.h"
#include "state.h"

CComparableListing::CComparableListing(wxWindow* pParent)
{
	m_pComparisonManager = 0;
	m_pParent = pParent;

	// Init backgrounds for directory comparison
	wxColour background = m_pParent->GetBackgroundColour();
	if (background.Red() + background.Green() + background.Blue() >= 384)
	{
		// Light background
		m_comparisonBackgrounds[0].SetBackgroundColour(wxColour(255, 128, 128));
		m_comparisonBackgrounds[1].SetBackgroundColour(wxColour(255, 255, 128));
		m_comparisonBackgrounds[2].SetBackgroundColour(wxColour(128, 255, 128));
	}
	else
	{
		// Light background
		m_comparisonBackgrounds[0].SetBackgroundColour(wxColour(192, 64, 64));
		m_comparisonBackgrounds[1].SetBackgroundColour(wxColour(192, 192, 64));
		m_comparisonBackgrounds[2].SetBackgroundColour(wxColour(64, 192, 64));
	}

	m_pOther = 0;
}

bool CComparableListing::IsComparing() const
{
	if (!m_pComparisonManager)
		return false;

	return m_pComparisonManager->IsComparing();
}

void CComparableListing::ExitComparisonMode()
{
	if (!m_pComparisonManager)
		return;

	m_pComparisonManager->ExitComparisonMode();
}

void CComparableListing::RefreshComparison()
{
	if (!m_pComparisonManager)
		return;

	if (!IsComparing())
		return;

	if (!CanStartComparison(0) || !GetOther() || !GetOther()->CanStartComparison(0))
	{
		ExitComparisonMode();
		return;
	}

	m_pComparisonManager->CompareListings();
}

bool CComparisonManager::CompareListings()
{
	if (!m_pLeft || !m_pRight)
		return false;

	CFilterManager filters;
	if (filters.HasActiveFilters() && !filters.HasSameLocalAndRemoteFilters())
	{
		m_pState->NotifyHandlers(STATECHANGE_COMPARISON);
		wxMessageBox(_("Cannot compare directories, different filters for local and remote directories are enabled"), _("Directory comparison failed"), wxICON_EXCLAMATION);
		return false;
	}

	wxString error;
	if (!m_pLeft->CanStartComparison(&error))
	{
		m_pState->NotifyHandlers(STATECHANGE_COMPARISON);
		wxMessageBox(error, _("Directory comparison failed"), wxICON_EXCLAMATION);
		return false;
	}
	if (!m_pRight->CanStartComparison(&error))
	{
		m_pState->NotifyHandlers(STATECHANGE_COMPARISON);
		wxMessageBox(error, _("Directory comparison failed"), wxICON_EXCLAMATION);
		return false;
	}

	const int mode = COptions::Get()->GetOptionVal(OPTION_COMPARISONMODE);
	const int threshold = COptions::Get()->GetOptionVal(OPTION_COMPARISON_THRESHOLD);

	m_pLeft->m_pComparisonManager = this;
	m_pRight->m_pComparisonManager = this;

	m_isComparing = true;

	m_pState->NotifyHandlers(STATECHANGE_COMPARISON);

	m_pLeft->StartComparison();
	m_pRight->StartComparison();

	wxString localFile, remoteFile;
	bool localDir = false;
	bool remoteDir = false;
	wxLongLong localSize, remoteSize;
	wxDateTime localDate, remoteDate;
	bool localHasTime = false;
	bool remoteHasTime = false;

	const int dirSortMode = COptions::Get()->GetOptionVal(OPTION_FILELIST_DIRSORT);

	const bool hide_identical = COptions::Get()->GetOptionVal(OPTION_COMPARE_HIDEIDENTICAL) != 0;

	bool gotLocal = m_pLeft->GetNextFile(localFile, localDir, localSize, localDate, localHasTime);
	bool gotRemote = m_pRight->GetNextFile(remoteFile, remoteDir, remoteSize, remoteDate, remoteHasTime);

	while (gotLocal && gotRemote)
	{
		int cmp = CompareFiles(dirSortMode, localFile, remoteFile, localDir, remoteDir);
		if (!cmp)
		{
			if (!mode)
			{
				const CComparableListing::t_fileEntryFlags flag = (localDir || localSize == remoteSize) ? CComparableListing::normal : CComparableListing::different;

				if (!hide_identical || flag != CComparableListing::normal || localFile == _T(".."))
				{
					m_pLeft->CompareAddFile(flag);
					m_pRight->CompareAddFile(flag);
				}
			}
			else
			{
				if (!localDate.IsValid() || !remoteDate.IsValid())
				{
					if (!hide_identical || localDate.IsValid() || remoteDate.IsValid() || localFile == _T(".."))
					{
						const CComparableListing::t_fileEntryFlags flag = CComparableListing::normal;
						m_pLeft->CompareAddFile(flag);
						m_pRight->CompareAddFile(flag);
					}
				}
				else
				{
					CComparableListing::t_fileEntryFlags localFlag, remoteFlag;

					localDate.SetSecond(0);
					localDate.SetMillisecond(0);
					remoteDate.SetSecond(0);
					remoteDate.SetMillisecond(0);
					if (!localHasTime || !remoteHasTime)
					{
						localDate.ResetTime();
						remoteDate.ResetTime();
					}

					wxTimeSpan span = remoteDate - localDate;
					int minutes = span.GetMinutes();
					if ((minutes >= 0 && minutes <= threshold) ||
						(minutes < 0 && threshold + minutes >= 0))
					{
						localFlag = remoteFlag = CComparableListing::normal;
					}
					else if (minutes > 0)
					{
						localFlag = CComparableListing::normal;
						remoteFlag = CComparableListing::newer;
					}
					else 
					{
						localFlag = CComparableListing::newer;
						remoteFlag = CComparableListing::normal;
					}

					if (!hide_identical || localFlag != CComparableListing::normal || remoteFlag != CComparableListing::normal || localFile == _T(".."))
					{
						m_pLeft->CompareAddFile(localFlag);
						m_pRight->CompareAddFile(remoteFlag);
					}
				}
			}
			gotLocal = m_pLeft->GetNextFile(localFile, localDir, localSize, localDate, localHasTime);
			gotRemote = m_pRight->GetNextFile(remoteFile, remoteDir, remoteSize, remoteDate, remoteHasTime);
			continue;
		}

		if (cmp == -1)
		{
			m_pLeft->CompareAddFile(CComparableListing::lonely);
			m_pRight->CompareAddFile(CComparableListing::fill);
			gotLocal = m_pLeft->GetNextFile(localFile, localDir, localSize, localDate, localHasTime);
		}
		else
		{
			m_pLeft->CompareAddFile(CComparableListing::fill);
			m_pRight->CompareAddFile(CComparableListing::lonely);
			gotRemote = m_pRight->GetNextFile(remoteFile, remoteDir, remoteSize, remoteDate, remoteHasTime);
		}
	}
	while (gotLocal)
	{
		m_pLeft->CompareAddFile(CComparableListing::lonely);
		m_pRight->CompareAddFile(CComparableListing::fill);
		gotLocal = m_pLeft->GetNextFile(localFile, localDir, localSize, localDate, localHasTime);
	}
	while (gotRemote)
	{
		m_pLeft->CompareAddFile(CComparableListing::fill);
		m_pRight->CompareAddFile(CComparableListing::lonely);
		gotRemote = m_pRight->GetNextFile(remoteFile, remoteDir, remoteSize, remoteDate, remoteHasTime);
	}

	m_pRight->FinishComparison();
	m_pLeft->FinishComparison();

	return true;
}

int CComparisonManager::CompareFiles(const int dirSortMode, const wxString& local, const wxString& remote, bool localDir, bool remoteDir)
{
	switch (dirSortMode)
	{
	default:
		if (localDir)
		{
			if (!remoteDir)
				return -1;
		}
		else if (remoteDir)
			return 1;
		break;
	case 2:
		// Inline
		break;
	}

#ifdef __WXMSW__
	return local.CmpNoCase(remote);
#else
	return local.Cmp(remote);
#endif

	return 0;
}

CComparisonManager::CComparisonManager(CState* pState)
	: m_pState(pState), m_pLeft(0), m_pRight(0)
{
	m_isComparing = false;
}

void CComparisonManager::SetListings(CComparableListing* pLeft, CComparableListing* pRight)
{
	wxASSERT((pLeft && pRight) || (!pLeft && !pRight));

	if (IsComparing())
		ExitComparisonMode();

	if (m_pLeft)
		m_pLeft->SetOther(0);
	if (m_pRight)
		m_pRight->SetOther(0);

	m_pLeft = pLeft;
	m_pRight = pRight;

	if (m_pLeft)
		m_pLeft->SetOther(m_pRight);
	if (m_pRight)
		m_pRight->SetOther(m_pLeft);
}

void CComparisonManager::ExitComparisonMode()
{
	if (!IsComparing())
		return;

	m_isComparing = false;
	if (m_pLeft)
		m_pLeft->OnExitComparisonMode();
	if (m_pRight)
		m_pRight->OnExitComparisonMode();

	m_pState->NotifyHandlers(STATECHANGE_COMPARISON);
}
