///////////////////////////////////////////////////////////////////////////////
// Simple x264 Launcher
// Copyright (C) 2004-2015 LoRd_MuldeR <MuldeR2@GMX.de>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// http://www.gnu.org/licenses/gpl-2.0.txt
///////////////////////////////////////////////////////////////////////////////

//x264 includes
#include "global.h"
#include "targetver.h"

//Windows includes
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <MMSystem.h>
#include <ShellAPI.h>
#include <Objbase.h>
#include <Psapi.h>
#include <SensAPI.h>

//C++ includes
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <time.h>

//VLD
#include <vld.h>

//Version
#define ENABLE_X264_VERSION_INCLUDE
#include "version.h"
#undef  ENABLE_X264_VERSION_INCLUDE

//MUtils includes
#include <MUtils/Global.h>

//Qt includes
#include <QApplication>
#include <QMessageBox>
#include <QDir>
#include <QUuid>
#include <QMap>
#include <QDate>
#include <QIcon>
#include <QPlastiqueStyle>
#include <QImageReader>
#include <QSharedMemory>
#include <QSysInfo>
#include <QStringList>
#include <QSystemSemaphore>
#include <QDesktopServices>
#include <QMutex>
#include <QTextCodec>
#include <QLibrary>
#include <QRegExp>
#include <QResource>
#include <QTranslator>
#include <QEventLoop>
#include <QTimer>
#include <QLibraryInfo>
#include <QEvent>
#include <QReadLocker>
#include <QWriteLocker>
#include <QProcess>

//CRT includes
#include <fstream>
#include <io.h>
#include <fcntl.h>
#include <intrin.h>
#include <process.h>

//Debug only includes
#if X264_DEBUG
#include <Psapi.h>
#endif

