<#
  .Notes
	Copyright (c) 2013-2014, Intel Corporation. All rights reserved.
  .Synopsis 
	Sends a command (and the required command input data) to the specified solution component
  .Description
	Sends a command (and the required command input data) to the specified solution component.
	If the command is specified as a number, the cmdlet will send the command as is.
	If specified by name, the cmdlet runs solution discovery to verify the command exists and then gets the command ID before applying the command.

  .Example
	.\SCS-ApplyCommand -ComputerName MyHost.Domain.com -Component 0b93a9cb-64b1-43ed-bc33-e5b0b8692694 -Command "command"
  .Outputs
	An object of the form @{ Output = <data> ; Details = <details> } where each field is a byte array containing the output data or detailed data returned for the command

#>
[CmdletBinding()]
Param(
	[Parameter(Position=0,Mandatory=$True, HelpMessage='Fully qualified component identifer of the solution component on which to perform the operation')][string]$Component,
	[Parameter(Mandatory=$False, HelpMessage='Address of the computer on which to perform the operation (default: localhost)')][string]$ComputerName = $env:ComputerName,
	[Parameter(Mandatory=$False, HelpMessage='Credentials to use for authenticating to the Solutions Framework (default: current user)')][Object]$Credential = $null,
	[Parameter(Position=1,Mandatory=$True,  HelpMessage='Command to apply, either the name or a numeric ID')][Object]$Command,
	[Parameter(Position=2,Mandatory=$False, HelpMessage='Data to send in the command (must be a byte array)')][byte[]]$Data = @(),
	[Parameter(Mandatory=$False, HelpMessage='The number of seconds to wait before the operation times out. The default value is 60 seconds. Use 0 to specify an infinite waiting period.')][UInt32]$TimeoutInSeconds = 60
)

function GetCommandsForComponent {
	Param(
		[string]$component,
		[xml]$element
	)

	if (!$component) { # We should be here
		return $me.Commands
	}

	($current, $deeper) = $component.split(".",2)
	foreach ($me in $element.ManagedElement) {
		if ($me.'component-id' -eq $current) {
			return GetCommandsForComponent($deeper,$me) # Found something
		}
	}
	return $null # Didn't find
}

$filter = $(
	if ($Component -ne "") {
		if ($Component -Match '%') {
			"ComponentID LIKE ""$Component"""
		} else {
			"ComponentID = ""$Component"""
		}
	} else {
		""
	}
)

$SCS_ManagedElement = $( # Retrieve the specified ManagedElement
    if ($Credential) {
        Get-WMIObject -Computer $ComputerName -Credential $cred -Authentication 6 -Errorvariable err -Namespace Root\Intel_SCS_Framework -Class SCS_ManagedElement -Filter $filter
    } else {
        Get-WMIObject -Computer $ComputerName -Authentication 6 -Errorvariable err -Namespace Root\Intel_SCS_Framework -Class SCS_ManagedElement -Filter $filter
    }
) | Select -First 1 # If the filter returns multiple components, the cmdlet will run on only the first component

if ($SCS_ManagedElement -eq $null) {
	if($err.Count -gt 0) {$mg=$err[0];} else { $mg="";}
	Write-Error "Failed to load component $Component with error: $mg"
	exit 1
}

if ($Command.GetType().Name -eq "String") {
	# Need to perform discovery to get the command ID
	$method = "SolutionDiscovery"
	$params = $SCS_ManagedElement.GetMethodParameters($method)
	try {
		$ret = $SCS_ManagedElement.InvokeMethod($method, $params, $null)
	} catch [Exception] {
		Write-Error "Failed to execute the $method command with message: $_.Exception.Message"
		Write-Host "Please check the event log for more details."
		exit $_.Exception.ErrorCode
	}
	if ($ret.ReturnValue -ne 0) {
		Write-Error "Error resolving the command name: Failed to run solution discovery."
		exit $ret.ReturnValue
	}
	[xml]$discovery = $params.DiscoveryData
	$found = $False
	$commands = GetCommandsForComponent($SCS_ManagedElement.ComponentID, $discovery.Management)
	if ($commands -and $commands.Command) { 
		$commands.Command | %{
			if ($_.'#text' -eq $Command) {
				$Command = [Int32]$_.'command-id'
				$found = $True
			}
		}
	}
	if (!$found) {
		Write-Error "The specified command is not supported by component: $SCS_ManagedElement.ComponentID"
		exit 1
	}
}

$method = "ApplyCommandSync"
$params = $SCS_ManagedElement.GetMethodParameters($method)
$params.CommandID = $Command
$params.InData = $Data
$params.TimeoutInSeconds = $TimeoutInSeconds
try {
	$ret = $SCS_ManagedElement.InvokeMethod($method, $params, $null)
} catch [Exception] {
	Write-Error "Failed to execute the $method command with message: $_.Exception.Message"
	Write-Host "Please check the event log for more details."	
	exit $_.Exception.ErrorCode
}
$exitcode = $ret.ReturnValue

@{
	Output = $ret.OutData
	Details = $ret.OutDetailData
}

exit $exitcode
