#include <common/process.hpp>
#include <common/profile.hpp>
#include <common/pathfnd.hpp>
#include <common/version.hpp>

Process::~Process()
{
  closeHandle();
}

#if defined(WIN32)
Process::Process(void)
: mIsOkay(FALSE), mDisposition(None)
{
	::GetStartupInfo(&mStartupInfo);
}

Process::Process(HANDLE hProcess)
: mIsOkay(FALSE), mDisposition(AssumeHandle)
{
  *this=hProcess;
}

Process &Process::operator=(HANDLE hProcess)
{
  closeHandle();
	::GetStartupInfo(&mStartupInfo);
	mDisposition=AssumeHandle;
	if(!hProcess)return *this;
	setProcess(hProcess);
	mIsOkay=TRUE;
	return *this;
}

BOOL Process::openProcess(DWORD processID,AccessFlags accessFlags,BOOL inheritHandles)
{
  closeHandle();
	mDisposition=FreeHandle;
	setProcessID(processID);
	setProcess(::OpenProcess((DWORD)accessFlags,inheritHandles,processID));
	mIsOkay=getProcess()?TRUE:FALSE;
	return isOkay();
}

BOOL Process::createProcess(String moduleNameString,bool modNameIsFirstArg,String currentDirectory,String cmdLineString)
{
  return createProcess(moduleNameString,cmdLineString,modNameIsFirstArg,CreationFlags(),SW_SHOWNORMAL,false,currentDirectory);
}

BOOL Process::createProcess(String moduleNameString,String cmdLineString,bool modNameIsFirstArg,const CreationFlags &creationFlags,WORD showWindow,BOOL inheritHandles,String currentDirectory)
{
	PathFind pathFind;
	Profile profile;
	String pureFileName;
	String pathFileNameString;
	String commandLineString;
	String startupDirString(".");
	String blankString(" ");

	if(isOkay())terminateProcess();
	if(moduleNameString.isNull())return FALSE;
	pureFileName=moduleNameString;
	if(pathFind.findFile(pureFileName,pathFileNameString))
	{
		moduleNameString=pathFileNameString;
		if(modNameIsFirstArg)commandLineString+=pathFileNameString+blankString;
	}
	else if(modNameIsFirstArg)commandLineString=moduleNameString+blankString;
	commandLineString+=cmdLineString;
	if(SW_SHOWNORMAL!=showWindow){mStartupInfo.dwFlags=STARTF_USESHOWWINDOW;mStartupInfo.wShowWindow=showWindow;}
	mIsOkay=(WORD)::CreateProcess(moduleNameString,commandLineString,0,0,inheritHandles,(DWORD)creationFlags,0,currentDirectory.isNull()?0:currentDirectory.str(),&mStartupInfo,(PROCESS_INFORMATION*)((PureProcess*)this));
	if(!mIsOkay)return FALSE;
	mDisposition=FreeHandle;
	return mIsOkay;
}

void Process::waitForInputIdle(void)
{
	if(!mIsOkay)return;
	::WaitForInputIdle(getProcess(),INFINITE);
}
#else

inline
Process::Process(void)
: mIsOkay(FALSE), mDisposition(None)
{
}

inline
Process::Process(HANDLE hProcess)
: mIsOkay(FALSE), mDisposition(AssumeHandle)
{
  if(!hProcess)return;
	setProcess(hProcess);
  mIsOkay=TRUE;
}

BOOL Process::createProcess(String moduleNameString,String cmdLineString,WORD showWindow)
{
	PathFind pathFind;
  Profile profile;
  String pureFileName;
	String pathFileNameString;
	String commandLineString;
	String startupDirString(".");
	String blankString(" ");

  if(isOkay())terminateProcess();
	if(moduleNameString.isNull())return FALSE;
  pureFileName=moduleNameString;
  profile.makeFileName(pureFileName);
	if(pathFind.findFile(pureFileName,pathFileNameString))
	{
    moduleNameString=pathFileNameString;
		startupDirString=pathFileNameString;
    commandLineString=pathFileNameString;
		profile.makeDirectoryName(startupDirString);
		commandLineString+=blankString;
	}
	else commandLineString=moduleNameString;
	commandLineString+=blankString;
  commandLineString+=cmdLineString;
	mIsOkay=(::WinExec(commandLineString,showWindow)<32?FALSE:TRUE);
	mDisposition=FreeHandle;
  return mIsOkay;
}

void Process::waitForInputIdle(void)
{
  return;
}
#endif

void Process::closeHandle(void)
{
  if(FreeHandle==mDisposition)
	{
	  if(getThread()){::CloseHandle(getThread());setThread(0);}
	  if(getProcess()){::CloseHandle(getProcess());setProcess(0);}
	}
	mDisposition=None;
}

BOOL Process::terminateProcess(BOOL waitForProcess)
{
	WORD returnCode(FALSE);

#if !defined(__FLAT__)
	return returnCode;
#else
	if(!isOkay())return returnCode;
	if(!getProcess())return returnCode;
  returnCode=::TerminateProcess(getProcess(),0L);
	if(!returnCode)return FALSE;
	if(waitForProcess)::WaitForSingleObject(getProcess(),INFINITE);
	closeHandle();
	mIsOkay=FALSE;
	return returnCode;
#endif
}

BOOL Process::safeTerminateProcess(BOOL waitForProcess)
{
  HINSTANCE hInstanceKernel(::GetModuleHandle("KERNEL32"));
	FARPROC pfnExitProcess((FARPROC)::GetProcAddress(hInstanceKernel,"ExitProcess"));
	HANDLE hRemoteThread(INVALID_HANDLE_VALUE);
	DWORD dwThreadId(0);
	DWORD dwExitCode(0);
	UINT uExitCode(0);
	WinVersionInfo versionInfo;

	if(!versionInfo.isWinNT())return terminateProcess(waitForProcess);
  if(!hInstanceKernel||!pfnExitProcess)return FALSE;
	if(!::GetExitCodeProcess(getProcess(),&dwExitCode))return FALSE;
	if(STILL_ACTIVE!=dwExitCode)return TRUE;
	hRemoteThread=::CreateRemoteThread(getProcess(),NULL,0,(LPTHREAD_START_ROUTINE)pfnExitProcess,(PVOID)&uExitCode,0,&dwThreadId);
  if(!hRemoteThread)return terminateProcess(waitForProcess);
	if(waitForProcess)::WaitForSingleObject(getProcess(),INFINITE);
	::CloseHandle(hRemoteThread);
	return TRUE;
}


