--[[
Update an installed driver (network or USB) with the new version.

Prerequisties
=============
Update requires that the installed driver is at least version 4.0

Globals used by script:
=======================
IsUSBDriverInstalled

Returned:
=========
bContinueInstall is a global flag that is uses to let the calling app know if the action succeeded of failed.
bScriptRanToCompletion tells calling app that script ran.
]]--

fDebug("[Script Start]: UpdateDriverv60.lua");
bContinueInstall = true;
bScriptRanToCompletion = false;
bUSBLANDriverInstalled = false;
local pathCommonAppDataFolder = Shell.GetFolder(SHF_APPLICATIONDATA_COMMON);
local pathDpinstFolder = pathCommonAppDataFolder.."\\Datacard\\XPS Card Printer";
local LastError = 0;
local dpinstExe = "";
local printerDriverUnstaged = false;
local USBLANDriverUnstaged = false;

if (Is64bit) then
	dpinstExe = pathDpinstFolder.."\\dpinst64.exe";
else
	dpinstExe = pathDpinstFolder.."\\dpinst32.exe";
end

local dpinstQuietParam = "";
if (UseDCGCert) then
	fDebug("INFO: Datacard signed. Staging will ask user to trust our install.");
	dpinstQuietParam = " ";
else
	dpinstQuietParam = " /q /sw ";
end	

--[[ 
============================================================
        FUNCTIONS
============================================================ 
]]--

function IsUSBLANDriverInstalled()  -- sets the variable bUSBLANDriverInstalled used in this script
	fDebug("  INFO: Checking if USBLAN driver is installed...");
	bScriptRanToCompletion = false;
	Application.RunScriptFile("AutoPlay\\Scripts\\IsUSBLANDriverInstalled.lua");
	if (not bScriptRanToCompletion) then
		fDebug("  ERROR: Script <IsUSBLANDriverInstalled.lua> did not run to completion.");
		ExitInstallerDueToError();
	end
	if bContinueInstall then
		return true;
	else
		return false;
	end		
end


function UnstagePrinterDriver()
	if (File.DoesExist(pathDpinstFolder.."\\network\\dxp01drv.inf")) then
		fDebug("  Calling dpinst to uninstall the printer driver...");
		local nStagingStatus = File.Run(dpinstExe, dpinstQuietParam.."/u \""..pathDpinstFolder.."\\network\\dxp01drv.inf\"", pathDpinstFolder, SW_SHOWNORMAL, true);
		local dpinstRtnAsHexCode = string.format("%X", nStagingStatus);
		local dpinstRtnAsNumber = String.ToNumber(dpinstRtnAsHexCode);
		fDebug("   INFO: dpinst return code: "..dpinstRtnAsNumber);
		if (dpinstRtnAsNumber >= 80000000) then
			fDebug("  ERROR: printer driver could not be uninstalled by dpinst.");
			return false;
		elseif (dpinstRtnAsNumber >= 40000000) and (dpinstRtnAsNumber < 80000000) then
			fDebug("  SUCCESS: printer driver uninstalled but requires restart.");
			printerDriverUnstaged = true;
			return true;
		elseif (dpinstRtnAsNumber <= 100) then
			fDebug("  SUCCESS: printer driver uninstalled.");
			printerDriverUnstaged = true;
			return true;
		else
			fDebug("  ERROR: unrecognized return code from dpinst. ");
			return false;
		end
	else
		fDebug("  ERROR: Printer driver INF file not found.");
		return false;
	end
end