//Global types
typedef HRESULT (WINAPI *SHGetKnownFolderPath_t)(const GUID &rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
typedef HRESULT (WINAPI *SHGetFolderPath_t)(HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);

//Const
static const char *g_x264_months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static const char *g_x264_imageformats[] = {"png", "jpg", "gif", "ico", "svg", NULL};

//Build version
static const struct
{
	unsigned int ver_major;
	unsigned int ver_minor;
	unsigned int ver_patch;
	unsigned int ver_build;
}
g_x264_version =
{
	(VER_X264_MAJOR),
	(VER_X264_MINOR),
	(VER_X264_PATCH),
	(VER_X264_BUILD),
};

//Portable mode
static QReadWriteLock g_portableModeLock;
static bool           g_portableModeData = false;
static bool           g_portableModeInit = false;

//Data path
static QString        g_dataPathData;
static QReadWriteLock g_dataPathLock;

///////////////////////////////////////////////////////////////////////////////
// MACROS
///////////////////////////////////////////////////////////////////////////////

//String helper
#define CLEAN_OUTPUT_STRING(STR) do \
{ \
	const char CTRL_CHARS[3] = { '\r', '\n', '\t' }; \
	for(size_t i = 0; i < 3; i++) \
	{ \
		while(char *pos = strchr((STR), CTRL_CHARS[i])) *pos = char(0x20); \
	} \
} \
while(0)

//String helper
#define TRIM_LEFT(STR) do \
{ \
	const char WHITE_SPACE[4] = { char(0x20), '\r', '\n', '\t' }; \
	for(size_t i = 0; i < 4; i++) \
	{ \
		while(*(STR) == WHITE_SPACE[i]) (STR)++; \
	} \
} \
while(0)

//Check for CLI flag
static inline bool _CHECK_FLAG(const int argc, char **argv, const char *flag)
{
	for(int i = 1; i < argc; i++)
	{
		if(_stricmp(argv[i], flag) == 0) return true;
	}
	return false;
}

#define CHECK_FLAG(FLAG) _CHECK_FLAG(argc, argv, "--" FLAG)
#define X264_ZERO_MEMORY(X) SecureZeroMemory(&X, sizeof(X))

///////////////////////////////////////////////////////////////////////////////
// GLOBAL FUNCTIONS
///////////////////////////////////////////////////////////////////////////////

/*
 * Version info
 */
unsigned int x264_version_major(void)
{
	return g_x264_version.ver_major;
}

unsigned int x264_version_minor(void)
{
	return (g_x264_version.ver_minor * 10) + (g_x264_version.ver_patch % 10);
}

unsigned int x264_version_build(void)
{
	return g_x264_version.ver_build;
}

/*
 * Check for portable mode
 */
bool x264_is_portable(void)
{
	QReadLocker readLock(&g_portableModeLock);

	if(g_portableModeInit)
	{
		return g_portableModeData;
	}
	
	readLock.unlock();
	QWriteLocker writeLock(&g_portableModeLock);

	if(!g_portableModeInit)
	{
		if(VER_X264_PORTABLE_EDITION)
		{
			qWarning("Simple x264 Launcher portable edition!\n");
			g_portableModeData = true;
		}
		else
		{
			QString baseName = QFileInfo(QApplication::applicationFilePath()).completeBaseName();
			int idx1 = baseName.indexOf("x264", 0, Qt::CaseInsensitive);
			int idx2 = baseName.lastIndexOf("portable", -1, Qt::CaseInsensitive);
			g_portableModeData = (idx1 >= 0) && (idx2 >= 0) && (idx1 < idx2);
		}
		g_portableModeInit = true;
	}
	
	return g_portableModeData;
}

/*
 * Get data path (i.e. path to store config files)
 */
const QString &x264_data_path(void)
{
	QReadLocker readLock(&g_dataPathLock);

	if(!g_dataPathData.isEmpty())
	{
		return g_dataPathData;
	}
	
	readLock.unlock();
	QWriteLocker writeLock(&g_dataPathLock);
	
	if(g_dataPathData.isEmpty())
	{
		g_dataPathData = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
		if(g_dataPathData.isEmpty() || x264_is_portable())
		{
			g_dataPathData = QApplication::applicationDirPath();
		}
		if(!QDir(g_dataPathData).mkpath("."))
		{
			qWarning("Data directory could not be created:\n%s\n", g_dataPathData.toUtf8().constData());
			g_dataPathData = QDir::currentPath();
		}
	}
	
	return g_dataPathData;
}

/*
 * Is pre-release version?
 */
bool x264_is_prerelease(void)
{
	return (VER_X264_PRE_RELEASE);
}

/*
 * Convert path to short/ANSI path
 */
QString x264_path2ansi(const QString &longPath, bool makeLowercase)
{
	QString shortPath = longPath;
	
	const QString longPathNative = QDir::toNativeSeparators(longPath);
	DWORD buffSize = GetShortPathNameW(MUTILS_WCHR(longPathNative), NULL, NULL);
	
	if(buffSize > 0)
	{
		wchar_t *buffer = (wchar_t*) _malloca(sizeof(wchar_t) * buffSize);
		DWORD result = GetShortPathNameW(MUTILS_WCHR(longPathNative), buffer, buffSize);

		if((result > 0) && (result < buffSize))
		{
			shortPath = QDir::fromNativeSeparators(QString::fromUtf16(reinterpret_cast<const unsigned short*>(buffer), result));

			if(makeLowercase)
			{
				QFileInfo info(shortPath);
				shortPath = QString("%1/%2").arg(info.absolutePath(), info.fileName().toLower());
			}
		}

		_freea(buffer);
		buffer = NULL;
	}

	return shortPath;
}

/*
 * Read value from registry
 */
QString x264_query_reg_string(const bool bUser, const QString &path, const QString &name)
{
	QString result; HKEY hKey = NULL;
	if(RegOpenKey((bUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE), MUTILS_WCHR(path), &hKey) == ERROR_SUCCESS)
	{
		const size_t DATA_LEN = 2048; wchar_t data[DATA_LEN];
		DWORD type = REG_NONE, size = sizeof(wchar_t) * DATA_LEN;
		if(RegQueryValueEx(hKey, MUTILS_WCHR(name), NULL, &type, ((BYTE*)&data[0]), &size) == ERROR_SUCCESS)
		{
			if((type == REG_SZ) || (type == REG_EXPAND_SZ))
			{
				result = MUTILS_QSTR(&data[0]);
			}
		}
		RegCloseKey(hKey);
	}
	return result;
}

/*
 * Inform the system that it is in use, thereby preventing the system from entering sleep
 */
bool x264_set_thread_execution_state(const bool systemRequired)
{
	EXECUTION_STATE state = NULL;
	if(systemRequired)
	{
		state = SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED);
	}
	else
	{
		state = SetThreadExecutionState(ES_CONTINUOUS);
	}
	return (state != NULL);
}
