// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_file_utilities_win.h"

#include "base/algorithm.h"

#include <QtCore/QString>
#include <QtCore/QDir>

#include <array>
#include <string>
#include <Shlwapi.h>
#include <shlobj.h>
#include <RestartManager.h>

namespace base::Platform {

bool ShowInFolder(const QString &filepath) {
	auto nativePath = QDir::toNativeSeparators(filepath);
	const auto path = nativePath.toStdWString();
	if (const auto pidl = ILCreateFromPathW(path.c_str())) {
		const auto result = SHOpenFolderAndSelectItems(pidl, 0, nullptr, 0);
		ILFree(pidl);
		return (result == S_OK);
	}
	const auto pathEscaped = nativePath.replace('"', QString("\"\""));
	const auto command = ("/select," + pathEscaped).toStdWString();
	const auto result = int(ShellExecute(
		0,
		0,
		L"explorer",
		command.c_str(),
		0,
		SW_SHOWNORMAL));
	return (result > 32);
}

QString FileNameFromUserString(QString name) {
	const auto kBadExtensions = { qstr(".lnk"), qstr(".scf") };
	const auto kMaskExtension = qstr(".download");
	for (const auto extension : kBadExtensions) {
		if (name.endsWith(extension, Qt::CaseInsensitive)) {
			name += kMaskExtension;
		}
	}

	static const auto BadNames = {
		qstr("CON"),
		qstr("PRN"),
		qstr("AUX"),
		qstr("NUL"),
		qstr("COM1"),
		qstr("COM2"),
		qstr("COM3"),
		qstr("COM4"),
		qstr("COM5"),
		qstr("COM6"),
		qstr("COM7"),
		qstr("COM8"),
		qstr("COM9"),
		qstr("LPT1"),
		qstr("LPT2"),
		qstr("LPT3"),
		qstr("LPT4"),
		qstr("LPT5"),
		qstr("LPT6"),
		qstr("LPT7"),
		qstr("LPT8"),
		qstr("LPT9")
	};
	for (const auto bad : BadNames) {
		if (name.startsWith(bad, Qt::CaseInsensitive)) {
			if (name.size() == bad.size() || name[bad.size()] == '.') {
				name = '_' + name;
				break;
			}
		}
	}
	return name;
}

bool DeleteDirectory(QString path) {
	if (path.endsWith('/')) {
		path.chop(1);
	}
	const auto wide = QDir::toNativeSeparators(path).toStdWString()
		+ wchar_t(0)
		+ wchar_t(0);
	SHFILEOPSTRUCT file_op = {
		NULL,
		FO_DELETE,
		wide.data(),
		L"",
		FOF_NOCONFIRMATION |
		FOF_NOERRORUI |
		FOF_SILENT,
		false,
		0,
		L""
	};
	return (SHFileOperation(&file_op) == 0);
}

void RemoveQuarantine(const QString &path) {
}

QString CurrentExecutablePath(int argc, char *argv[]) {
	auto result = std::array<WCHAR, MAX_PATH + 1>{ 0 };
	const auto count = GetModuleFileName(
		nullptr,
		result.data(),
		MAX_PATH + 1);
	if (count < MAX_PATH + 1) {
		const auto info = QFileInfo(QDir::fromNativeSeparators(
			QString::fromWCharArray(result.data(), count)));
		return info.absoluteFilePath();
	}

	// Fallback to the first command line argument.
	auto argsCount = 0;
	if (const auto args = CommandLineToArgvW(GetCommandLine(), &argsCount)) {
		auto info = QFileInfo(QDir::fromNativeSeparators(
			QString::fromWCharArray(args[0])));
		LocalFree(args);
		return info.absoluteFilePath();
	}
	return QString();
}

bool CloseProcesses(const QString &filename) {
	auto result = BOOL(FALSE);
	auto session = DWORD();
	auto sessionKey = std::wstring(CCH_RM_SESSION_KEY + 1, wchar_t(0));
	auto error = RmStartSession(&session, 0, sessionKey.data());
	if (error != ERROR_SUCCESS) {
		return false;
	}
	const auto guard = gsl::finally([&] { RmEndSession(session); });

	const auto path = QDir::toNativeSeparators(filename).toStdWString();
	auto nullterm = path.c_str();
	error = RmRegisterResources(
		session,
		1,
		&nullterm,
		0,
		nullptr,
		0,
		nullptr);
	if (error != ERROR_SUCCESS) {
		return false;
	}

	auto processInfoNeeded = UINT(0);
	auto processInfoCount = UINT(0);
	auto reason = DWORD();

	error = RmGetList(
		session,
		&processInfoNeeded,
		&processInfoCount,
		nullptr,
		&reason);
	if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) {
		return false;
	} else if (processInfoNeeded <= 0) {
		return true;
	}
	error = RmShutdown(session, RmForceShutdown, NULL);
	if (error != ERROR_SUCCESS) {
		return false;
	}
	return true;
}

} // namespace base::Platform