function UnstageUSBLANDriver()
	if (File.DoesExist(pathDpinstFolder.."\\USB\\dxp01usbdrv.inf")) then
		fDebug("  INFO: Calling dpinst to uninstall the USBLAN driver...");
		local nStagingStatus = File.Run(dpinstExe, dpinstQuietParam.."/u \""..pathDpinstFolder.."\\USB\\dxp01usbdrv.inf\"", pathDpinstFolder, SW_SHOWNORMAL, true); 
		local dpinstRtnAsHexCode = string.format("%X", nStagingStatus);
		local dpinstRtnAsNumber = String.ToNumber(dpinstRtnAsHexCode);
		fDebug("    INFO: dpinst return code: "..dpinstRtnAsNumber);
		if (dpinstRtnAsNumber >= 80000000) then
			fDebug("  ERROR: USBLAN driver could not be uninstalled by dpinst.");
			return false;
		elseif (dpinstRtnAsNumber >= 40000000) and (dpinstRtnAsNumber < 80000000) then
			fDebug("  SUCCESS: USBLAN driver uninstall successful but requires restart.");
			USBLANDriverUnstaged = true;
			return true;
		elseif (dpinstRtnAsNumber <= 100) then
			fDebug("  SUCCESS: USBLAN driver uninstalled.");
			USBLANDriverUnstaged = true;
			return true;
		else
			fDebug("  ERROR: unrecognized return code from dpinst. ");
			return false;
		end
	else
		fDebug("  ERROR: USBLAN driver INF file not found.");
		return false;
	end
end


function StopUSBWatcherService()
	local ServiceDisplayName = "DXP01 USB Event Watcher";
	local ServiceKeyName = "DXP01USBWatcher";
	local nServiceState = Service.Query(ServiceDisplayName, ServiceKeyName);
	if (nServiceState == 0) then -- service is not present.
		fDebug("  WARNING: USB Event Watcher service not detected.");
		return true;
	elseif (nServiceState == 1) then -- service is present but stopped.
		fDebug("  WARNING: USB Event Watcher service detected but not running.");
		return true;
	elseif (nServiceState > 1) then -- service is running. Stop service.
		fDebug("  INFO: USB Event Watcher service detected and running. State returned: "..nServiceState);
		fDebug("  Stopping USB Event Watcher service... ")
		Service.Stop(ServiceDisplayName, "", 5);
		Application.Sleep(3000);
		if (Service.Query(ServiceDisplayName, ServiceKeyName) == 1) then -- check to make sure service was stopped.
			fDebug("  INFO: USB Event Watcher service stopped. ")
			return true;
		else
			fDebug("  ERROR: Request to stop USB Event Watcher service failed. State returned: "..nServiceState);
			return false;
		end	
	else
		fDebug("  ERROR: Unexpected USB Event Watcher service state. State returned: "..nServiceState);
		return false;
	end
end


function StopSpoolerService()
	local ServiceDisplayName = "Print Spooler";
	local ServiceKeyName = "Spooler";
	local nServiceState = Service.Query(ServiceDisplayName, ServiceKeyName);
	if (nServiceState == 0) then -- service is not present.
		fDebug("  WARNING: spooler service not detected.");
		return true;
	elseif (nServiceState == 1) then -- service is present but stopped.
		fDebug("  WARNING: spooler service detected but not running.");
		return true;
	elseif (nServiceState > 1) then -- service is running. Stop service.
		fDebug("  INFO: spooler service detected and running. State returned: "..nServiceState);
		fDebug("  Stopping spooler service... ")
		Service.Stop(ServiceDisplayName, "", 5);
		Application.Sleep(3000);
		if (Service.Query(ServiceDisplayName, ServiceKeyName) == 1) then -- check to make sure service was stopped.
			fDebug("  INFO: Spooler service stopped. ")
			return true;
		else
			fDebug("  ERROR: Request to stop spooler service failed. State returned: "..nServiceState);
			return false;
		end	
	else
		fDebug("  ERROR: Unexpected spooler service state. State returned: "..nServiceState);
		return false;
	end
end


function DeleteDriverFilesInCommonAppFolders() -- using the overwrite flag in CopyDriverFilesToCommonAppFolders() should make this redundant.
	if (Folder.DoesExist(pathDpinstFolder)) then
		fDebug("  INFO: Deleting contents of XPS Card Printer folder."); 
		File.Delete(pathDpinstFolder.."\\*.*", true, false, true); -- this will remove any customized INI files too!
		Application.Sleep(3000);
	else
		fDebug("  INFO: Application folder does not exist. ");
	end
end


