#include "stdafx.h"
#include "BoxMonitor.h"
#include "../MiscHelpers/Common/Common.h"

CBoxMonitor::CBoxMonitor() 
{ 
	m_bTerminate = false;
}

CBoxMonitor::~CBoxMonitor() 
{
	Stop();
}

void CBoxMonitor::Notify(const std::wstring& strDirectory)
{
	m_Mutex.lock();
	m_Boxes[QString::fromStdWString(strDirectory)].Changed = true;
	m_Mutex.unlock();
}

quint64 CBoxMonitor::CounDirSize(const QString& Directory, SBox* Box) 
{
	quint64 TotalSize = 0;
	if (m_bTerminate || Box->pBox.isNull())
		return TotalSize;

	QDir Dir(Directory);
	foreach(const QFileInfo & Info, Dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot))
	{
		if (Info.isDir())
			TotalSize += CounDirSize(Info.filePath(), Box);
		else if (Info.isShortcut())
		{
			QFile File(Info.filePath());
			if (File.open(QFile::ReadOnly))
				TotalSize += File.size();
			File.close();
		}
		else
			TotalSize += QFile(Info.filePath()).size();
	}

	return TotalSize;
}

void CBoxMonitor::run()
{
	while (!m_bTerminate) 
	{
		Sleep(100);

		m_Mutex.lock();
		QList<QString> Keys = m_Boxes.keys();
		m_Mutex.unlock();

		quint64 CurTick = GetCurTick();

		foreach(const QString& Key, Keys) {

			m_Mutex.lock();
			SBox* Box = &m_Boxes[Key];
			m_Mutex.unlock();

			quint64 MinScanInterval = Box->ScanDuration * 100;
			if (MinScanInterval < 30 * 1000)
				MinScanInterval = 30 * 1000;
			if (MinScanInterval > 30 * 60 * 1000)
				MinScanInterval = 30 * 60 * 1000;

			if ((Box->Changed && (!Box->IsWatched || Box->LastScan == 0 || (CurTick - Box->LastScan) > MinScanInterval)) || Box->ForceUpdate) {

				qDebug() << "Rescanning:" << Key << "(" + QDateTime::currentDateTime().toString() + ")";

				quint64 ScanStart = GetCurTick();

				Box->ScanDuration = -1;

				Box->TotalSize = CounDirSize(Key, Box);

				Box->ScanDuration = GetCurTick() - ScanStart;
				Box->LastScan = GetCurTick();

				QMetaObject::invokeMethod(this, "UpdateBox", Qt::QueuedConnection,
					//Q_RETURN_ARG(int, retVal),
					Q_ARG(QString, Key)
				);

				Box->Changed = false;
				Box->ForceUpdate = false;
			}

			m_Mutex.lock();
			if (Box->pBox.isNull())
				m_Boxes.remove(Key);
			m_Mutex.unlock();
		}
	}
}

void CBoxMonitor::UpdateBox(const QString& Path)
{
	// Note: this private function runs in the main thread

	m_Mutex.lock();
	SBox Box = m_Boxes.value(Path);
	m_Mutex.unlock();

	if (Box.pBox)
		Box.pBox->SetSize(Box.TotalSize);
}

void CBoxMonitor::WatchBox(CSandBoxPlus* pBox)
{
	QMutexLocker Lock(&m_Mutex);
	if (!isRunning()) start();

	SBox& Box = m_Boxes[pBox->GetFileRoot()];
	Box.pBox = pBox;

	Box.IsWatched = true;
	AddDirectory(pBox->GetFileRoot().toStdWString().c_str(), true, FILE_NOTIFY_CHANGE_SIZE);
}

void CBoxMonitor::ScanBox(CSandBoxPlus* pBox)
{
	QMutexLocker Lock(&m_Mutex);
	if (!isRunning()) start();

	SBox& Box = m_Boxes[pBox->GetFileRoot()];
	Box.pBox = pBox;

	Box.ForceUpdate = true;
}

void CBoxMonitor::CloseBox(CSandBoxPlus* pBox)
{
	QMutexLocker Lock(&m_Mutex);
	if (!isRunning()) return;

	SBox& Box = m_Boxes[pBox->GetFileRoot()];

	if(Box.IsWatched)
		DetachDirectory(pBox->GetFileRoot().toStdWString().c_str());
	Box.IsWatched = false;

	//Box.Changed = true;
}

void CBoxMonitor::RemoveBox(CSandBoxPlus* pBox)
{
	QMutexLocker Lock(&m_Mutex);
	if (!isRunning()) return;

	auto I = m_Boxes.find(pBox->GetFileRoot());
	if (I == m_Boxes.end())
		return;

	if(I->IsWatched)
		DetachDirectory(pBox->GetFileRoot().toStdWString().c_str());
	I->pBox.clear();
}

bool CBoxMonitor::IsScanPending(const CSandBoxPlus* pBox)
{
	QMutexLocker Lock(&m_Mutex);
	if (!isRunning()) false;

	auto I = m_Boxes.find(pBox->GetFileRoot());
	if (I == m_Boxes.end())
		return false;

	return (I->Changed && !I->IsWatched) || I->ForceUpdate || I->ScanDuration == -1;
}

void CBoxMonitor::Stop()
{
	if (!isRunning()) return;

	m_bTerminate = true;

	if (!wait(10 * 1000)) {
		terminate();
		qDebug() << "Failed to stop monitor thread, terminating!!!";
	}

	QMutexLocker Lock(&m_Mutex);

	while (!m_Boxes.isEmpty()) {
		SBox Box = m_Boxes.take(m_Boxes.firstKey());
		if(Box.IsWatched && Box.pBox)
			DetachDirectory(Box.pBox->GetFileRoot().toStdWString().c_str());
	}

	m_bTerminate = false;
}
