#########################################################################################
#
# Copyright (c) Microsoft Corporation.
#
# This PS DSC resource enables installing a package. The resource uses Install-Package cmdlet
# to install the package from various providers/sources.
#
#########################################################################################

Import-LocalizedData -BindingVariable LocalizedData -filename MSFT_PackageManagement.strings.psd1

Import-Module -Name "$PSScriptRoot\..\PackageManagementDscUtilities.psm1"

function Get-TargetResource
{
    <#
    .SYNOPSIS

    This DSC resource provides a mechanism to download and install packages on a computer. 

    Get-TargetResource returns the current state of the resource.

    .PARAMETER Name
    Specifies the name of the Package to be installed or uninstalled.

    .PARAMETER Source
    Specifies the name of the package source where the package can be found.
    This can either be a URI or a source registered with Register-PackageSource cmdlet.
    The DSC resource MSFT_PackageManagementSource can also register a package source.
    
    .PARAMETER RequiredVersion
    Specifies the exact version of the package that you want to install. If you do not specify this parameter, 
    this DSC resource installs the newest available version of the package that also satisfies any
    maximum version specified by the MaximumVersion parameter.

    .PARAMETER MaximumVersion
    Specifies the maximum allowed version of the package that you want to install. If you do not specify this parameter, 
    this DSC resource installs the highest-numbered available version of the package.

    .PARAMETER MinimumVersion
    Specifies the minimum allowed version of the package that you want to install. If you do not add this parameter, 
    this DSC resource intalls the highest available version of the package that also satisfies any maximum 
    specified version specified by the MaximumVersion parameter.

    .PARAMETER SourceCredential
    Specifies a user account that has rights to install a package for a specified package provider or source.

    .PARAMETER ProviderName
    Specifies a package provider name to which to scope your package search. You can get package provider names 
    by running the Get-PackageProvider cmdlet.

    .PARAMETER AdditionalParameters
    Provider specific parameters that are passed as an Hashtable. For example, for NuGet provider you can
    pass additional parameters like DestinationPath.
    #>

    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter()]
        [System.String]
        $RequiredVersion,
        
        [Parameter()]
        [System.String]
        $MinimumVersion,

        [Parameter()]
        [System.String]
        $MaximumVersion,

        [Parameter()]
        [System.String]
        $Source,

        [Parameter()]
        [PSCredential] $SourceCredential,

        [Parameter()]
        [System.String]
        $ProviderName,
        
        [Parameter()]
        [Microsoft.Management.Infrastructure.CimInstance[]]$AdditionalParameters        
    )
    
    $ensure = "Absent"
    $null = $PSBoundParameters.Remove("Source")
    $null = $PSBoundParameters.Remove("SourceCredential")

    if ($AdditionalParameters)
    {
         foreach($instance in $AdditionalParameters)
         {
             Write-Verbose ('AdditionalParameter: {0}, AdditionalParameterValue: {1}' -f $instance.Key, $instance.Value)
             $null = $PSBoundParameters.Add($instance.Key, $instance.Value)
         }
    }
    $null = $PSBoundParameters.Remove("AdditionalParameters")
    
    $verboseMessage =$localizedData.StartGetPackage -f (GetMessageFromParameterDictionary $PSBoundParameters),$env:PSModulePath
    Write-Verbose -Message $verboseMessage
    $result = PackageManagement\Get-Package @PSBoundParameters -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
        

    if ($result.count -eq 1)
    {
        Write-Verbose -Message ($localizedData.PackageFound -f $Name)
        $ensure = "Present"
    }
    elseif ($result.count -gt 1)
    {
        Write-Verbose -Message ($localizedData.MultiplePackagesFound -f $Name)
        $ensure = "Present"
    }
    else
    {
        Write-Verbose -Message ($localizedData.PackageNotFound -f $($Name))
    }

    Write-Debug -Message "Source $($Name) is $($ensure)"
                         
    
    if ($ensure -eq 'Absent')
    {
        return @{
            Ensure       = $ensure
            Name         = $Name
            ProviderName = $ProviderName
            RequiredVersion = $RequiredVersion
            MinimumVersion = $MinimumVersion
            MaximumVersion = $MaximumVersion
        }
    }
    else
    {
        if ($result.Count -gt 1)
        {
          $result = $result[0]
        }

        return  @{
                Ensure             = $ensure
                Name               = $result.Name
                ProviderName       = $result.ProviderName
                Source             = $result.source
                RequiredVersion    = $result.Version
            } 
    } 
}