function DeleteDriverBUDFile() -- need to do this so updated GPD file can create a new BUD file.
	local BUDFileWithPath = "";
	local BUDFileDeleted = false;
	if (System.Is64BitOS()) then
	  	DLL.CallFunction("kernel32.dll", "Wow64DisableWow64FsRedirection", "\""..Application.GetWndHandle().."\"", DLL_RETURN_TYPE_LONG, DLL_CALL_STDCALL);
	  	BUDFileWithPath = _SystemFolder.."\\spool\\drivers\\x64\\3\\dxp01drv.BUD";
	else
	  	BUDFileWithPath = _SystemFolder.."\\spool\\drivers\\W32x86\\3\\dxp01drv.BUD";
	end
	if (File.DoesExist(BUDFileWithPath)) then
		fDebug("  Deleting BUD file..."); 
		File.Delete(BUDFileWithPath);
		local lastError = Application.GetLastError();
		if (lastError ~= 0) then
			fDebug("  INFO: Attempt to delete dxp01drv.BUD failed.  Will delete on restart.");
			BUDFileDeleted = false;
		else
			fDebug("  INFO: BUD file deleted.");
			BUDFileDeleted = true;
		end
		if not BUDFileDeleted then
			File.DeleteOnReboot(BUDFileWithPath);
			local lastError = Application.GetLastError();
			if (lastError ~= 0) then
				fDebug("  ERROR: Attempt to set dxp01drv.BUD for delete on reboot failed with error: "..lastError);
				BUDFileDeleted = false;
			else
				fDebug("  INFO: BUD file marked for deletion on restart.");
				BUDFileDeleted = true;
			end
		end
	else
		fDebug("  INFO: BUD file does not exist. ");
		BUDFileDeleted = true;
	end
	if (System.Is64BitOS()) then	
		DLL.CallFunction("kernel32.dll", "Wow64RevertWow64FsRedirection", "\""..Application.GetWndHandle().."\"" , DLL_RETURN_TYPE_LONG, DLL_CALL_STDCALL);
	end	
	if BUDFileDeleted then -- need to fall through to this so the file system redirection is reset before returning.
		return true;
	else
		return false;
	end		
end


function CopyDriverFilesToCommonAppFolders()
	fDebug("  INFO: Copying files to XPS Card Printer folder.");
	bOverwriteFiles = true;
	Application.RunScriptFile("AutoPlay\\Scripts\\Copy_Driver_Files.lua");
	if bContinueInstall then
		return true;
	else
		return false;
	end		
end


function CopyCoinstallerDLLToSystemFolders()
	fDebug("  INFO: Copying dxp01coi.dll file to system32 and drivers folders.");
	local lastError = 0;
	local CopyActionSucceeded = false;
	local CoinstallerFileWithPath = "";
	if System.Is64BitOS() then
		DLL.CallFunction("kernel32.dll", "Wow64DisableWow64FsRedirection", "\""..Application.GetWndHandle().."\"", DLL_RETURN_TYPE_LONG, DLL_CALL_STDCALL);
		CoinstallerFileWithPath = pathDpinstFolder.."\\USB\\amd64\\dxp01coi.dll";
	else
		CoinstallerFileWithPath = pathDpinstFolder.."\\USB\\i386\\dxp01coi.dll";
	end	
  	File.Copy(CoinstallerFileWithPath, _SystemFolder.."\\dxp01coi.dll", false, true, true, true, nil);
  	lastError = Application.GetLastError();
	if (lastError ~= 0) then
		CopyActionSucceeded = false;
	else
		CopyActionSucceeded = true;	
	end
	File.Copy(CoinstallerFileWithPath, _SystemFolder.."\\drivers\\dxp01coi.dll", false, true, true, true, nil);
	lastError = Application.GetLastError();
	if (lastError ~= 0) then
		CopyActionSucceeded = false;
	else
		CopyActionSucceeded = true;	
	end
	if System.Is64BitOS() then
		DLL.CallFunction("kernel32.dll", "Wow64RevertWow64FsRedirection", "\""..Application.GetWndHandle().."\"" , DLL_RETURN_TYPE_LONG, DLL_CALL_STDCALL);
	end	
	if not(CopyActionSucceeded) then	
		fDebug("  ERROR: Driver file dxp01coi.dll copy to system folders failed with error: "..lastError);
		return false;
	else 
		fDebug("  INFO: Driver file dxp01coi.dll copied to system32 and drivers folders.");
		return true;
	end
end


