# encoding: ascii
# api: powershell
# title: Get Function Parameters
# description: This function will return all the parameters defined in a param() portion of a script as well as any default values, variable type information, HelpMessage             text, ValidateSet items, and mandatory settings if present.
# version: 1.1
# type: function
# author: Zachary Loeber
# license: CC0
# function: Get-FunctionParameters
# x-poshcode-id: 4879
# x-archived: 2014-02-07T23:48:01
# x-published: 2014-02-04T16:17:00
#
#
Function Get-FunctionParameters
{
    <#
        .SYNOPSIS
            Return all parameter elements from a function param() block.
       
           	Zachary Loeber
        	
        	THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE 
        	RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
        	
        	Version 1.1 - 01/27/2014
        .DESCRIPTION
            This function will return all the parameters defined in a param() portion of a
            script as well as any default values, variable type information, HelpMessage 
            text, ValidateSet items, and mandatory settings if present.
        .PARAMETER ScriptBlock
            A scriptblock containing a parameter set
        .EXAMPLE
            Get-FunctionParameter $Script
        .EXAMPLE
            $testscript = Get-Content 'SampleFunction.ps1' -Raw
            $scriptparams = get-functionparameters -ScriptBlock $testscript
        .NOTES
            Author: Zachary Loeber

            Version History:
            1.1 - 01/27/2014
                - Added ability to return mandatory info as well as validateset
                  values.
            1.0 - 05/31/2013
                - First release
        .LINK 
            http://www.the-little-things.net 
        .LINK
            http://nl.linkedin.com/in/zloeber
    #>
    [CmdletBinding()]
    param
    (
        [Parameter( Mandatory=$true,
                    ValueFromPipeline=$false,
                    HelpMessage="A scriptblock containing a parameter set.")]
        [String]$ScriptBlock
    )
    BEGIN
    {
        # Turn our string into a scriptblock
    	$ScriptBlock = [Scriptblock]::Create($ScriptBlock)
        $paramset = @()
        
        # Tokenize the script
        $tokens = [Management.Automation.PSParser]::Tokenize($ScriptBlock, [ref]$null) | 
                  Where {$_.Type -ne 'NewLine'}
    }
    PROCESS{}
    END
    {
        # First Pass - Grab all tokens between the first param block.
        $paramsearch = $false
        $groupstart = 0
        $groupend = 0
        for ($i = 0; $i -lt $tokens.Count; $i++) {
            if (!$paramsearch)
            {
                if ($tokens[$i].Content -eq "param" )
                {
                    $paramsearch = $true
                }
            }
            if ($paramsearch)
            {
                if (($tokens[$i].Type -eq "GroupStart") -and ($tokens[$i].Content -eq '(') )
                {
                    $groupstart++
                }
                if (($tokens[$i].Type -eq "GroupEnd") -and ($tokens[$i].Content -eq ')') )
                {
                    $groupend++
                }
                if (($groupstart -ge 1) -and ($groupstart -eq $groupend))
                {
                    $paramsearch = $false
                }
                $isparameter = $false
                $paramdatatype = ''
                $defaultvalue = ''
                if (($tokens[$i].Type -eq 'Variable') -and ($tokens[($i-1)].Content -ne '='))
                {
                    if ((($groupstart - $groupend) -eq 1))
                    {
                        $isparameter = $true
                    }
                }
                if ($isparameter)
                {
                    # if assigned, get the parameter default value
                    if (($tokens[($i+1)].Type -eq 'Operator') -and ($tokens[($i+1)].Content -eq '='))
                    {
                        $defaultvalue = $tokens[($i+2)].Content
                    }
                    
                    # Get the parameter data type
                    if (($tokens[($i-1)].Type -eq 'Type'))
                    {
                        $paramdatatype = $tokens[($i-1)].Content
                    }
                }
                $objprop = @{ 'Type' = $tokens[$i].Type;
                              'Content' = $tokens[$i].Content;
                              'IsParameter' = $isparameter;
                              'ParameterVariableType' = ($paramdatatype -replace '[^A-Za-z]','');
                              'ParamDefaultValue' = $defaultvalue;
                              'HelpMessage' = '';
                              'Mandatory' = $false;
                              'ValidateSet' = @();
                            }
                $newobj = New-Object PSObject -Property $objprop
                $paramset = $paramset + $newobj
            }
        }

        # Second Pass - Find all helpmessage and other parameter attributes
        #               associated with each parameter in the block
        $i=0
        Foreach ($param_item in $paramset)
        {
            if ($param_item.IsParameter)
            {
                # Once we find a parameter we essentially do a backward search
                # until we either hit another parameter or the beginning of the
                # entire param block.
                $lookforhelptext = $true
                $helpsearch = $i
                While ($lookforhelptext)
                {
                    $helpsearch--
                    if(($paramset[$helpsearch].IsParameter) -or 
                       (($paramset[$helpsearch].Type -eq 'Keyword') -and 
                        ($paramset[$helpsearch].Content -eq 'param')) -or 
                       ($helpsearch -eq 0))
                    {
                        $lookforhelptext = $false
                    }
                    # Get help message
                    elseif (($paramset[$helpsearch].Content -eq 'HelpMessage') -and 
                            ($paramset[$helpsearch].Type -eq 'Member'))
                    {
                        $param_item.HelpMessage = $paramset[($helpsearch+2)].Content
                    }
                    # Get mandatory state
                    elseif (($paramset[$helpsearch].Content -eq 'Mandatory') -and 
                            ($paramset[$helpsearch].Type -eq 'Member'))
                    {
                        $param_item.Mandatory = ($paramset[($helpsearch+2)].Content -eq 'true')
                    }
                    # Get our validateset items
                    elseif (($paramset[$helpsearch].Content -eq 'ValidateSet') -and 
                            ($paramset[$helpsearch].Type -eq 'Attribute'))
                    {
                        $foundallinset = $false
                        $vssetsearch = $helpsearch+2
                        do
                        {
                            if ($paramset[$vssetsearch].Type -ne 'Operator')
                            {
                                $param_item.ValidateSet += $paramset[$vssetsearch].Content
                            }
                            $vssetsearch++
                        } Until ($paramset[$vssetsearch].Type -eq 'GroupEnd')
                    }
                }
            }
            $i++
        }
        $paramset | where {($_.IsParameter)}
    }
}
