Param(
	[string]$DllFile,
	[string]$ServerInstance,
	[string]$ProjectPath,
	[string]$SolutionDir,
	[string]$Configuration
)

# Please, edit the following line if you want client add-in and its dependencies to be copied 
# to client add-ins folder, and if the path is different than the one specified.
$AddInsPath = "C:\Program Files (x86)\Microsoft Dynamics NAV\100\RoleTailored Client\Add-ins"

if ([System.IO.Directory]::Exists($AddInsPath) -eq $false)
{
	Write-Host
	Write-Host
	Write-Host "-----------------------------------------------------------------------------------------------"
	Write-Host
	Write-Host "                   I M P O R T A N T   B U I L D   I N F O R M A T I O N"
	Write-Host
	Write-Host "Add-ins path could not be automatically detected. Please edit the _build\PostBuild.ps1 file"
	Write-Host "and enter the path to the Microsoft Dynamics NAV RoleTailored Client Add-Ins folder."
	Write-Host "Before you do, control add-in assembly and its dependencies won't be copied to Add-ins folder."
	Write-Host "-----------------------------------------------------------------------------------------------"
	Write-Host
	Write-Host
}

# You do not need to edit the following line - the path will be automatically detected 
# if Service Tier is installed on your computer.
$ServerAddInsPath = ""
$DeployToNst = $false

Function ImportNavManagementModule() 
{
	Param([string]$Server)

	$svcName = "MicrosoftDynamicsNavServer$" + $Server
	foreach ($mo in [System.Management.ManagementClass]::new("Win32_Service").GetInstances())
	{
		if ($mo.GetPropertyValue("Name").ToString() -eq $svcName)
		{
			$path = $mo.GetPropertyValue("PathName").ToString()
			$path = $path.Substring(1, $path.IndexOf("$") - 3)
			$path = [System.IO.Path]::Combine(
				[System.IO.Path]::GetDirectoryName($path),
				"Microsoft.Dynamics.Nav.Management.dll")
			Import-Module -FullyQualifiedName $path
			return [System.IO.Path]::Combine([System.IO.Path]::GetDirectoryName($path), "Add-ins")
		}
	}
}

Function ImportAddIn() 
{
	Param (
		[string]$AddInName,
		[string]$Token,
		[string]$Path,
		[string]$Server
	)

	$resFile = $null
	$folder = [System.IO.Path]::Combine($Path, "Resource", $AddInName)
	$zipFile = [System.IO.Path]::Combine($folder, $AddInName + ".zip")
	if ([System.IO.File]::Exists($zipFile))
	{
		Remove-Item $zipFile
	}
	if ([System.IO.Directory]::Exists($folder))
	{
		$resFile = ZipFolder -Folder $folder
	}
	Try
	{
		if ($resFile -ne $null)
		{
			Copy-Item $resFile $zipFile

			New-NAVAddIn `
				-AddInName $AddInName `
				-PublicKeyToken $Token `
				-ServerInstance $Server `
				-Category JavaScriptControlAddIn `
				-ResourceFile $zipFile `
				-ErrorAction Stop
		} 
		else 
		{
			New-NAVAddIn `
				-AddInName $AddInName `
				-PublicKeyToken $Token `
				-ServerInstance $Server `
				-Category DotNetControlAddIn `
				-ErrorAction Stop
		}
	}
	Catch [System.Exception]
	{
		if ($resFile -ne $null)
		{
			Set-NAVAddIn `
				-AddInName $AddInName `
				-PublicKeyToken $Token `
				-ServerInstance $Server `
				-Category JavaScriptControlAddIn `
				-ResourceFile $resFile
		}
		else
		{
			Set-NAVAddIn `
				-AddInName $AddInName `
				-PublicKeyToken $Token `
				-ServerInstance $Server `
				-Category DotNetControlAddIn
		}
	}
	Finally
	{
		if ($resFile -ne $null -and [System.IO.File]::Exists($resFile))
		{
			[System.IO.File]::Delete($resFile)
		}
	}
}

Function LoadAssemblies() 
{
	Param (
		[string]$Solution,
		[string]$Path
	)

	$navPath = $null
	$project = [Xml](Get-Content $Path)
	[System.Xml.XmlNamespaceManager] $nsmgr = $project.NameTable
	$nsmgr.AddNamespace("p", "http://schemas.microsoft.com/developer/msbuild/2003")
	$nodes = $project.SelectNodes("/p:Project/p:ItemGroup//p:Reference", $nsmgr)
	foreach ($node in $nodes)
	{
		$hint = $node.SelectSingleNode("p:HintPath", $nsmgr)
		if ($hint -ne $null) 
		{
			$deppath = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine([System.IO.Path]::GetDirectoryName($Path), $hint.InnerText))
			if ([System.IO.File]::Exists($deppath) -eq $false)
			{
				$deppath = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($Solution, $hint.InnerText))
			}
			$a = [System.Reflection.Assembly]::LoadFile($deppath)
		}
	}
}

