--[[
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]: UpdateDriver_USBorMixed.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 (System.Is64BitOS()) 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	

if bDebugOn then
	dpinstQuietParam = dpinstQuietParam.."/c ";
end	

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

function UnstageUSBLANDriver()
	if (File.DoesExist(pathDpinstFolder.."\\USB\\dxp01usbdrv.inf")) then
		fDebug("  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 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("  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("  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 RestartSpoolerService()
	fDebug("  Making sure Spooler is running...");
	Application.RunScriptFile("AutoPlay\\Scripts\\IsSpoolerRunning.lua");
	if bContinueInstall then
		return true;
	else
		return false;
	end		
end

function UpdatePrinterDriver() 
	fDebug("  Calling rundll32 to update the printer driver...");
	local strInstalledPrinterName = "XPS Card Printer"; -- Name of our driver.  Do Not Change!
	local nRtnCode = Shell.Execute(_SystemFolder.."\\rundll32.exe", "open", _SystemFolder.."\\printui.dll,PrintUIEntry /ia /m\""..strInstalledPrinterName.."\" /f\""..pathDpinstFolder.."\\network\\dxp01drv.inf\"", "", SW_SHOWNORMAL, true);
	local nLastError = Application.GetLastError();
	fDebug("  INFO: rundll32 of printui dll return code: "..nRtnCode.."  GetLastError value is: "..nLastError);	
	if (nLastError ~= 0) then
		bContinueInstall = false;
		fDebug("ERROR: Driver not updated. Call to PrintUIEntry of printui dll failed with error: "..LastError..": ".._tblErrorMessages[LastError].."\r\n");
		return false;
	end
	return true;	
end	

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

function InstallDriverUninstallUtility()
	fDebug("  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("  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
========================================================
]]--

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

if bContinueInstall then
	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

if bContinueInstall then
	if not(DeleteDriverBUDFile()) then
		bContinueInstall = false;
		fDebug("  FAILED: Driver BUD file could not be deleted.");
	end
end

if bContinueInstall then
	if not(CopyDriverFilesToCommonAppFolders()) then
		bContinueInstall = false;
		fDebug("  FAILED: Driver files did not copy to CommonAppFolder.");
	end
end

if bContinueInstall then
	if not(CopyCoinstallerDLLToSystemFolders()) then
		bContinueInstall = false;
		fDebug("  FAILED: Coinstaller file did not copy to System folders.");
	end
end

if not(RestartSpoolerService()) then
	bContinueInstall = false;
	fDebug("  FAILED: Spooler could not be started.");
end

if bContinueInstall then 
	if not(UpdatePrinterDriver()) then
		bContinueInstall = false;
		fDebug("  FAILED: Printer driver was not updated.");
	end
end

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


-- Update the driver uninstall utility. Continue even if this fails.
if bContinueInstall then
	InstallDriverUninstallUtility()
end

-- Restart USB Watcher service. Continue even if this fails.
if (bUSBLANDriverInstalled) then
	RestartUSBEventWatcherService()
end

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