function CopyPrinterDriverFilesToSystemFolder()
	fDebug("  INFO: Copying dxp01DPCLLib.dll and dxp01mon.dll files to system32 folder.");
	local lastError = 0;
	local CopyActionSucceeded = false;
	local PrinterFilePath = "";
	if System.Is64BitOS() then
		DLL.CallFunction("kernel32.dll", "Wow64DisableWow64FsRedirection", "\""..Application.GetWndHandle().."\"", DLL_RETURN_TYPE_LONG, DLL_CALL_STDCALL);
		PrinterFilePath = pathDpinstFolder.."\\network\\amd64";
	else
		PrinterFilePath = pathDpinstFolder.."\\network\\i386";
	end	
  	CopyActionSucceeded = File.Install(PrinterFilePath.."\\dxp01DPCLLib.dll", _SystemFolder.."\\dxp01DPCLLib.dll", FILE_INSTALL_ALWAYS, false, false);
  	if (not CopyActionSucceeded) then
	  	lastError = Application.GetLastError();
	end	

	if CopyActionSucceeded then
		CopyActionSucceeded = File.Install(PrinterFilePath.."\\dxp01mon.dll", _SystemFolder.."\\dxp01mon.dll", FILE_INSTALL_ALWAYS, false, false);
	  	if (not CopyActionSucceeded) then
		  	lastError = Application.GetLastError();
		end	
	end	
	if not CopyActionSucceeded then -- copy did not succeed, try an alternate approach to delete and move the file after the system restart.
		fDebug("  INFO: Driver file copy to system folders failed. Will delete and move at restart.");
		File.DeleteOnReboot(_SystemFolder.."\\dxp01DPCLLib.dll");
		lastError = Application.GetLastError();
		if (lastError ~= 0) then
			CopyActionSucceeded = false;
		end
		if	CopyActionSucceeded then
			File.DeleteOnReboot(_SystemFolder.."\\dxp01mon.dll");
			lastError = Application.GetLastError();
			if (lastError ~= 0) then
				CopyActionSucceeded = false;
			end
		end	
		if	CopyActionSucceeded then
			File.MoveOnReboot(PrinterFilePath.."\\dxp01DPCLLib.dll", _SystemFolder.."\\dxp01DPCLLib.dll");
			lastError = Application.GetLastError();
			if (lastError ~= 0) then
				CopyActionSucceeded = false;
			end		
		end
		if	CopyActionSucceeded then	
			File.MoveOnReboot(PrinterFilePath.."\\dxp01mon.dll", _SystemFolder.."\\dxp01mon.dll");
			lastError = Application.GetLastError();
			if (lastError ~= 0) then
				CopyActionSucceeded = false;
			else
				CopyActionSucceeded = true;
			end
		end	
	end
	if System.Is64BitOS() then
		DLL.CallFunction("kernel32.dll", "Wow64RevertWow64FsRedirection", "\""..Application.GetWndHandle().."\"" , DLL_RETURN_TYPE_LONG, DLL_CALL_STDCALL);
	end	
	if CopyActionSucceeded then	
		fDebug("  INFO: Driver files copied to system32 folder.");
		return true;	
	else 
		fDebug("  ERROR: Driver file copy to system folders failed with error: "..lastError);
		return false;	end
end


function RestartSpoolerService()
	fDebug("  INFO: Making sure Spooler is running.");
	Application.RunScriptFile("AutoPlay\\Scripts\\IsSpoolerRunning.lua");
	if bContinueInstall then
		return true;
	else
		return false;
	end		
end


function StageUSBLANDriver() 
	Application.RunScriptFile("AutoPlay\\Scripts\\Stage_USBLANDriver.lua");
	if bContinueInstall then
		return true;
	else
		return false;
	end	
end	


function StagePrinterDriver() 
	Application.RunScriptFile("AutoPlay\\Scripts\\Stage_Driver.lua");
	if bContinueInstall then
		return true;
	else
		return false;
	end	
end		


function InstallDriverUninstallUtility()
	fDebug("  INFO: Installing uninstall utility...");
	Application.RunScriptFile("AutoPlay\\Scripts\\Install_Uninstaller.lua");
	if bContinueInstall then
		return true;
	else
		fDebug("  WARNING: Uninstall utility did not install properly. Driver update will continue.");
		return false;
	end		