Function ZipFolder()
{
	Param(
		[string]$Folder
	)

	#Add-Type -Assembly "System.IO.Compression.FileSystem"
	$dest = [System.IO.Path]::GetTempFileName()
	[System.IO.File]::Delete($dest)
	#[System.IO.Compression.ZipFile]::CreateFromDirectory($Folder, $dest)

	$zip = [Ionic.Zip.ZipFile]::new()
	$zip.AddSelectedFiles("*", $Folder, [System.String]::Empty, $true);
	$zip.Save($dest)

	return $dest
}

Function GetToken()
{
	Param(
		[string]$file
	)

	return -join ([Reflection.AssemblyName]::GetAssemblyName($file).GetPublicKeyToken() `
		| foreach { $_.ToString("X2").ToLower() })
}

Function InstallAllControlAddIns() 
{
	Param (
		[string]$Dll,
		[string]$Server,
		[string]$Project,
		[string]$Solution,
		[string]$Config
	)

	LoadAssemblies -path $Project -Solution $Solution
	$path = [System.IO.Path]::GetDirectoryName($Project)
	$token = GetToken -file $Dll

	if ([System.String]::IsNullOrWhiteSpace($token))
	{
		throw "The assembly is not signed with a strong name. Please, choose a strong name file on the Signing tab of the Project Properties."
	}

	$asm = [System.Reflection.Assembly]::LoadFile($Dll)
	$types = $asm.GetTypes() | Where { $_.GetCustomAttributes($false).Count -gt 0 }
	foreach ($type in $types)
	{
		$ctrlAddIn = $type.GetCustomAttributes($false) `
			| Where { $_.TypeId.ToString() -eq `
				"Microsoft.Dynamics.Framework.UI.Extensibility.ControlAddInExportAttribute" }
		if ($ctrlAddIn -ne $null) 
		{
			ImportAddIn `
				-AddInName $ctrlAddIn.Name `
				-Token $token `
				-Path $path `
				-Server $Server

			if ($type.IsInterface) 
			{
				CreateResourceFolders `
					-Name $ctrlAddIn.Name `
					-Project $Project
			}
		}
	}
	CopyDllToNavAddIns `
		-Dll $Dll `
		-Server $Server `
		-Config $Config
}

Function CreateResourceFolders()
{
	Param(
		[string]$Name,
		[string]$Project
	)

	$addInPath = [System.IO.Path]::Combine([System.IO.Path]::GetDirectoryName($Project), "Resource", $Name);
	if ([System.IO.Directory]::Exists($addInPath) -eq $false)
	{
		$d = [System.IO.Directory]::CreateDirectory($addInPath)
		$source = [System.IO.Path]::Combine([System.IO.Path]::GetDirectoryName($Project), "_build", "Resource", "*")
		Copy-Item $source $addInPath -Recurse
		Write-Host "Resource folder for ["$Name"] created - you may want to include it in the project."
	}
}

Function CopyDllToNavAddIns()
{
	Param(
		[string]$Dll,
		[string]$Server,
		[string]$Config
	)

	if ([System.IO.Directory]::Exists($AddInsPath))
	{
		$asmName = [Reflection.AssemblyName]::GetAssemblyName($Dll).Name

		$path = [System.IO.Path]::Combine($AddInsPath, $asmName)
		$path2 = [System.IO.Path]::Combine($ServerAddInsPath, $asmName)
		$copyPath2 = [System.IO.Directory]::Exists($ServerAddInsPath) -and $DeployToNst

		$p = [System.IO.Directory]::CreateDirectory($path)

		if ($copyPath2)
		{
			$p2 = [System.IO.Directory]::CreateDirectory($path2)
		}

		$allDlls = [System.IO.Path]::GetDirectoryName($Dll) + "\*.dll"
		Try
		{
			Copy-Item $allDlls $path -ErrorAction Stop
			if ($copyPath2)
			{
				Copy-Item $allDlls $path2 -ErrorAction Stop
			}
		}
		Catch [System.Exception]
		{
			Try
			{
				if ($Config -eq "Release")
				{
					Write-Host "Restarting service tier."
					Set-NAVServerInstance $Server -Restart

					Copy-Item $allDlls $path -ErrorAction Stop
					if ($copyPath2)
					{
						Copy-Item $allDlls $path2 -ErrorAction Stop
					}
				}
				else
				{
					Write-Host "Assembly and its dependencies have not been deployed because they were locked by the service tier."
					Write-Host "To restart the service tier and deploy, choose the Release configuration, then rebuild."
				}
			}
			Catch [System.Exception]
			{
				Write-Host "Dll files are still in use. Could they be locked by the Development Environment?"
			}
		}
	}
}

$myServerAddInsPath = ImportNavManagementModule -Server $ServerInstance
if ([System.IO.Directory]::Exists($myServerAddInsPath))
{
	$ServerAddInsPath = $myServerAddInsPath
}
InstallAllControlAddIns -Dll $DllFile -Server $ServerInstance -Project $ProjectPath -Config $Configuration -Solution $SolutionDir
