Function Main 
{
	try
	{
		Connect
		LegacyPublicationTargetsReport
		ConvertLegacyPublicationTargetsToCdEnvironments
		CreateCdTopologyTypeWithAllPurposes
		CreateRootBusinessProcessTypes
		ConfigurePublishingForPublications
		ReportExecutionResult  
	}
	finally
	{
		Cleanup
	}
}

Function Connect
{
	$global:yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes",""
	$global:no = New-Object System.Management.Automation.Host.ChoiceDescription "&No",""
	$global:YesNoChoices = [System.Management.Automation.Host.ChoiceDescription[]]($global:yes,$global:no)
	$RepurposeAssemblyName = "Tridion.ContentManager.PublishingConfigurationHelper.dll"
	$TtmCmdletsModuleName = "Tridion.TopologyManager.Automation"
	$TcmCoreServiceClientAssembly = "Tridion.ContentManager.CoreService.Client"
	$global:ScriptDirectoryPath = split-path -parent $PSCommandPath
	$RepurposeAssemblyPath = [System.IO.Path]::Combine($global:ScriptDirectoryPath, $RepurposeAssemblyName)
	Write-Host -ForegroundColor Green "Loading CoreService.Client assembly..."

	$error.Clear()
	[System.Reflection.Assembly]::LoadWithPartialName($TcmCoreServiceClientAssembly)
	[System.Reflection.Assembly]::LoadWithPartialName("Tridion.Common")
	ChangeAppConfig
	Import-Module $TtmCmdletsModuleName -Erroraction 'silentlycontinue'
	Import-Module $RepurposeAssemblyPath -Erroraction 'silentlycontinue'
	if ($error.Count -gt 0)
	{
		Write-Host -ForegroundColor Red ("Critical error occurred: " + $error[0])
		Write-Host -ForegroundColor Red ("Script execution stopped.")
		Exit
	}
	
	$connected = $false
	Do
	{
		Write-Host -ForegroundColor Green "`r`nProvide credentials for SDL Web Administrator User..."
		Do {$username = Read-Host "Enter Username"}
		while ($username -eq "")
		Do {$password = Read-Host -AsSecureString "Enter Password"}
		while ($password.Length -eq 0)
		$credentials = New-Object System.Management.Automation.PSCredential ` -ArgumentList $username, $password
		try
		{
			$global:CoreServiceClient = New-Object Tridion.ContentManager.CoreService.Client.SessionAwareCoreServiceClient
			$global:CoreServiceClient.ClientCredentials.Windows.ClientCredential.UserName = $username
			$global:CoreServiceClient.ClientCredentials.Windows.ClientCredential.Password = $credentials.GetNetworkCredential().password
			$global:CoreServiceClient.GetCurrentUser() > $null
			$global:DefaultReadOptions = New-Object Tridion.ContentManager.CoreService.Client.ReadOptions
			$global:ExpandedReadOptions = New-Object Tridion.ContentManager.CoreService.Client.ReadOptions
			$global:ExpandedReadOptions.LoadFlags = [Tridion.ContentManager.CoreService.Client.LoadFlags]::Expanded
			$connected = $true
			Write-Host -ForegroundColor Green "Connection succeeded."
		}
		Catch [Exception]
		{
			Write-Host -ForegroundColor Red ("Error: " + $_.Exception.Message)
			$caption = "Cannot connect to SDL Web using provided credentials..."
			$message = "Do you want to retry?"
			$result = $Host.UI.PromptForChoice($caption, $message, $global:YesNoChoices, 0)
			if($result -eq 1) 
			{
				Write-Host -ForegroundColor Red ("Script execution stopped.")
				Exit
			}
		}
	}
	while  ($connected -eq $false)
}

Function LegacyPublicationTargetsReport
{
	Write-Host -ForegroundColor White  ("`r`n====================== Generating CD Environments ======================")
	GetListLoadedPublicationTargets
	Write-Host -ForegroundColor Green "`r`n`r`nExisting Publication Targets information:"
	ForEach($publicationTarget in $global:PublicationTargetList)
	{
		Write-Host -ForegroundColor Cyan  ("   *{0} ({1})" -f $publicationTarget.Title, $publicationTarget.Id)
		Write-Host -ForegroundColor Cyan   "       TargetTypes:"
		ForEach($targetType in $publicationTarget.TargetTypes)
		{
			Write-Host -ForegroundColor Cyan  ("           **{0} ({1})" -f $targetType.Title, $targetType.IdRef)
		}
		Write-Host -ForegroundColor Cyan   "       Destinations:"
		ForEach($destination in $publicationTarget.Destinations)
		{
			Write-Host -ForegroundColor Cyan  ("           **{0} (Protocol schema: {1})" -f $destination.Title, $destination.ProtocolSchema.Title)
		}
		Write-Host -ForegroundColor Cyan   "       Used in Publications:"
		ForEach($publication in $publicationTarget.Publications)
		{
			Write-Host -ForegroundColor Cyan  ("           **{0} ({1})" -f $publication.Title, $publication.IdRef)
		}
		[Environment]::NewLine
	}
}

Function ConvertLegacyPublicationTargetsToCdEnvironments
{
	$global:SkippedPublicationTargets = New-Object "System.Collections.Generic.Dictionary``2[Tridion.ContentManager.CoreService.Client.PublicationTargetData,System.String]"
	$global:PubTargetConversionResult = New-Object "System.Collections.Generic.Dictionary``2[System.String,PSObject]"
	$message = "`r`n`r`nDo you want to start conversion wizard?"
	$result = $Host.UI.PromptForChoice("", $message, $global:YesNoChoices, 0)
	if($result -eq 1) 
	{
		Write-Host -ForegroundColor Red ("Script execution stopped.")
		Exit
	}
	ForEach($publicationTarget in $global:PublicationTargetList)
	{
		$created = $false
		$currentChoise = 0
		Do
		{
			Write-Host -ForegroundColor Green  ("`r`n`r`nCurrent Publication Target: {0} ({1})" -f $publicationTarget.Title, $publicationTarget.Id)
			$message = "Do you want to convert this Publication Target?"
			$result = $Host.UI.PromptForChoice("", $message, $global:YesNoChoices, 0)
			
			if ($result -eq 0)
			{
				#Checking prerequisites
				if ($publicationTarget.TargetTypes.Count -eq 0)
				{
					$global:SkippedPublicationTargets.Add($publicationTarget, "Current Publication Target does not have associated Target Types.")
					Write-Warning "Current Publication Target does not have associated Target Types. It will be skipped."
					break
				}
				if ($publicationTarget.Publications.Count -eq 0)
				{
					$global:SkippedPublicationTargets.Add($publicationTarget, "Current Publication Target does not have associated Publications.")
					Write-Warning "Current Publication Target does not have associated Publications. It will be skipped."
					break
				}
				if ($publicationTarget.Destinations.Count -gt 1)
				{
					$global:SkippedPublicationTargets.Add($publicationTarget, "Current Publication Target has more than 1 destination.")
					Write-Warning "Current Publication Target has more than 1 destination. It will be skipped."
					break
				}
				if ($publicationTarget.TargetTypes.Count -gt 1)
				{
					$global:SkippedPublicationTargets.Add($publicationTarget, "Current Publication Target has more than 1 associated Target Types.")
					Write-Warning "Current Publication Target has more than 1 associated Target Types. It will be skipped."
					break
				}
				Write-Host -ForegroundColor Green  ("For creating CD Environment in the Topology Manager you need to provide additional parameters:")
				Do {$discoveryEndPointUrl = Read-Host "Discovery Endpoint URL"}
				while ($discoveryEndPointUrl -eq "")
				$defaultPurpose = [System.Linq.Enumerable]::First($publicationTarget.TargetTypes).Title.Replace(" ", "")
				Do 
				{
					$environmentPurpose = Read-Host ("Environment Purpose (Can contain only letters, numbers and underscores. Default value is '{0}')" -f $defaultPurpose)
					if($environmentPurpose -eq "")
					{
						$environmentPurpose = $defaultPurpose
					}
					if (($global:PubTargetConversionResult.GetEnumerator()| Where {$_.Value.EnvironmentPurpose -eq $environmentPurpose}).Count -gt 0)
					{
						Write-Warning ("Environment Purpose '{0}' is already in use. Specify another Environment Purpose." -f $environmentPurpose)
						$environmentPurpose = ""
					}
				}
				while($environmentPurpose -eq "")
				$registeredAuthenticationTypes = 'OAuth','Anonymous'
				Do {$authenticationType = Read-Host ("Authentication type (allowed values: '{0}'. Default value: 'OAuth')" -f [System.String]::Join(', ', $registeredAuthenticationTypes))}
				until ($registeredAuthenticationTypes.Contains($authenticationType) -eq $true -Or $authenticationType -eq "")
				if ($authenticationType -eq "")
				{
					$authenticationType = "OAuth"
				}
				if($authenticationType -eq "OAuth")
				{
					Do {$clientId = Read-Host "Provide ClientId for OAuth authentication"}
					while ($clientId -eq "")
					
					Do {$clientSecret = Read-Host "Provide ClientSecret for OAuth authentication"}
					while ($clientSecret -eq "")
				}
				
				$defaultcdEnvironmentId = $publicationTarget.Title.Replace(" ", "")
				$cdEnvironmentId = Read-Host ("CD Environment Id (default value is '{0}')" -f $defaultcdEnvironmentId)
				if($cdEnvironmentId -eq "")
				{
					$cdEnvironmentId = $defaultcdEnvironmentId
				}

				$currentChoise = 0
				$error.Clear()
				
				if ($authenticationType -eq "Anonymous")
				{
					Add-TtmCdEnvironment -Id $cdEnvironmentId -DiscoveryEndPointUrl $discoveryEndPointUrl -EnvironmentPurpose $environmentPurpose -authenticationType $authenticationType -Erroraction 'silentlycontinue' > $null
				}
				else
				{
					Add-TtmCdEnvironment -Id $cdEnvironmentId -DiscoveryEndPointUrl $discoveryEndPointUrl -EnvironmentPurpose $environmentPurpose -authenticationType $authenticationType -ClientId $clientId -ClientSecret $clientSecret -Erroraction 'silentlycontinue' > $null
				}
				
				if ($error.Count -gt 0)
				{
					Write-Host -ForegroundColor Red ("Error: " + $error[0])
					$caption = "Cannot create CD Environment using provided data..."
					$message = "Do you want to retry (Yes = retry, No = Skip converting this Publication Target)?"
					$currentChoise = $Host.UI.PromptForChoice($caption, $message, $global:YesNoChoices, 0) 

					# If error has been thrown and user chose to not retry, skip current Publication Target.
					if ($currentChoise -eq 1)
					{
						$global:SkippedPublicationTargets.Add($publicationTarget, "Skipped by user after getting an exception on creating CD Environment.")
					}
				}
				else
				{
					Write-Host -ForegroundColor Green  ("Successfully created CdEnvironment with id '{0}'." -f $cdEnvironmentId)

					$defaultWebsiteId = $cdEnvironmentId + "_WebSite"
					$webSiteId = Read-Host ("WebSite Id (default value is '{0}')" -f $defaultWebsiteId)
					if($webSiteId -eq "")
					{
						$webSiteId = $defaultWebsiteId
					}

					Do {$baseUrl = Read-Host "Base URL for creating TopologyManager WebSite"}
					while ($baseUrl -eq "")

					Add-TtmWebSite -id $webSiteId -BaseUrls ($baseUrl) -CdEnvironmentId $cdEnvironmentId -Erroraction 'silentlycontinue' > $null
					
					if ($error.Count -gt 0)
					{
						# Try to remove already created cd environment ?
						Write-Host -ForegroundColor Red ("`r`n`r`nCannot create website.")
						Write-Host -ForegroundColor Red ("Error: " + $error[0])
						Write-Host -ForegroundColor Green ("Deleting CdEnvironment with id '{0}'" -f $cdEnvironmentId)
						
						# If we get error on clean up - assume, that something critical has happened. In this case - stop script.
						$error.Clear()
						Remove-TtmCdEnvironment -Id $cdEnvironmentId -Force -Erroraction 'silentlycontinue' > $null
						if($error.Count -gt 0)
						{
							Write-Host -ForegroundColor Red ("Critical error occurred during cleanup of the CdEnvironment: " + $error[0])
							Write-Host -ForegroundColor Red ("Script execution stopped.")
							Exit
						}
						Write-Host -ForegroundColor Green ("CdEnvironment with id '{0}' was deleted." -f $cdEnvironmentId)
						
						# After deleting CdEnvironment suggest user to input new data and try to create new CdTopologyType and Website.
						$caption = "Cannot create CdEnvironment\WebSite using provided data..."
						$message = "Do you want to retry (Yes = retry, No = Skip converting this Publication Target)?"
						$currentChoise = $Host.UI.PromptForChoice($caption, $message, $global:YesNoChoices, 0)
					}
					else
					{
						$webAppId = $webSiteId + "_RootWebApp" # Id of the default webApp is WebsiteId + "_RootWebApp"
						Write-Host -ForegroundColor Green  ("Successfully created Website with id '{0}'." -f $webSiteId)
						Write-Host -ForegroundColor Green  ("Successfully created default Web Application for Website '{0}' with id '{1}'." -f $webSiteId, $webAppId)
						$created = $true
						$global:PubTargetConversionResult.Add($publicationTarget.Id, (New-Object PSObject -Property @{
							CdEnvironmentId = $cdEnvironmentId
							EnvironmentPurpose = $environmentPurpose
							WebSiteId = $webSiteId
							WebSiteBaseUrl = $baseUrl
							WebApplicationId = $webAppId
							TargetTypeId =  [System.Linq.Enumerable]::First($publicationTarget.TargetTypes).IdRef
						}))
					}
				}
			}
			else
			{
				$global:SkippedPublicationTargets.Add($publicationTarget, "Skipped by user.")
				break;
			}
		}
		while  ($created -eq $false -And $currentChoise -eq 0)
	}
	if($global:PubTargetConversionResult.Count -eq 0)
	{
		[Environment]::NewLine
		Write-Warning "No Publication Targets were converted. Script execution stopped."
		Exit
	}
	$global:AffectedPublicationTargets = New-Object  System.Collections.Generic.List[Tridion.ContentManager.CoreService.Client.PublicationTargetData]
	$affectedPublicationTargetIds = $global:PubTargetConversionResult.GetEnumerator()|Foreach{ $_.Key}
	if($affectedPublicationTargetIds -ne $null)
	{
		$global:AffectedPublicationTargets = $global:CoreServiceClient.BulkRead($affectedPublicationTargetIds, $global:DefaultReadOptions).GetEnumerator() | Foreach{ $_.Value.Item}
	}
	$global:AffectedPublicationIds = New-Object  System.Collections.Generic.List[string]
	foreach ($pubTarget in $global:AffectedPublicationTargets)
	{
		foreach ($id in ($pubTarget.Publications|Foreach{ $_.IdRef}))
		{
			$global:AffectedPublicationIds.Add($id)
		}
	}
	$global:AffectedPublications = $global:CoreServiceClient.BulkRead($global:AffectedPublicationIds, $global:DefaultReadOptions).GetEnumerator() | Foreach{ $_.Value.Item}
}

Function CreateCdTopologyTypeWithAllPurposes
{
	Write-Host -ForegroundColor White  ("`r`n====================== Creating CD Topology Type ======================")
	$global:UsedPurposes = New-Object "System.Collections.Generic.List[System.String]"
	foreach ($kvp in $global:PubTargetConversionResult.GetEnumerator())
	{
		if ($global:UsedPurposes.Contains($kvp.Value.EnvironmentPurpose) -eq $false)
		{
			$global:UsedPurposes.Add($kvp.Value.EnvironmentPurpose)
		}
	}  
	$currentChoise = 0;
	$created = $false
	
	Do
	{
		Write-Host -ForegroundColor Green ("CdTopologyType with purposes below will be created: {0}" -f [System.String]::Join(", ", ($global:UsedPurposes | Foreach{'"' + $_ + '"'})))
		Do {$global:topologyTypeName = Read-Host "Provide Id for the CdTopologyType (it will be used both for Id and Name)"}
		while ($global:topologyTypeName -eq "")
		$error.Clear()
		Add-TtmCdTopologyType -id $global:topologyTypeName -Name $global:topologyTypeName -EnvironmentPurposes ($global:UsedPurposes) -Erroraction 'silentlycontinue' > $null
		if ($error.Count -gt 0)
		{
			Write-Host -ForegroundColor Red ("Error: " + $error[0])
			$caption = "Cannot create CdTopologyType using provided data..."
			$message = "Do you want to retry (Yes = retry, No = Stop script execution)?"
			$currentChoise = $Host.UI.PromptForChoice($caption, $message, $global:YesNoChoices, 0)
		}
		else
		{
			Write-Host -ForegroundColor Green ("Successfully created CdTopologyType with id '{0}'." -f $global:topologyTypeName)
			$created = $true
		}
	}
	while  ($created -eq $false -And $currentChoise -eq 0)
	if ($created -eq $false)
	{
		[Environment]::NewLine
		Write-Warning "Unbale to create CdTopologyType. Script execution stopped."
		Exit
	}
}

Function CreateRootBusinessProcessTypes
{
	$global:BusinessProcessTypes = New-Object "System.Collections.Generic.Dictionary``2[Tridion.ContentManager.TcmUri,Tridion.ContentManager.TcmUri]"
	$global:CreatedBPT = New-Object "System.Collections.Generic.List[PSObject]"

	Write-Host -ForegroundColor White  ("`r`n====================== Creating Business Process Types ======================")

	foreach ($pub in $global:AffectedPublications)
	{
		if ($global:BusinessProcessTypes.ContainsKey($pub.id))
		{
			continue
		}

		$bluePrintNodesFilter = New-Object Tridion.ContentManager.CoreService.Client.BluePrintNodesFilterData 
		$bluePrintNodesFilter.ForRepository = New-Object Tridion.ContentManager.CoreService.Client.LinkToRepositoryData
		$bluePrintNodesFilter.ForRepository.IdRef = $pub.Id

		$parentPub = ($CoreServiceClient.GetSystemWideList($bluePrintNodesFilter) | where {$_.Parents.Count -eq 0})[0]

		if ($global:BusinessProcessTypes.ContainsKey($parentPub.id))
		{
			# construct BPT TCM URI on the current BluePrint level
			$rootBpt = $global:BusinessProcessTypes[$parentPub.id]
			$childBpt = $global:CoreServiceClient.GetTcmUri($rootBpt, $pub.Id, $null)
			$global:BusinessProcessTypes.Add($pub.Id, $childBpt)

			continue
		}

		Write-Host -ForegroundColor Green  ("Creating Business Process Type for root publication '{0}' with id '{1}'" -f $parentPub.Title, $parentPub.id)
		$parentPub = $global:CoreServiceClient.Read($parentPub.id, $global:DefaultReadOptions)
		$bptId = $null

		# In the bluePrint root create new bpt.
		$bptCreated = $false
		Do
		{
			$error.Clear()
			try
			{
				$bptId = CreateBusinessProcessType $parentPub $global:topologyTypeName $global:UsedPurposes
			}
			catch [Exception]
			{
				# handle exception by checking $error variable.
			}
			
			if ($error.Count -gt 0)
			{
				Write-Host -ForegroundColor Red ("Error: " + $error[0])
				$caption = "Cannot create Business Process Type with provided data..."
				$message = "Do you want to retry (Yes = retry, No = Skip creating this Business Process Type)?"
				$repeatRepurpose = $Host.UI.PromptForChoice($caption, $message, $global:YesNoChoices, 0)
				if($repeatRepurpose -eq 1)
				{
					break
				}
			}
			else
			{
				$global:BusinessProcessTypes.Add($parentPub.Id, $bptId)

				if ($parentPub.Id -ne $pub.Id)
				{
					$childBpt = $global:CoreServiceClient.GetTcmUri($bptId, $pub.Id, $null)
					$global:BusinessProcessTypes.Add($pub.Id, $childBpt)
				}

				$bptCreated = $true
			}
			
		}
		while($bptCreated -eq $false)
	}
}

Function ConfigurePublishingForPublications
{
	$global:CreatedMappings = New-Object "System.Collections.Generic.List[PSObject]"
	$global:PublicationsWithInvalidatedPublicationUrl = New-Object "System.Collections.Generic.List[Tridion.ContentManager.CoreService.Client.PublicationData]"
	$global:PublicationsWithSkippedPublicationUrl = New-Object "System.Collections.Generic.List[Tridion.ContentManager.CoreService.Client.PublicationData]"
	$global:BPTAssignedToPublications = New-Object "System.Collections.Generic.List[PSObject]"
	$global:RepurposeStatistics = New-Object "System.Collections.Generic.List[PSObject]"
	$global:NotRepurposedPublicationsWithPubTargets = New-Object "System.Collections.Generic.List[PSObject]"
	$global:SkippedMappings = New-Object "System.Collections.Generic.List[PSObject]"
	$global:DatabaseType = ""
	
	foreach ($publication in $AffectedPublications)
	{
		Write-Host -ForegroundColor White  ("`r`n====================== Configuring publishing for Publication '{0}' ({1}) ======================" -f $publication.Title, $publication.id)
		$pub = $global:CoreServiceClient.Read($publication.id, $global:DefaultReadOptions)
		
		# assign shared BPT to the current publication
		$bptId = $global:BusinessProcessTypes[$publication.id]
		$bpt = $global:CoreServiceClient.Read($bptId, $global:ExpandedReadOptions)
		$pub.BusinessProcessType = New-Object Tridion.ContentManager.CoreService.Client.LinkToBusinessProcessTypeData
		$pub.BusinessProcessType.IdRef = $bptId
		$pub = $global:CoreServiceClient.Save($pub, $global:DefaultReadOptions)
		$global:BPTAssignedToPublications.Add((New-Object PSObject -Property @{
			Publication = $publication
			Bpt = $bpt
		}))
		
		Write-Host -ForegroundColor Green  ("Business Process Type '{0}' with id '{1}' was assigned to Publication '{2}' with id '{3}'." -f $bpt.Title, $bptId, $publication.Title, $publication.id)
		
		# set publication.PublicationUrl to empty string
		$usedPubTargetIds = New-Object "System.Collections.Generic.List[System.String]"
		Foreach($pubTarget in $global:AffectedPublicationTargets)
		{
			if (($pubTarget.Publications| Foreach {$_.IdRef}).Contains($pub.Id) )
			{
				$usedPubTargetIds.Add($pubTarget.Id)
			}
		}

		$defaultRelativeUrl = $pub.PublicationUrl
		if ($defaultRelativeUrl -eq "")
		{
			$defaultRelativeUrl = "/"
		}

		InvalidatePublicationUrl $pub
		$pub = $global:CoreServiceClient.Read($pub.Id, $global:DefaultReadOptions)
		
		Foreach ($pubTargetId in $usedPubTargetIds)
		{
			CreateMappingAndRepurposePublishStates $pubTargetId $pub $bpt $defaultRelativeUrl
		}
	}
}

Function CreateMappingAndRepurposePublishStates
{
	param([System.String] $pubTargetId, 
	[Tridion.ContentManager.CoreService.Client.PublicationData] $publication,
	[Tridion.ContentManager.CoreService.Client.BusinessProcessTypeData] $bpt,
	[System.String] $defaultRelativeUrl)

	$publicationTarget = ($global:AffectedPublicationTargets|Where {$_.Id -eq $pubTargetId})[0]
	$purpose = $global:PubTargetConversionResult[$pubTargetId].EnvironmentPurpose
	$targetType = ($bpt.TargetTypes| Where{$_.Purpose -eq $purpose})[0]
	$webAppId = $global:PubTargetConversionResult[$pubTargetId].WebApplicationId
	$mappingId = $publication.Id.Replace(":", "").Replace("-", "_") + "_" +  $purpose.Replace(" ", "").Replace("-", "_")
	$relativeUrl = $defaultRelativeUrl
	$created = $false

	Write-Host -ForegroundColor Green  ("`r`nCreating mapping to enable publishing from this publication to a CD Environment with purpose '{0}' (a replacement of the Publication Target '{1}' with Id '{2}')." -f $purpose, $publicationTarget.Title, $publicationTarget.Id)

	Do
	{
		$error.Clear()
		
		$relativeUrl = Read-Host ("Provide RelativeUrl for mapping (default value is '{0}')" -f $defaultRelativeUrl)
		if($relativeUrl -eq "")
		{
			$relativeUrl = $defaultRelativeUrl
		}

		Add-TtmMapping -id $mappingId -WebApplicationId $webAppId -PublicationId $publication.Id  -RelativeUrl $relativeUrl -Erroraction 'silentlycontinue' > $null
		if ($error.Count -gt 0)
		{
			Write-Host -ForegroundColor Red ("`r`nCannot create Topology Manager Mapping for Publication '{0}' with id '{1}' and purpose '{2}'." -f $publication.Title, $publication.Id, $purpose)
			Write-Host -ForegroundColor Red ("Error: " + $error[0])
			$message = "Do you want to retry (Yes = retry, No = Stop processing of this Publication and purpose pair)?"
			$repeatCreateMapping = $Host.UI.PromptForChoice("", $message, $global:YesNoChoices, 0)
			if($repeatCreateMapping -ne 0)
			{
				$global:SkippedMappings.Add((New-Object PSObject -Property @{
					Publication = $publication
					Purpose = $purpose
					Reason = $error[0]
				}))
				break;
			}
		}
		else
		{
			$created = $true
			$global:CreatedMappings.Add((New-Object PSObject -Property @{
				Id = $mappingId
				WebApplicationId = $webAppId
				PublicationId = $publication.Id
				RelativeUrl = $relativeUrl
			}))
		}
	}
	while ($created -ne $true)
	
	if ($created -eq $true)
	{
		Write-Host -ForegroundColor Green ("Topology Manager Mapping with id '{0}' was created for Publication '{1}' with id '{2}' and purpose '{3}'." -f $mappingId, $publication.Title, $publication.Id, $purpose)
		
		# Calling repurpose cmdlet for current Publication
		Do
		{
			$executed = $false
			$caption = "The ContentManager / TopologyManager configuration created for Publication '{0}' with id '{1}' and purpose '{2}'." -f $publication.Title, $publication.Id, $purpose
			$message = "Do you want to update publish states of published items from old Publication Target '{0}' with Id '{1}' to the new Target Type '{2}' with Id {3} and purpose '{4}'?" -f $publicationTarget.Title, $publicationTarget.Id, $targetType.Title, $targetType.Id, $purpose
			$callRepurposeResult = $Host.UI.PromptForChoice($caption, $message, $global:YesNoChoices, 0)
			if ($callRepurposeResult -eq 1)
			{
				break
			}
			
			if($global:DatabaseType -eq "")
			{
				Write-Host -ForegroundColor Green "`r`nYou need to specify parameters for database connection to execute the Convert-TcmPublishStates cmdlet"
				Do {$global:DatabaseType = Read-Host "Enter Database Type (allowed values - MsSql/Oracle)"}
				until ($global:DatabaseType -eq "MsSql" -Or $global:DatabaseType -eq "Oracle")
				if($global:DatabaseType -eq "MsSql")
				{
					ReadMsSqlConnectionOptions
				}
				else
				{
					ReadOracleConnectionOptions
				}
			}
			$error.Clear()
			
			# Using split as we need to get ItemId from system wide objects. 
			Try
			{
				ExecuteRepurposeScript $publicationTarget.Id.Split('-')[1] $targetType.Id.Split('-')[1] $publication.Id.Split('-')[1]
			}
			Catch [Exception]
			{
				# handle exception by checking $error variable.
			}
			if ($error.Count -gt 0)
			{
				# one of the reasons for failing repurpose is that customer entered wrong credentials, we'll request them again
				$global:DatabaseType = ""

				Write-Host -ForegroundColor Red ("Error: " + $error[0])
				$caption = "Cannot repurpose publish states to new Target Type..."
				$message = "Do you want to retry (Yes = retry, No = Skip repurposing for current Publication)?"
				$repeatRepurpose = $Host.UI.PromptForChoice($caption, $message, $global:YesNoChoices, 0)
				if($repeatRepurpose -eq 1)
				{
					break
				}
			}
			else
			{
				$global:RepurposeStatistics.Add((New-Object PSObject -Property @{
					FromPublicationTarget = $publicationTarget
					ToTargetType = $targetType
					Publication = $publication
				}))
				$executed = $true
			}
		}
		while ($executed -eq $false)

		if ($executed -eq $false)
		{
			$global:NotRepurposedPublicationsWithPubTargets.Add((New-Object PSObject -Property @{
					Publication = $publication
					Target = $publicationTarget
			}))
		}
	}	
}

Function InvalidatePublicationUrl
{
	param([Tridion.ContentManager.CoreService.Client.PublicationData] $publication)

	$caption = "'PublicationUrl' property is not required anymore. Relative URL will be configured in Mapping.RelativeUrl property."
	$message = "Do you want to set 'PublicationUrl' to empty value for current Publication?"
	$result = $Host.UI.PromptForChoice($caption, $message, $global:YesNoChoices, 0)
	if ($result -eq 0)
	{
		$publication.PublicationUrl = ""
		$global:CoreServiceClient.Save($publication, $global:DefaultReadOptions) > $null
		$global:PublicationsWithInvalidatedPublicationUrl.Add($publication)
	}
	else
	{
		$global:PublicationsWithSkippedPublicationUrl.Add($publication)
	}
}

Function ExecuteRepurposeScript
{
	param([System.Int32] $SourceId, [System.Int32] $DestinationTargetTypeId, [System.Int32] $PublicationId)

	if($global:DatabaseType -eq "MsSql")
	{
		Convert-TcmPublishStates -SourceId $SourceId -SourceType 65537 -DestinationTargetTypeId $DestinationTargetTypeId -PublicationId $PublicationId -DatabaseType MsSql -Server $global:MsSqlServer -DatabaseName $global:MsSqlDatabaseName -User $global:DatabaseUser -Password $global:DatabasePassword -Erroraction 'silentlycontinue'
	}
	else
	{
		if($global:OracleAsSysDba)
		{
			$asSysDbaString = "-AsSysDba"
			Convert-TcmPublishStates -SourceId $SourceId -SourceType 65537 -DestinationTargetTypeId $DestinationTargetTypeId -PublicationId $PublicationId -DatabaseType Oracle -NetServiceName $global:OracleNetServiceName -SchemaName $global:OracleSchemaName -User $global:DatabaseUser -Password $global:DatabasePassword -AsSysDba -Erroraction 'silentlycontinue'
		}
		else
		{
			Convert-TcmPublishStates -SourceId $SourceId -SourceType 65537 -DestinationTargetTypeId $DestinationTargetTypeId -PublicationId $PublicationId -DatabaseType Oracle -NetServiceName $global:OracleNetServiceName -SchemaName $global:OracleSchemaName -User $global:DatabaseUser -Password $global:DatabasePassword -Erroraction 'silentlycontinue'
		}
	}
}

Function CreateBusinessProcessType
{
	param([Tridion.ContentManager.CoreService.Client.PublicationData] $publication, [string] $cdTopologyTypeId, [System.Collections.Generic.List[System.String]] $purposes)
	
	$initialBptTitle = "Bpt_" + $cdTopologyTypeId;
	$bptTitle = $initialBptTitle
	
	$found = $false
	$counter = 1
	$bptListFilter = New-Object Tridion.ContentManager.CoreService.Client.OrganizationalItemsFilterData
	$bptListFilter.BaseColumns = [Tridion.ContentManager.CoreService.Client.ListBaseColumns]::IdAndTitle
	$bptListFilter.ItemTypes = (4096) #bpt
	$bptList = $global:CoreServiceClient.GetList($publication.Id, $bptListFilter)
	Do
	{
		if (($bptList | where {$_.Title.ToUpperInvariant() -eq $bptTitle.ToUpperInvariant()}).Count -ne 0)
		{
			$bptTitle = $initialBptTitle + "_" + ($counter++).ToString()
		}
		else
		{
			$found = $true
		}
	}
	while ($found -eq $false)
	
	$bpt = New-Object Tridion.ContentManager.CoreService.Client.BusinessProcessTypeData
	$bpt.IsRootOrganizationalItem = $true
	$bpt.CdTopologyTypeId = $cdTopologyTypeId
	$bpt.Title = $bptTitle
	$bpt.Description = $bptTitle
	$bpt.LocationInfo = New-Object Tridion.ContentManager.CoreService.Client.LocationInfo
	$bpt.LocationInfo.ContextRepository = New-Object Tridion.ContentManager.CoreService.Client.LinkToRepositoryData
	$bpt.LocationInfo.ContextRepository.IdRef = $publication.Id
	$targetTypes = New-Object System.Collections.Generic.List[Tridion.ContentManager.CoreService.Client.TargetTypeData]
	
	$targetTypesFilter = New-Object Tridion.ContentManager.CoreService.Client.TargetTypesFilterData
	$targetTypesFilter.BaseColumns = [Tridion.ContentManager.CoreService.Client.ListBaseColumns]::IdAndTitle
	$targetTypesList = $global:CoreServiceClient.GetSystemWideList($targetTypesFilter)
	
	foreach ($purpose in $purposes)
	{
		$existingTtId = ($global:PubTargetConversionResult.GetEnumerator()|Where { $_.Value.EnvironmentPurpose -eq $purpose}).Value.TargetTypeId
		$existingTt = $global:CoreServiceClient.Read($existingTtId, $global:DefaultReadOptions)

		$initialTtTitle = "Cloned_" + $purpose + "_" + $existingTt.Title
		$ttTitle = $initialTtTitle
		$found = $false
		$counter = 1
		Do
		{
			if (($targetTypesList | where {$_.Title.ToUpperInvariant() -eq $ttTitle.ToUpperInvariant()}).Count -ne 0)
			{
				$ttTitle = $initialTtTitle + "_" + ($counter++).ToString()
			}
			else
			{
				$found = $true
			}
		}
		while($found -eq $false)
				
		$tt = New-Object Tridion.ContentManager.CoreService.Client.TargetTypeData
		$tt.Purpose = $purpose
		$tt.Title = $ttTitle
		$tt.Description = $ttTitle
		
		# Copy security from old target type to new one.
		$aceList = New-Object System.Collections.Generic.List[Tridion.ContentManager.CoreService.Client.AccessControlEntryData]
		foreach($ace in $existingTt.AccessControlList.AccessControlEntries)
		{
		$clonedAce = New-Object Tridion.ContentManager.CoreService.Client.AccessControlEntryData 
		$clonedAce.Trustee = New-Object Tridion.ContentManager.CoreService.Client.LinkToTrusteeData 
		$clonedAce.Trustee.IdRef = $ace.Trustee.IdRef
		$clonedAce.AllowedRights = $ace.AllowedRights
		$aceList.Add($clonedAce)
		}
		$tt.AccessControlList = New-Object Tridion.ContentManager.CoreService.Client.AccessControlListData
		$tt.AccessControlList.AccessControlEntries = $aceList
		$targetTypes.Add($tt)
	}
	$bpt.TargetTypes = $targetTypes
	$result =  $global:CoreServiceClient.Save($bpt, $global:DefaultReadOptions)
	
	Write-Host -ForegroundColor Green  ("Successfully created Business Process Type '{0}' with id '{1}'." -f $result.Title, $result.Id)
	$global:CreatedBPT.Add((New-Object PSObject -Property @{
		Publication = $publication
		Bpt = $result
	}))
	return $result.Id
}

Function ReportExecutionResult
{
	Write-Host -ForegroundColor Green "`r`n`r`n====================== Script execution result ======================"
	
	Write-Host -ForegroundColor Green "`r`nItems created in the TopologyManager:"
	
	Write-Host -ForegroundColor Green "`r`nCdEnvironments:"
	$i=0
	foreach($kvp in $global:PubTargetConversionResult.GetEnumerator())
	{
		Write-Host -ForegroundColor  Cyan ("{0}) Id = '{1}'; EnvironmentPurpose = '{2}'" -f ++$i, $kvp.Value.CdEnvironmentId, $kvp.Value.EnvironmentPurpose)
	}
	
	Write-Host -ForegroundColor Green "`r`nWebsites:"
	$i=0
	foreach($kvp in $global:PubTargetConversionResult.GetEnumerator())
	{
		Write-Host -ForegroundColor  Cyan ("{0}) Id = '{1}'; BaseURL = '{2}'" -f ++$i, $kvp.Value.WebsiteId, $kvp.Value.WebsiteBaseUrl)
	}
	
	Write-Host -ForegroundColor Green "`r`nWebApplications:"
	$i=0
	foreach($kvp in $global:PubTargetConversionResult.GetEnumerator())
	{
		Write-Host -ForegroundColor  Cyan ("{0}) Id = '{1}'; ContextURL = '/'" -f ++$i, $kvp.Value.WebApplicationId)
	}
	
	Write-Host -ForegroundColor Green "`r`nCdTopologyType:"
	Write-Host -ForegroundColor Cyan ("1) Id = '{0}'" -f $global:topologyTypeName)
	
	if($global:CreatedMappings.Count -gt 0)
	{
		Write-Host -ForegroundColor Green "`r`nMappings:"
		$i=0
		foreach($item in $global:CreatedMappings)
		{
			Write-Host -ForegroundColor  Cyan ("{0}) Id = '{1}'; WebApplicationId = '{2}'; PublicationId = '{3}'; RelativeUrl = '{4}'" -f ++$i, $item.Id, $item.WebApplicationId, $item.PublicationId, $item.RelativeUrl)
		}
	}
	
	Write-Host -ForegroundColor Green "`r`n`r`nContent Manager changes:"
	
	if ($global:CreatedBPT.Count -gt 0)
	{
		Write-Host -ForegroundColor Green "`r`nCreated Business Process Types:"
		$i=0
		foreach ($item in $global:CreatedBPT)
		{
			Write-Host -ForegroundColor  Cyan ("{0})'{1}' (Id = '{2}') in Publication '{3}' (Id = '{4}')" -f ++$i, $item.Bpt.Title, $item.Bpt.Id, $item.Publication.Title, $item.Publication.Id)
		}
	}
	
	if ($global:BPTAssignedToPublications.Count -gt 0)
	{
		Write-Host -ForegroundColor Green "`r`nBusiness Process Types associated with Publications:"
		$i=0
		foreach ($item in $global:BPTAssignedToPublications)
		{
			Write-Host -ForegroundColor  Cyan ("{0}) Publication: '{1}' (Id = '{2}'); Business Process Type: '{3}' (Id = '{4}')" -f ++$i, 
			$item.Publication.Title, $item.Publication.Id, $item.Bpt.Title, $item.Bpt.Id)
		}
	}
	
	if ($global:RepurposeStatistics.Count -gt 0)
	{
		Write-Host -ForegroundColor Green "`r`nPublish states of published items repurposed from Publication Targets to new Target Types:"
		$i=0
		foreach ($item in $global:RepurposeStatistics)
		{
			Write-Host -ForegroundColor  Cyan ("{0}) Publication: '{1}' (Id = '{2}'); Publication Target: '{3}' (Id = '{4}'); Target Type: '{5}' (Id = '{6}') " -f ++$i, $item.Publication.Title, $item.Publication.Id, 
			$item.FromPublicationTarget.Title, $item.FromPublicationTarget.Id, $item.ToTargetType.Title, $item.ToTargetType.Id)
		}
	}
	
	if($global:PublicationsWithInvalidatedPublicationUrl.Count -gt 0)
	{
		Write-Host -ForegroundColor Green "`r`nPublications updated with empty 'PublicationUrl' property:"
		$i=0
		foreach($pub in $global:PublicationsWithInvalidatedPublicationUrl)
		{
			Write-Host -ForegroundColor Cyan ("{0})'{1}' (Id = '{2}')" -f ++$i, $pub.Title, $pub.id)
		}
	}
	
	Write-Host -ForegroundColor Yellow "`r`n`r`nDetected problems:"
	
	# Skipped part.
	if($global:SkippedPublicationTargets.Count -gt 0)
	{
		Write-Host -ForegroundColor Yellow "`r`nSkipped Publication Targets:"
		$i=0
		foreach($kvp in $global:SkippedPublicationTargets.GetEnumerator())
		{
			Write-Host -ForegroundColor  Cyan  ("{0})Publication Target: '{1}' (Id = '{2}'); Reason: '{3}'"  -f ++$i, $kvp.Key.Title, $kvp.Key.Id, $kvp.Value)
		}
	}
	
	if($global:SkippedMappings.Count -gt 0)
	{
		Write-Host -ForegroundColor Yellow "`r`nMappings were not created for the Publication and purpose combinations below:"
		$i=0
		foreach($item in $global:SkippedMappings)
		{
			Write-Host -ForegroundColor  Cyan ("{0})Publication: '{1}' (Id = '{2}'); Purpose: '{3}'; Reason: '{4}'"  -f ++$i, $item.Publication.Title, 
			$item.Publication.Id, $item.Purpose, $item.Reason)
		}
	}
		
	if($global:NotRepurposedPublicationsWithPubTargets.Count -gt 0)
	{
		Write-Host -ForegroundColor Yellow "`r`nRepurposing of publish states failed or skipped for the following combinations of Publications and Publication Targets:"
		$i=0
		foreach($pub in $global:NotRepurposedPublicationsWithPubTargets)
		{
			Write-Host -ForegroundColor Cyan ("{0})'{1}' (Id = '{2}') and '{3}' (Id = '{4}')" -f ++$i, $pub.Publication.Title, $pub.Publication.id, $pub.Target.Title, $pub.Target.id)
		}
	}
	
	if($global:PublicationsWithSkippedPublicationUrl.Count -gt 0)
	{
		Write-Host -ForegroundColor Yellow "`r`nFollowing publications have Topology Manager mappings, but 'PublicationUrl' property was not set to empty string (which is redundant):"
		$i=0
		foreach($pub in $global:PublicationsWithSkippedPublicationUrl)
		{
			Write-Host -ForegroundColor  Cyan ("{0})'{1}' (Id = '{2}')" -f ++$i, $pub.Title, $pub.Id)
		}
	}
	
	Write-Host -ForegroundColor  Green "`r`n`r`nScript execution successfully finished."
}

Function GetListLoadedPublicationTargets
{
	$global:PublicationTargetList = New-Object System.Collections.Generic.List[Tridion.ContentManager.CoreService.Client.PublicationTargetData]
	$ptFilter = New-Object Tridion.ContentManager.CoreService.Client.PublicationTargetsFilterData 
	$ptFilter.BaseColumns = [Tridion.ContentManager.CoreService.Client.ListBaseColumns]::Id
	$ptFilter.IncludeEmulated = $false
	$ptIds = $global:CoreServiceClient.GetSystemWideList($ptFilter)| Foreach{ $_.Id }
	if ($ptIds -ne $null)
	{
		$global:PublicationTargetList = $global:CoreServiceClient.BulkRead($ptIds, $global:DefaultReadOptions).GetEnumerator() | Foreach{ $_.Value.Item}
	}
}

Function ReadMsSqlConnectionOptions
{
	Do {$global:MsSqlServer = Read-Host "Enter MsSql server name"}
	while ($global:MsSqlServer -eq "")

	Do {$global:MsSqlDatabaseName = Read-Host "Enter MsSql database name"}
	while ($global:MsSqlDatabaseName -eq "")

	ReadDatabaseCredentials
}

Function ReadOracleConnectionOptions
{
	Do {$global:OracleNetServiceName = Read-Host "Enter Oracle net service name"}
	while ($global:OracleNetServiceName -eq "")
	
	Do {$global:OracleSchemaName = Read-Host "Enter Oracle schema of the CM database"}
	while ($global:OracleSchemaName -eq "")
	
	$result = $Host.UI.PromptForChoice("", "Connect using SYSDBA privilege?", $global:YesNoChoices, 1)
	if ($result -eq 0)
	{
		$global:OracleAsSysDba = $true
	}
	else
	{
		$global:OracleAsSysDba = $false
	}
	ReadDatabaseCredentials
}

Function ReadDatabaseCredentials
{
	Do {$global:DatabaseUser = Read-Host "Enter admin user name for the CM database"}
	while ($global:DatabaseUser -eq "")
	Do {$password = Read-Host -AsSecureString "Enter admin user password for the CM database"}
	while ($password.Length -eq 0)
	$credentials = New-Object System.Management.Automation.PSCredential ` -ArgumentList $global:DatabaseUser, $password
	$global:DatabasePassword = $credentials.GetNetworkCredential().password
}

Function ChangeAppConfig
{
	$configName = "Generate-Topology.config"
	$configFullPath = [System.IO.Path]::Combine($global:ScriptDirectoryPath, $configName)
	[System.AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", $configFullPath)
	[Configuration.ConfigurationManager].GetField("s_initState", "NonPublic, Static").SetValue($null, 0)
	[Configuration.ConfigurationManager].GetField("s_configSystem", "NonPublic, Static").SetValue($null, $null)
	([Configuration.ConfigurationManager].Assembly.GetTypes() | where {$_.FullName -eq "System.Configuration.ClientConfigPaths"})[0].GetField("s_current", "NonPublic, Static").SetValue($null, $null)
}

Function Cleanup
{
	if ($global:CoreServiceClient -ne $null)
	{
		$global:CoreServiceClient.Dispose()
	}
}

Main