function Test-TargetResource
{
    <#
    .SYNOPSIS

    This DSC resource provides a mechanism to download and install packages on a computer. 

    Test-TargetResource returns a boolean which determines whether the resource is in
    desired state or not.

    .PARAMETER Name
    Specifies the name of the Package to be installed or uninstalled.

    .PARAMETER Source
    Specifies the name of the package source where the package can be found.
    This can either be a URI or a source registered with Register-PackageSource cmdlet.
    The DSC resource MSFT_PackageManagementSource can also register a package source.
    
    .PARAMETER RequiredVersion
    Specifies the exact version of the package that you want to install. If you do not specify this parameter, 
    this DSC resource installs the newest available version of the package that also satisfies any
    maximum version specified by the MaximumVersion parameter.

    .PARAMETER MaximumVersion
    Specifies the maximum allowed version of the package that you want to install. If you do not specify this parameter, 
    this DSC resource installs the highest-numbered available version of the package.

    .PARAMETER MinimumVersion
    Specifies the minimum allowed version of the package that you want to install. If you do not add this parameter, 
    this DSC resource intalls the highest available version of the package that also satisfies any maximum 
    specified version specified by the MaximumVersion parameter.

    .PARAMETER SourceCredential
    Specifies a user account that has rights to install a package for a specified package provider or source.

    .PARAMETER ProviderName
    Specifies a package provider name to which to scope your package search. You can get package provider names 
    by running the Get-PackageProvider cmdlet.

    .PARAMETER AdditionalParameters
    Provider specific parameters that are passed as an Hashtable. For example, for NuGet provider you can
    pass additional parameters like DestinationPath.
    #>

    [CmdletBinding()]
    [OutputType([bool])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter()]
        [System.String]
        $RequiredVersion,
        
        [Parameter()]
        [System.String]
        $MinimumVersion,

        [Parameter()]
        [System.String]
        $MaximumVersion,

        [Parameter()]
        [System.String]
        $Source,

        [Parameter()]
        [PSCredential] $SourceCredential,
                
        [ValidateSet("Present","Absent")]
        [System.String]
        $Ensure="Present",

        [Parameter()]
        [System.String]
        $ProviderName,
        
        [Parameter()]
        [Microsoft.Management.Infrastructure.CimInstance[]]$AdditionalParameters         
    )

    
    Write-Verbose -Message ($localizedData.StartTestPackage -f (GetMessageFromParameterDictionary $PSBoundParameters))
    $null = $PSBoundParameters.Remove("Ensure")
    
    $temp = Get-TargetResource @PSBoundParameters

    if ($temp.Ensure -eq $ensure)
    {
        Write-Verbose -Message ($localizedData.InDesiredState -f $Name, $Ensure, $temp.Ensure)            
        return $True 
    }
    else
    {
        Write-Verbose -Message ($localizedData.NotInDesiredState -f $Name,$ensure,$temp.ensure)            
        return [bool] $False
    }    
}

function Set-TargetResource
{
    <#
    .SYNOPSIS

    This DSC resource provides a mechanism to download and install packages on a computer. 

    Set-TargetResource either intalls or uninstall a package as defined by the vaule of Ensure parameter.

    .PARAMETER Name
    Specifies the name of the Package to be installed or uninstalled.

    .PARAMETER Source
    Specifies the name of the package source where the package can be found.
    This can either be a URI or a source registered with Register-PackageSource cmdlet.
    The DSC resource MSFT_PackageManagementSource can also register a package source.
    
    .PARAMETER RequiredVersion
    Specifies the exact version of the package that you want to install. If you do not specify this parameter, 
    this DSC resource installs the newest available version of the package that also satisfies any
    maximum version specified by the MaximumVersion parameter.

    .PARAMETER MaximumVersion
    Specifies the maximum allowed version of the package that you want to install. If you do not specify this parameter, 
    this DSC resource installs the highest-numbered available version of the package.

    .PARAMETER MinimumVersion
    Specifies the minimum allowed version of the package that you want to install. If you do not add this parameter, 
    this DSC resource intalls the highest available version of the package that also satisfies any maximum 
    specified version specified by the MaximumVersion parameter.

    .PARAMETER SourceCredential
    Specifies a user account that has rights to install a package for a specified package provider or source.

    .PARAMETER ProviderName
    Specifies a package provider name to which to scope your package search. You can get package provider names 
    by running the Get-PackageProvider cmdlet.

    .PARAMETER AdditionalParameters
    Provider specific parameters that are passed as an Hashtable. For example, for NuGet provider you can
    pass additional parameters like DestinationPath.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter()]
        [System.String]
        $RequiredVersion,
        
        [Parameter()]
        [System.String]
        $MinimumVersion,

        [Parameter()]
        [System.String]
        $MaximumVersion,

        [Parameter()]
        [System.String]
        $Source,

        [Parameter()]
        [PSCredential] $SourceCredential,

        [ValidateSet("Present","Absent")]
        [System.String]
        $Ensure="Present",

        [Parameter()]
        [System.String]
        $ProviderName,
        
        [Parameter()]
        [Microsoft.Management.Infrastructure.CimInstance[]]$AdditionalParameters        
    )

    Write-Verbose -Message ($localizedData.StartSetPackage -f (GetMessageFromParameterDictionary $PSBoundParameters))
    
    $null = $PSBoundParameters.Remove("Ensure")
    
    if ($AdditionalParameters)
    {
         foreach($instance in $AdditionalParameters)
         {
             Write-Verbose ('AdditionalParameter: {0}, AdditionalParameterValue: {1}' -f $instance.Key, $instance.Value)
             $null = $PSBoundParameters.Add($instance.Key, $instance.Value)
         }
    }

    $PSBoundParameters.Remove("AdditionalParameters")

       
        # We do not want others to control the behavior of ErrorAction
        # while calling Install-Package/Uninstall-Package.
        $PSBoundParameters.Remove("ErrorAction")
        if ($Ensure -eq "Present")
        {
            PackageManagement\Install-Package @PSBoundParameters -ErrorAction Stop
        }   
        else
        {
            # we dont source location for uninstalling an already
            # installed package
            $PSBoundParameters.Remove("Source")
            # Ensure is Absent
            PackageManagement\Uninstall-Package @PSBoundParameters -ErrorAction Stop
        }
 }
 
 function GetMessageFromParameterDictionary
 {
    <#
        Returns a strng of form "ParameterName:ParameterValue"
        Used with Write-Verbose message. The input is mostly $PSBoundParameters
    #>
    param([System.Collections.IDictionary] $paramDictionary)

    $returnValue = ""
    $paramDictionary.Keys | ForEach-Object { $returnValue += "-{0} {1} " -f $_,$paramDictionary[$_] }
    return $returnValue
 }

Export-ModuleMember -function Get-TargetResource, Set-TargetResource, Test-TargetResource