end

function RestartUSBEventWatcherService()
	fDebug("  INFO: Restarting USB Event Watcher service...");
	Application.RunScriptFile("AutoPlay\\Scripts\\Install_USBEventService.lua");
	if bContinueInstall then
		return true;
	else
		fDebug("  WARNING: USB Watcher service did not restart properly. Driver update will continue.");
		return false;
	end		
end

--[[
========================================================
 CALL THE FUNCTIONS
========================================================
]]--

-- STEP 1: Determine if the USBLAN driver is installed.
if not (IsUSBLANDriverInstalled()) then
	bContinueInstall = false;
	fDebug("[Script Exit]: UpdateDriverv60.lua. Update failed. Attempt to find installed USBLAN driver failed.");
end

-- STEP 2: Unstage installed driver
if bContinueInstall then
	if not(UnstagePrinterDriver()) then
		bContinueInstall = false;
		fDebug("  FAILED: Printer driver was not unstaged.");
	end
end
-- Step 2a: If USB driver, unstage USBLAN driver and stop USB Watcher service
-- NOTE: Could add test here to detect is USBLAN driver needs to be updated but will always be true 
--       for v6.0 customers so leaving out for this release.
if (bContinueInstall and bUSBLANDriverInstalled) then
	if not(UnstageUSBLANDriver()) then
		bContinueInstall = false;
		fDebug("  FAILED: USBLAN driver was not unstaged.");
	end
	if not(StopUSBWatcherService()) then
		bContinueInstall = false;
		fDebug("  FAILED: Update failed. USB Watcher service could not be stopped.");
	end
end

if bContinueInstall then
	if not(StopSpoolerService()) then
		bContinueInstall = false;
		fDebug("  FAILED: USBLAN driver was not unstaged.");
	end
end


-- Step 2b: Delete BUD file so new GPD is used
if bContinueInstall then
	if not(DeleteDriverBUDFile()) then
		bContinueInstall = false;
		fDebug("  FAILED: Driver BUD file could not be deleted.");
	end
end

-- Step 3: Copy new driver files to system
-- Step 3a: Copy installation files over existing files in CommonAppData folders
if bContinueInstall then
	if not(CopyDriverFilesToCommonAppFolders()) then
		bContinueInstall = false;
		fDebug("  FAILED: Driver files did not copy to CommonAppFolder.");
	end
end

-- Step 3b: Copy USB coinstaller DLL to System folders
if (bContinueInstall and bUSBLANDriverInstalled) then
	if not(CopyCoinstallerDLLToSystemFolders()) then
		bContinueInstall = false;
		fDebug("  FAILED: Coinstaller file did not copy to System folders.");
	end
end

-- Step 3c: Copy printer driver DLLs that do not get staged to System folder
if (bContinueInstall) then
	if not(CopyPrinterDriverFilesToSystemFolder()) then
		bContinueInstall = false;
		fDebug("  FAILED: Driver files did not copy to the System folder.");
	end
end

-- Step 4: Make sure the spooler is running
if not(RestartSpoolerService()) then
	bContinueInstall = false;
	fDebug("  FAILED: Spooler could not be started.");
end


-- Step 5: restage the drivers using the new driver files
if bContinueInstall or printerDriverUnstaged then -- if the driver was unstaged, try to restage it even if the update failed.
	if not(StagePrinterDriver()) then
		bContinueInstall = false;
		fDebug("  FAILED: Printer driver was not staged.");
	end
end

if ((bContinueInstall and bUSBLANDriverInstalled)or USBLANDriverUnstaged) then
	if not(StageUSBLANDriver()) then
		bContinueInstall = false;
		fDebug("  FAILED: USBLAN driver was not staged.");
	end
end


-- Step 6: Reinstall the driver uninstall utility. Continue even if this fails.
if bContinueInstall then
	InstallDriverUninstallUtility()
end

-- Step 7: If USB, restart USB Watcher service. Continue even if this fails.
if (bUSBLANDriverInstalled) then
	RestartUSBEventWatcherService()
end

-- END
bScriptRanToCompletion = true;
fDebug("[Script Exit]: UpdateDriverv60 \r\n");
return;