FogApi.psm1

$PSModuleRoot = $PSScriptRoot
$script:lib = "$PSModuleRoot\lib"
$script:tools = "$PSModuleRoot\tools"
function Add-FogHostMac {
# .ExternalHelp FogApi-help.xml
    
    [CmdletBinding()]
    param ( 
        $hostID,
        $macAddress,
        [switch]$primary,
        [switch]$ignoreMacOnClient,
        [switch]$ignoreMacForImaging,
        [switch]$forceUpdate
    )

    process {
        $hostID = Resolve-HostID $hostID
        if ($null -ne $hostID) {
            if ($primary) {
                $primaryVal = '1'
            } else {
                $primaryVal = '0'
            }
            if ($ignoreMacForImaging) {
                $imageIgnoreVal = '1'
            } else {
                $imageIgnoreVal = '0'
            }
            if ($ignoreMacOnClient) {
                $clientIgnoreVal = '1'
            } else {
                $clientIgnoreVal = '0'
            }
            $newMac = @{
                hostID       = "$hostID"
                mac          = "$macAddress"
                description  = " "
                pending      = '0'
                primary      = $primaryVal
                clientIgnore = $clientIgnoreVal
                imageIgnore  = $imageIgnoreVal
            }
            $macExists = Get-FogMacAddresses | Where-Object mac -eq $macAddress
            if ($null -ne $macExists) {
                if ($forceUpdate) {
                    Write-Warning "this mac already exists in fog!, updating it to be assigned to this host instead"
                    Write-Verbose "Old mac definition was $($macExists | out-string). Updating to $($newMac | Out-String)";
                    return Update-FogObject -type object -coreObject macaddressassociation -jsonData ($newMac | ConvertTo-Json) -IDofObject $macExists.id
                } else {
                    Write-Warning "This mac already exists in fog! It is attached to the host named: $((get-foghost -hostID $macExists.hostID).name)"
                }
    
            } else {
                return New-FogObject -type object -coreObject macaddressassociation -jsonData ($newMac | ConvertTo-Json)
            }
        } else {
            Write-Error "provided hostid was invalid!"
            return $null;
        }
    }
    
}

function Add-FogResultData {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    param ( 
        [Parameter()]
        $result
    )
    
    process {
        #test if result has data property
        if ($null -ne $result) {
            if ($null -eq ($result | Get-Member -Name data -ea 0)) {
                #result doesn't have data property
                $property = ($result | get-member -MemberType NoteProperty | Where-Object name -notmatch 'count').name
                $newResult = [PSCustomObject]@{
                    count = $result.count;
                    data = $result.$property;
                    $property = $result.$property;
                }
                $result = $newResult;
                # $result | Add-Member -MemberType NoteProperty -Name data -Value $result.$property -Force
            }
        }
        return $result;
    }
    
    
}
function Approve-FogPendingMac {
# .ExternalHelp FogApi-help.xml
    
    [CmdletBinding()]
    param ( 
        [object]$macObject
    )

    process {
        $macObject.pending = '0';
        $data = ($macObject | ConvertTo-Json);
        $result = Update-FogObject -type object -coreObject macaddressassociation -IDofObject $macObject.id -jsonData $data 
        return $result;
    }
    
}

function Deny-FogPendingMac {
# .ExternalHelp FogApi-help.xml
    
    [CmdletBinding()]
    [Alias('Remove-FogMac')]
    param ( 
        [Parameter(Mandatory=$true)]        
        $macObject
    )

    process {
        return Remove-FogObject -type object -coreObject macaddressassociation -IDofObject $macObject.id;
    }
    
}

function Find-FogObject {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    param (
        # The type of object being requested, should be search
        [Parameter(Position=0)]
        [ValidateSet("search")]
        [string]$type = "search",
        # The string to search all fields for
        [Parameter(Position=2)]
        [string]$stringToSearch
    )

    DynamicParam { $paramDict = Set-DynamicParams $type; return $paramDict;}

    begin {
        $paramDict | ForEach-Object { New-Variable -Name $_.Keys -Value $($_.Values.Value);}
        # $paramDict;
        Write-Verbose "Building uri and api call for $($paramDict.keys) $($paramDict.values.value)";
        switch ($type) {
            search {
                if ($coreObject -ne "unisearch") {
                    $uri = "$coreObject/$type/$stringToSearch";
                } else {
                    $uri = "$coreObject/$stringToSearch"
                }
            }
        }
        Write-Verbose "uri for get is $uri";
        $apiInvoke = @{
            uriPath=$uri;
            Method="GET";
        }
    }

    process {
        $result = Invoke-FogApi @apiInvoke;
    }
    
    end {
        #convert the output to use the data property added in fog 1.6
        if ($coreObject -ne "unisearch") {
            $result = Add-FogResultData $result;
        } else {
            "Search Results: `n" | Out-Host;
            "$($result._results | out-string)" | out-host; 
        }
        return $result;
    }

}

function Get-FogActiveTasks {
    # .ExternalHelp FogApi-help.xml
    
    [CmdletBinding()]
    param ()
    
    process {
        $result = Get-FogObject -type objectactivetasktype -coreActiveTaskObject task
        return $result.data;
    }
}
function Get-FogAssociatedSnapins {
# .ExternalHelp FogApi-help.xml
    
    [CmdletBinding()]
    param (
        $hostId=((Get-FogHost).id)
    )
    
    process {
        # $AllAssocs = (Invoke-FogApi -Method GET -uriPath snapinassociation).snapinassociations;
        $AllAssocs = (Get-FogObject -type object -coreObject snapinassociation).data
        $snapins = New-Object System.Collections.Generic.List[object];
        # $allSnapins = Get-FogSnapins;
        $AllAssocs | Where-Object hostID -eq $hostID | ForEach-Object {
            $snapinID = $_.snapinID;
            $snapin = Get-FogObject -type object -coreObject snapin -IDofObject $snapinID;
            # $snapins.add((Invoke-FogApi -uriPath "snapin\$snapinID"))
            $snapins.add($snapin)

        }
        return $snapins;
    }
    
}
function Get-FogGroupAssociations {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    param ()
    
    process {
        $groupAssocs = (Get-FogObject -type object -coreObject groupassociation);
        return $groupAssocs.data;
    }
    
}
function Get-FogGroupByName {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    param (
        $groupName
    )
    
    process {
        
        $group = (Find-FogObject -type search -coreobject group -stringToSearch $groupName);
        $group = $group.data | Where-Object name -match $groupName;
        return $group;      
    }
    
}
function Get-FogGroups {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    param ()
    
    
    process {
        $groups = (Get-FogObject -type object -coreobject group);
        return $groups.data;
    }
    
}
function Get-FogHost {
# .ExternalHelp FogApi-help.xml
    
    [CmdletBinding(DefaultParameterSetName='searchTerm')]
    param (
        [parameter(ParameterSetName='searchTerm')]
        [string]$uuid,
        [parameter(ParameterSetName='searchTerm')]
        [string]$hostName,
        [parameter(ParameterSetName='searchTerm')]
        [string]$macAddr,
        [parameter(ParameterSetName='byID',Mandatory=$true)]
        [string]$hostID,
        [parameter(ParameterSetName='serialNumber',Mandatory=$true)]
        [string]$serialNumber
    )

    begin {
        [bool]$found = $false;
        Write-Verbose 'Checking for passed variables'
        if ($serialNumber) {
            $inventorys = (Get-FogObject -type object -coreObject inventory).data
            $hostID = $inventorys | Where-Object { $_.sysserial -eq $serialNumber -OR $_.mbserial -eq $serialNumber -OR $_.caseserial -eq $serialNumber } | Select-Object -ExpandProperty HostID #find the inventory where the serial number matches one of the serial numbers in a hosts inventory and select the host id from that
        } elseif (!$uuid -and !$hostName -and !$macAddr -and !$hostID) {
            Write-Verbose 'no params given, getting current computer variables';
            try {
                $compSys = Get-CimInstance -ClassName win32_computersystemproduct
            } catch {
                $compSys = (Get-WmiObject Win32_ComputerSystemProduct);
            }
            if ($compSys.UUID -notmatch "12345678-9012-3456-7890-abcdefabcdef" ) {
                $uuid = $compSys.UUID;
            } else {
                $uuid = ($compSys.Qualifiers | Where-Object Name -match 'UUID' | Select-Object -ExpandProperty Value);
            }
            # $macAddr = ((Get-NetAdapter | Select-Object MacAddress)[0].MacAddress).Replace('-',':');
            $make = Get-CimInstance -classname win32_computersystem | Select-Object -ExpandProperty manufacturer
            if (($Make) -notmatch "vmware" ) { 
                $macAddr = (
                    Get-NetAdapter | Where-Object { 
                        $_.Status -eq 'up' -And $_.Name -notmatch 'VMware'
                    } | Select-Object -first 1 | Select-Object -expand MacAddress
                ).Replace("-",":");
            } else {
                $macAddr = (
                    Get-NetAdapter | Where-Object { 
                        $_.Status -eq 'up'
                    } | Select-Object -first 1 | Select-Object -expand MacAddress
                ).Replace("-",":");
            }
            $hostName = $(hostname);
        } else {
            if ($hostID) {
                Write-Verbose "getting host from ID $hostID directly..."
            }
        }
        Write-Verbose 'getting all hosts to search...';
        Write-Verbose "search terms: uuid is $uuid, macAddr is $macAddr, hostname is $hostName";
    }

    process {
        Write-Verbose 'finding host in hosts';
        [bool]$found = $false;
        if ($hostID) {
            $hostObj = Get-FogObject -type object -coreObject host -IDofObject "$hostID";
            if ($null -ne $hostObj) {
                $found = $true;
            }
        } else {
            $hosts = (Get-FogHosts)
            $hostObj = $hosts | Where-Object {
                ($uuid -ne "" -AND $_.inventory.sysuuid -eq $uuid) -OR `
                ($hostName -ne "" -AND $_.name -eq $hostName) -OR `
                ($macAddr -ne "" -AND $_.macs -contains $macAddr);
                if  ($uuid -ne "" -AND $_.inventory.sysuuid -eq $uuid) {
                    $found = $true;
                    Write-Verbose "$($_.inventory.sysuuid) matches the uuid $uuid`! host found is $found";
                }
                if ($macAddr -ne "" -AND $_.macs -contains $macAddr) {
                    Write-Verbose "$($_.macs) matches the macaddress $macAddr`! host found";
                    $found = $true;
                }
                if  ($hostName -ne "" -AND $_.name -eq $hostName) {
                    Write-Verbose "$($_.name) matches the hostname $hostName`! host found";
                    $found = $true;
                }
            }
        }
    }

    end {
        if ($found){
            if ($hostObj.count -gt 1) {
                if ($hostName) { 
                    $hostObjByName = $hostObj | Where-Object name -eq $hostName;
                    if ($null -ne $hostObjByName) {
                        $hostObj = $hostObjByName
                    } else {
                        "Multiple hosts found and none of them match given hostname! Review hosts in return object and select just one if needed" | Out-Host;
                    }
                } else {
                    "Multiple hosts found! Review hosts in return object and select just one if needed" | Out-Host;
                }
            }
            if ($null -eq ($hostObj | Get-Member -Name ADOU)) {
                Write-Verbose "Host is $($hostObj | out-string) which is missing adou, reobtaining with id"
                $hostID = $hostObj.id;
                $hostObj = (Get-Fogobject -type object -coreObject host -IDofObject "$hostID");
            }
            return $hostObj;
        }
        return $found; #return false if host not found
    }

}

function Get-FogHostGroup {
# .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    [Alias('Get-FogGroup')]
    param (
        [int]$hostId
    )
    
    process {
        if (!$hostId) {
            $hostId = (Get-FogHost).id;
        }
        $hostGroups = (Get-FogObject -Type object -coreObject groupassociation).data
        $hostGroups = $hostGroups | Where-Object hostID -eq $hostId;
        $groups = New-Object System.Collections.Generic.list[system.object];

        if ($hostGroups) {
            $hostGroups.groupID | ForEach-Object {
                $group = Get-FogObject -type object -coreObject group -IDofObject $_
                $groups.add(($group))
            }
        } else {
            $groups = $Null;
        }
        return $groups;
    }

}

function Get-FogHostMacs {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    [Alias('Get-MacsForHost')]
    param (
        [Parameter(ParameterSetName='byHostObject')]
        $hostObject = (Get-FogHost),
        [Parameter(ParameterSetName='byHostID')]
        $hostID
    )
    
    process {
        $hostID = Resolve-HostID $hostID
        if ($null -ne $hostID) {
            $macs = Get-FogMacAddresses;   
            $hostMacs = $macs | Where-Object hostID -eq $hostId
            return $hostMacs;   
        } else {
            Write-Error "invalid input!"
            return $null;
        }
    }
    
}
function Get-FogHostPendingMacs {
    # .ExternalHelp FogApi-help.xml
    
    [Alias('Get-PendingMacsForHost')]
    [CmdletBinding()]
    param (
        $hostID
    )
    
    
    process {
        $hostID = Resolve-HostID -hostID $hostid;
        if ($null -ne $hostID) {
            $hostMacs = Get-FogHostMacs -hostid $hostID;
            $pendingMacs = $hostMacs | Where-Object pending -eq '1';
        } else {
            Write-Error "provided hostid was invalid!"
        }
        return $pendingMacs;
    }
    
}
function Get-FogHosts {
# .ExternalHelp FogApi-help.xml
    
    [CmdletBinding()]
    param ()
    
    process {
        Write-Verbose "getting fog hosts"
        $hosts = (Get-FogObject -type Object -CoreObject host).data
        return $hosts;
    }

}

function Get-FogImages {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    param (  )
    
    process {
        return (Get-FogObject -type object -coreObject image).data;
    }
}
function Get-FogInventory {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    param (
        $hostObj = (Get-FogHost),
        [switch]$fromFog
    )

    process {
        if ($IsLinux -OR $fromFog) {
            if ($IsLinux) {
                "Not yet implemented for getting the host inventory from linux inline, returning the hosts currently set inventory object" | out-host;
            }
            return $hostObj.inventory;
        }
        else {
            $comp = Get-CimInstance -ClassName Win32_ComputerSystem;
            $compSys = Get-CimInstance -ClassName Win32_ComputerSystemProduct;
            $cpu = Get-CimInstance -ClassName Win32_processor;
            $bios = Get-CimInstance -ClassName Win32_Bios;
            $hdd = Get-CimInstance -ClassName Win32_DiskDrive | Where-Object DeviceID -match '0'; #get just drive 0 in case of multiple drives
            $baseBoard = Get-CimInstance -ClassName Win32_BaseBoard;
            $case = Get-CimInstance -ClassName Win32_SystemEnclosure;
            $info = Get-ComputerInfo;
        }
        $hostObj.inventory.hostID        = $hostObj.id;
        # $hostObj.inventory.primaryUser =
        # $hostObj.inventory.other1 =
        # $hostObj.inventory.other2 =
        $hostObj.inventory.createdTime   = $((get-date -format u).replace('Z',''));
        # $hostObj.inventory.deleteDate = '0000-00-00 00:00:00'
        $hostObj.inventory.sysman        = $compSys.Vendor; #manufacturer
        $hostObj.inventory.sysproduct    = $compSys.Name; #model
        $hostObj.inventory.sysversion    = $compSys.Version;
        $hostObj.inventory.sysserial     = $compSys.IdentifyingNumber;
        if ($compSys.UUID -notmatch "12345678-9012-3456-7890-abcdefabcdef" ) {
            $hostObj.inventory.sysuuid       = $compSys.UUID;
        } else {
            $hostObj.inventory.sysuuid       = ($compSys.Qualifiers | Where-Object Name -match 'UUID' | Select-Object -ExpandProperty Value);
        }
        $hostObj.inventory.systype       = $case.chassistype; #device form factor found chassistype member of $case but it references a list that hasn't been updated anywhere I can find. i.e. returns 35 for a minipc but documented list only goes to 24
        $hostObj.inventory.biosversion   = $bios.name;
        $hostObj.inventory.biosvendor    = $bios.Manufacturer;
        $hostObj.inventory.biosdate      = $(get-date $info.BiosReleaseDate -Format d);
        $hostObj.inventory.mbman         = $baseBoard.Manufacturer;
        $hostObj.inventory.mbproductname = $baseBoard.Product;
        $hostObj.inventory.mbversion     = $baseBoard.Version;
        $hostObj.inventory.mbserial      = $baseBoard.SerialNumber;
        $hostObj.inventory.mbasset       = $baseBoard.Tag;
        $hostObj.inventory.cpuman        = $cpu.Manufacturer;
        $hostObj.inventory.cpuversion    = $cpu.Name;
        $hostObj.inventory.cpucurrent    = "Current Speed: $($cpu.currentClockSpeed) MHz";
        $hostObj.inventory.cpumax        = "Max Speed $($cpu.MaxClockSpeed) MHz";
        $hostObj.inventory.mem           = "MemTotal: $($comp.TotalPhysicalMemory) kB";
        $hostObj.inventory.hdmodel       = $hdd.Model;
        $hostObj.inventory.hdserial      = $hdd.SerialNumber;
        $hostObj.inventory.hdfirmware    = $hdd.FirmareRevision;
        $hostObj.inventory.caseman       = $case.Manufacturer;
        $hostObj.inventory.casever       = $case.Version;
        $hostObj.inventory.caseserial    = $case.SerialNumber;
        $hostObj.inventory.caseasset     = $case.SMBIOSAssetTag;
        $hostObj.inventory.memory        = "$([MATH]::Round($(($comp.TotalPhysicalMemory) / 1GB),2)) GiB";
        $jsonData = $hostObj.inventory | ConvertTo-Json;
        return $jsonData;
    }


}

function Get-FogLog {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    param (
        [switch]$static,
        [switch]$userFogLog
    )
    
    process {
        if ($userFogLog) {
            $fogLog = "$home/.fog_user.log"
        } else {
            $fogLog = 'C:\fog.log';
            if (!Test-Path $fogLog) {
                $fogLog = C:\ProgramData\fog\fog.log
            }
        }
        if (!$static) {
            "Starting dynamic fog log in new window, Hit Ctrl+C on new window or close it to exit dynamic fog log" | Out-Host;
            Start-Process Powershell.exe -ArgumentList "-Command `"Get-Content $fogLog -Wait`"";
        }
        else {
            Get-Content $fogLog;
        }
        return $fogLog;
    }

}

function Get-FogMacAddresses {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    [Alias('Get-FogMacs')]
    param ()
    
    
    process {
        $macs = Get-FogObject -type object -coreObject macaddressassociation;
        return $macs.data;
    }
    
}
function Get-FogModules {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    param ()
    
    process {
        $modules = (Get-FogObject -type object -coreObject module)
        return $modules.data;
    }
    
}
function Get-FogObject {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    param (
        # The type of object being requested
        [Parameter(Position=0)]
        [ValidateSet("objectactivetasktype","object")]
        [string]$type,
        # The json data for the body of the request
        [Parameter(Position=2)]
        [Object]$jsonData,
        # The id of the object to get
        [Parameter(Position=3)]
        [string]$IDofObject
    )

    DynamicParam { $paramDict = Set-DynamicParams $type; return $paramDict;}

    begin {
        $paramDict | ForEach-Object { New-Variable -Name $_.Keys -Value $($_.Values.Value);}
        # $paramDict;
        Write-Verbose "Building uri and api call for $($paramDict.keys) $($paramDict.values.value)";
        switch ($type) {
            objectactivetasktype {
                $uri = "$coreActiveTaskObject/current";
            }
            object {
                if ($null -eq $IDofObject -OR $IDofObject -eq "") {
                    $uri = "$coreObject";
                }
                else {
                    $uri = "$coreObject/$IDofObject";
                }
            }
            # search {
            # if ($coreObject) {
            # $uri = "$coreObject/$type/$stringToSearch";
            # } else {
            # $uri = "$type/$stringToSearch"
            # }
            # }
        }
        Write-Verbose "uri for get is $uri";
        $apiInvoke = @{
            uriPath=$uri;
            Method="GET";
            jsonData=$jsonData;
        }
        if ($null -eq $apiInvoke.jsonData -OR $apiInvoke.jsonData -eq "") {
            $apiInvoke.Remove("jsonData");
        }
    }

    process {
        $result = Invoke-FogApi @apiInvoke;
    }
    
    end {
        #if the api call wasn't for a specific object, convert the output to use the data property added in fog 1.6
        if (!$IDofObject) {
            $result = Add-FogResultData $result;
        }
        return $result;
    }

}

function Get-FogSecsSinceEpoch {
    # .ExternalHelp FogApi-help.xml
    param (
        $scheduleDate = (Get-Date)
    )
        process {
            $EpochDiff = New-TimeSpan "01 January 1970 00:00:00" $($scheduleDate)
            $EpochSecs = [INT] $EpochDiff.TotalSeconds - [timezone]::CurrentTimeZone.GetUtcOffset($(get-date)).totalseconds
            return $EpochSecs
        }
}
function Get-FogServerSettings {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    param ()

    process {
        Write-Verbose "Pulling settings from settings file"
        #since we updated the fog settings file path for linux and mac systems, make it auto migrate if the old settings exists
        $settingsFile = Get-FogServerSettingsFile;
        
        $move = $false;
        if ($isLinux -or $IsMacOS) {
            $oldsettingsFile = "$home/APPDATA/Roaming/FogApi/api-settings.json"
            if (Test-Path $oldsettingsFile) {
                $move = $true;
            }
        }

        if (!(Test-path $settingsFile)) {
            Write-Warning "Api Settings file not yet created, creating default version now!"
            try {
                #create just the parent path with built-in mkdir
                if (!(test-path (split-path $settingsFile -Parent))) {
                    mkdir (split-path $settingsFile -Parent)
                }
            } catch {
                #error creating parent path(s), looping through full path and using pwsh methods to create each part of the full path
                set-location "/"
                (split-path $settingsFile -Parent) -split "/" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object {
                    if (!(Test-Path $_)) {
                        new-item -path $_ -ItemType Directory -ea 0
                    }
                    Set-Location $_;
                }
            }
            if ($move) {
                Write-Host -BackgroundColor Yellow -ForegroundColor White -Object "The settings file path has changed from $oldSettingsFile to $SettingsFile. The permissions are also being updated to be more secure (rw for owner only)"
                $oldParentPath = Split-Path $oldsettingsFile -Parent;
                try {
                    Move-Item -path $oldsettingsFile -Destination $settingsFile -ea stop
                } catch {
                    Copy-Item -path $oldsettingsFile -Destination $settingsFile -ea 0 -force;
                    Write-Warning "there was an error moving the file, it was copied instead, please remove the $oldParentPath manually"
                }
                if ( ((Get-ChildItem $oldParentPath -force).count) -eq 0) {
                    Write-Host -BackgroundColor Yellow -ForegroundColor White -Object "The old settings parent directory is empty"
                    Remove-Item $oldParentPath -Force -Recurse;
                } else {
                    Write-Warning "The old settings path was not empty, please remove the $oldParentPath manually to stop these warnings"
                }
                Set-FogServerSettingsFileSecurity -settingsFile $settingsFile;
            } else {
                Copy-Item -path "$script:lib\settings.json" -Destination $settingsFile -Force
                Set-FogServerSettingsFileSecurity -settingsFile $settingsFile;
            }
        }       
        $serverSettings = (Get-Content $settingsFile | ConvertFrom-Json);
        return $serverSettings;
    }

}

function Get-FogServerSettingsFile {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    param (
            
    )
    
    
    process {
        if ($isLinux -or $IsMacOS) {
            $settingsFile = "$home/.FogApi/api-settings.json"
        } else {
            $settingsFile = "$env:APPDATA/FogApi/api-settings.json"
        }
        return $settingsFile
    }
    
}
function Get-FogSnapins {
# .ExternalHelp FogApi-help.xml
    
    [CmdletBinding()]
    param ()
    
    
    process {
        return (Get-FogObject -type object -coreObject snapin).data;
    }
    
    
}
function Get-LastImageTime {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding(DefaultParameterSetName="bySN")]
    param ( 
        [parameter(ParameterSetName='bySN')]
        $serialNumber, #scan the barcode input into powershell
        [parameter(ParameterSetName='byHostId')]
        $hostId,
        [parameter(ParameterSetName='byHost')]
        $fogHost,
        [parameter(ParameterSetName='byHost')]
        [switch]$currentHost
    )
    process {
        switch ($PSCmdlet.ParameterSetName) {
            bySN {
                Write-Verbose "Getting host by serial number" 
                if (!$serialNumber) {
                    $serialNumber = (Read-Host -Prompt "Scan Serial Number barcode")
                }
                $fogHost = Get-FogHost -serialNumber $serialNumber;
                $HostID = $fogHost.id;
            }
            byHostID {
                Write-Verbose "Getting host by id"
                $fogHost = Get-FogHost -hostID $hostId;
            }
            byHost {
                if (!$fogHost) {
                    Write-Verbose "getting host of current machine"
                    $fogHost = (Get-FogHost)
                }
                $HostID = $fogHost.id
            }
        }
        
        $imageLog = (get-fogobject -type object -coreObject imaginglog).data # get the image history log
        $hostLogs = $imageLog | where-object hostid -eq $HostID # get the image history logs for the given host
        if (!$hostLogs) {
            Write-Warning "No imaging logs found for host $($fogHost.name)!"
            return $null;
        } else {
            $hostLog = $hostLogs[-1] # select the last/most recent log
            #return a string of the information about the serial number
            if ($serialNumber) {
                "Serial number $serialNumber belongs to host $($fogHost.name), it was last imaged at $($hostLog.start) with the image $($hostLog.image)" | Out-Host
            } else {
                "hostname is $($fogHost.name), it was last imaged at $($hostLog.start) with the image $($hostLog.image)" | Out-Host
            }
            return $hostLog;
        } 
    }
}
function Install-FogService {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    param (
        $fogServer = ((Get-FogServerSettings).fogServer)
    )
    begin {
        $fileUrl = "http://$fogServer/fog/client/download.php?newclient";
        $fileUrl2 = "http://$fogServer/fog/client/download.php?smartinstaller";
        Write-Host "Making temp download dir";
        mkdir C:\fogtemp;
        Write-Host "downloading installer";
        Invoke-WebRequest -URI $fileUrl -UseBasicParsing -OutFile 'C:\fogtemp\fog.msi';
        Invoke-WebRequest -URI $fileUrl2 -UseBasicParsing -OutFile 'C:\fogtemp\fog.exe';
    }
    process {
        "installing fog service" | out-host;
        try {
            "Attempting silent msi install..." | out-host;
            Start-Process -FilePath msiexec -ArgumentList @('/i','C:\fogtemp\fog,msi','/qn') -NoNewWindow -Wait;
        } catch {
            "There was an error, attempting 'smart installer' and sending keystrokes to run through the wizard..." | out-host;
            if ($null -eq (Get-Service FogService -EA 0)) {
                & "C:\fogTemp\fog.exe";
                Write-Host "Waiting 10 seconds then sending 10 enter keys"
                Start-Sleep 10
                $wshell = New-Object -ComObject wscript.shell;
                $wshell.AppActivate('Fog Service Setup')            
                $wshell.SendKeys("{Enter}")
                $wshell.SendKeys("{Space}")
                $wshell.SendKeys("{Enter}")
                $wshell.SendKeys("{Enter}")
                $wshell.SendKeys("{Enter}")
                $wshell.SendKeys("{Enter}")
                Write-Host "waiting 30 seconds for service to install"
                Start-Sleep 30
                Write-host "sending more enter keys"
                $wshell.SendKeys("{Enter}")
                $wshell.SendKeys("{Enter}")
                $wshell.SendKeys("{Enter}")
                $wshell.SendKeys("{Enter}")
                $wshell.SendKeys("{Enter}")
            }
        }
    }
    end {
        Write-Host "removing download file and temp folder";
        Remove-Item -Force -Recurse C:\fogtemp;
        Write-Host "Starting fogservice";
        if ($null -ne (Get-Service FogService)) {
            Start-Service FOGService;
        }
        return;
    }

}

function Invoke-FogApi {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    param (
        [string]$uriPath,
        [string]$Method="GET",
        [string]$jsonData
    )

    begin {
        Write-Verbose "Pulling settings from settings file"
        # Set-FogServerSettings;
        $serverSettings = Get-FogServerSettings;

        [string]$fogApiToken = $serverSettings.fogApiToken;
        [string]$fogUserToken = $serverSettings.fogUserToken;
        [string]$fogServer = $serverSettings.fogServer;

        $baseUri = "http://$fogServer/fog";

        # Create headers
        Write-Verbose "Building Headers...";
        $headers = @{};
        $headers.Add('fog-api-token', $fogApiToken);
        $headers.Add('fog-user-token', $fogUserToken);

        # Set the Uri
        Write-Verbose "Building api call URI...";
        $uri = "$baseUri/$uriPath";
        $uri = $uri.Replace('//','/')
        $uri = $uri.Replace('http:/','http://')


        $apiCall = @{
            Uri = $uri;
            Method = $Method;
            Headers = $headers;
            Body = $jsonData;
            ContentType = "application/json"
        }
        if ($null -eq $apiCall.Body -OR $apiCall.Body -eq "") {
            Write-Verbose "removing body from call as it is null"
            $apiCall.Remove("Body");
        }
        # } else {
        # if (!($apiCall.Body.GetType() -eq "string")) {
        # $apiCall.Body = $apiCall.Body | ConvertTo-Json;
        # }
        # }

    }

    process {
        Write-Verbose "$Method`ing $jsonData to/from $uri";
        try {
            $result = Invoke-RestMethod @apiCall -ea Stop;
        } catch {
            $result = Invoke-WebRequest @apiCall;
        }
    }

    end {
        Write-Verbose "finished api call";
        return $result;
    }

}

function New-FogHost {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding(DefaultParameterSetName='default')]
    [Alias('Add-FogHost')]
    param (
        [Parameter(Mandatory=$true,ParameterSetName='default')]
        [string]$name,
        [Parameter(Mandatory=$true,ParameterSetName='default')]
        [string[]]$macs,
        [Parameter(ParameterSetName='default')]
        [string[]]$modules = ((Get-FogModules | Where-Object isDefault -eq '1') | Select-Object -ExpandProperty id),
        [Parameter(Mandatory=$true,ParameterSetName='custom')]
        [object]$customHost
    )
    
    begin {
        if ($PSCmdlet.ParameterSetName -eq 'default') {

            if ($name -match " ") {
                Write-Warning "name $name includes whitespace, replacing whitespace with - if not at end of string";
                $name = $name.TrimEnd();
                $name = $name.Replace(" ","-");
                "new name is $name" | Out-Host;
            }
            $hostObject = @{
                name = $name;
                macs = $macs;
                modules = $modules;
            }
        } else {
            $hostObject = $customHost;
            "Custom host is specified, no validation being performed, object properties are $($hostObject | Out-String)"
        }
        $hostJson = $hostObject | ConvertTo-Json;
        
    }
    
    process {
        if ($PSCmdlet.ParameterSetName -eq 'default') {
            try {
                Write-Verbose "Checking for existing name"
                $hostNameCheck = Get-FogHost -hostName $name;
                if ($hostNameCheck) {
                    Write-Warning "host with name $name already exists, returning existing host object";
                    return $hostNameCheck;                     
                }
                $hostMacCheck = New-Object System.Collections.Generic.list[object];
                $macs | ForEach-Object {
                    "Checking for existings host with mac $($_)" | Out-Host;
                    $hostMacCheckItem = (Get-FogHost -macAddr $_);
                    if ($hostMacCheckItem) {
                        Write-Warning "mac address $($_) already exists on host $($hostMacCheckItem.name)";
                        $hostMacCheck.add(($hostMacCheckItem));
                    }
                }
                if ($hostMacCheck.count -gt 0) {
                    Write-Warning "some macs already exist on hosts, returning list of matching host objects";
                    return $hostMacCheck;
                }

            } catch {
                Write-Warning "Error during input validation, attempting to create new host"
            }
        }
        "Creating new Host $hostObject" | Out-Host;
        
        $newHost = New-FogObject -type object -coreObject host -jsonData $hostJson
    }
    
    end {
        if ($null -ne $newHost) {
            return $newHost;
        }
    }
}
function New-FogObject {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    [Alias('Add-FogObject')]
    param (
        # The type of object being requested
        [Parameter(Position=0)]
        [ValidateSet("objecttasktype","object")]
        [string]
        $type,
        # The json data for the body of the request
        [Parameter(Position=2)]
        [Object]$jsonData,
        # The id of the object when creating a new task
        [Parameter(Position=3)]
        [string]$IDofObject
    )

    DynamicParam { $paramDict = Set-DynamicParams $type; return $paramDict;}

    begin {
        $paramDict | % { New-Variable -Name $_.Keys -Value $($_.Values.Value);}
        Write-Verbose "Building uri and api call";
        switch ($type) {
            objecttasktype {
                $uri = "$CoreTaskObject/$IDofObject/task";
            }
            object {
                $uri = "$CoreObject";
            }
        }
        $apiInvoke = @{
            uriPath=$uri;
            Method="POST";
            jsonData=$jsonData;
        }
    }

    process {
        $result = Invoke-FogApi @apiInvoke;
    }

    end {
        return $result;
    }

}

function Receive-FogImage {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding(DefaultParameterSetName='now')]
    [Alias('Save-FogImage','Invoke-FogImageCapture','Capture-FogImage','Pull-FogImage')]
    param (
        [Parameter(ParameterSetName='now')]
        [Parameter(ParameterSetName='schedule')]
        $hostId,
        [Parameter(ParameterSetName='schedule')]
        [datetime]$StartAtTime
    )
    
    
    process {
        $fogHost = Get-FogHost -hostID $hostId;
        $currentImage = $fogHost.imageName;
        $fogImages = Get-FogImages;
        $fogImage = ($fogImages | Where-Object name -match $currentImage)
        "Creating Capture Task for fog host of id $($hostID) named $($fogHost.name)" | Out-Host;
        "Will capture the assigned image $($fogImage.name) - $($fogImage.id) which will capture the os $($fogImage.osname)" | Out-host;
        if ($PSCmdlet.ParameterSetName -eq 'now') {
            "No Time was specified, queuing the task to start now" | out-host;
            $jsonData = "{`"taskTypeID`": `"2`" }";
        } else {
            "Start time of $($StartAtTime) specified, scheduling the task to start at that time" | out-host;
            $scheduleTime = Get-FogSecsSinceEpoch -scheduleDate $StartAtTime
            $runTime = get-date $StartAtTime -Format "yyyy-M-d HH:MM"
            $jsonData = @"
                    {
                        "name":"Capture Task",
                        "type":"S",
                        "taskTypeID":"2",
                        "runTime":"$runTime",
                        "scheduleTime":"$scheduleTime",
                        "isGroupTask":"0",
                        "hostID":"$($hostId)",
                        "shutdown":"0",
                        "other2":"0",
                        "other4":"1",
                        "isActive":"1"
                    }
"@

        }
        return New-FogObject -type objecttasktype -coreTaskObject host -jsonData $jsonData -IDofObject "$hostId";
    }
    
}
function Remove-FogObject {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    param (
        # The type of object being requested
        [Parameter(Position=0)]
        [ValidateSet("objecttasktype","objectactivetasktype","object")]
        [string]$type,
        # The json data for the body of the request
        [Parameter(Position=2)]
        [Object]$jsonData,
        # The id of the object to remove
        [Parameter(Position=3)]
        [string]$IDofObject
    )

    DynamicParam { $paramDict = Set-DynamicParams $type; return $paramDict;}

    begin {
        $paramDict | ForEach-Object { New-Variable -Name $_.Keys -Value $($_.Values.Value);}
        Write-Verbose "Building uri and api call";
        switch ($type) {
            objecttasktype {
                $uri = "$CoreTaskObject/$IDofObject/cancel";
             }
            object {
                $uri = "$CoreObject/$IDofObject/delete";
            }
            objectactivetasktype {
                $uri = "$CoreActiveTaskObject/cancel"
            }
        }
        $apiInvoke = @{
            uriPath=$uri;
            Method="DELETE";
            jsonData=$jsonData;
        }
        if ($apiInvoke.jsonData -eq $null -OR $apiInvoke.jsonData -eq "") {
            $apiInvoke.Remove("jsonData");
        }
    }

    process {
        $result = Invoke-FogApi @apiInvoke;
    }

    end {
        return $result;
    }

}

function Remove-UsbMac {
# .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    param (
        [string[]]$usbMacs,
        [string]$hostname = "$(hostname)",
        $macId
    )
        
    begin {
        if ($null -eq $usbMacs) {
            Write-Error "no macs to remove given";
            exit;   
        }
        Write-Verbose "remove usb ethernet adapter from host $hostname on fog server $fogServer ....";
        # get the host id by getting all hosts and searching the hosts array of the returned json for the item that has a name matching the current hostname and get the host id of that item
        $hostObj = Get-FogHost -hostName $hostname;
        $hostId = $hostObj.id;
        # $hostId = ( (Invoke-FogApi -fogServer $fogServer -fogApiToken $fogApiToken -fogUserToken $fogUserToken).hosts | Where-Object name -match "$hostname" ).id;
        # With the host id get mac associations that match that host id.
        $macs = (Get-FogObject -type object -coreObject macaddressassociation).data | Where-Object hostID -match $hostID

        # Copy the return fixedsize json array collection to a new powershell list variable for add and remove functions
        $macList = New-Object System.Collections.Generic.List[System.Object];
        try {
            $macs.ForEach({ $macList.add($_.mac); });
        } catch {
            $macs | ForEach-Object{
                $macList.add($_.mac);
            }
        }
    }

    process {
        # Check if any usbmacs are contained in the host's macs
        $usbMacs | ForEach-Object { #loop through list of usbMacs
            if ( $macList.contains($_) ) { # check if the usbMac is contained in the mac list of the host
                # Remove from the list so a new primary can be picked if needed
                $macList.Remove($_);

                Write-Verbose "$_ is a $usbMac connected to $hostname, checking if it is the primary...";
                $macItem = ($macs | Where-Object mac -eq $_ );

                if ( $macItem.primary -eq '1' ) {
                    Write-Verbose "It is primary, let's fix that and set $($macList[0]) to primary";
                    $macItem.primary = '0';
                    try {
                        Update-FogObject -type object -coreObject macaddressassociation -IDofObject $macItem.id -jsonData ($macItem | ConvertToJson)
                    } catch {   
                        $removePrimaryAttribute = @{
                            uriPath = "macaddressassociation/$($macItem.id)/edit";
                            Method = 'Put';
                            jsonData = ($macItem | ConvertTo-Json);
                        }
                        Invoke-FogApi @removePrimaryAttribute;
                    }

                    Write-Verbose "Primary attribute removed, setting new primary...";
                    $newPrimary = ($macs | Where-Object mac -eq $macList[0] );
                    if ($null -eq $newPrimary) {
                        Write-Verbose "host only has usbmacs, adding first non usbmac adapter";
                        $physicalMacs = (get-netadapter | select-object -expand macaddress).replace("-",":")
                        $physicalMacs = $physicalMacs | Where-Object {$_ -NotIn $usbMacs};
                        Add-FogHostMac -hostID $hostID -macAddress $physicalMacs[0] -primary;
                    } else {
                        $newPrimary.primary = '1';
                        $newPrimary.pending = '0';
                        Update-FogObject -type object -coreObject macaddressassociation -IDofObject $newPrimary.id -jsonData ($newPrimary | ConvertTo-Json)
                    }
                }

                Write-Verbose "Remove the usb ethernet mac association";
                try {
                    $result += Remove-FogObject -type object -coreObject macaddressassociation -IDofObject $macItem.id;
                } catch {
                    $removeMacAssoc = @{
                        uriPath = "macaddressassociation/$($macItem.id)/delete";
                        Method = 'Delete';
                    }
                    $result += Invoke-FogApi @removeMacAssoc;
                }
                Write-Verbose "Usb macs $usbMacs have been removed from $hostname on the $fogServer";
            }
        }
    }

    end {
        if ($null -eq $result) {
            $result = "no usb adapters found"; #replace string if found
        }
        return $result;
    }

}

function Reset-HostEncryption {
# .ExternalHelp FogApi-help.xml
    
    [CmdletBinding()]
    param (
        $fogHost = (Get-FogHost),
        [switch]$restartSvc
     )

    process {
        $fogHost.pub_key = "";
        $fogHost.sec_tok = "";
        $fogHost.sec_time = "0000-00-00 00:00:00";

        $jsonData = $fogHost | Select-Object id,pub_key,sec_tok,sec-time | ConvertTo-Json;
        $result = Update-FogObject -type object -coreObject host -IDofObject $fogHost.id -jsonData $jsonData;
        if ($restartSvc) {
            Restart-Service fogservice -force;
        }
        return $result;
    }
    
}

function Resolve-HostID {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    param (
        $hostID = (Get-foghost).id
    )
    
    
    process {
        if ($hostID.gettype().name -ne "Int32"){
            try {
                $check = [int32]$hostID
                Write-Verbose "checking if hostid is an int: '$check' yes it is"
            } catch {
                if ($hostID.gettype().name -eq "string") {
                    $hostID = (Get-FogHost -hostname $hostID).id
                } elseif($hostID.gettype().name -eq "PSCustomObject") {
                    if ($hostID.id) {
                        $hostID = $hostID.id
                    } else {
                        $hostID = $null;
                    }
                } else {
                    $hostID = $Null;
                }
                if ($null -eq $hostID) {
                    Write-Error "Please provide a valid foghost object, hostid or hostname, returning null for hostid"
                }
            }
        }
        if ($null -ne $hostID) {
            if ($null -eq (Get-foghost -hostID $hostID)) {
                Write-Error "hostid $hostID does not exist in your fog hosts! returning null!"
                $hostID = $null;
            }
        }
        return $hostID;
    }
    
}
function Send-FogImage {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding(DefaultParameterSetName='now')]
    [Alias('Push-FogImage','Deploy-FogImage')]
    param (
        [Parameter(ParameterSetName='now')]
        [Parameter(ParameterSetName='schedule')]
        $hostId,
        [Parameter(ParameterSetName='schedule')]
        [datetime]$StartAtTime
    )
    
    
    process {
        $fogHost = Get-FogHost -hostID $hostId;
        $currentImage = $fogHost.imageName;
        $fogImages = Get-FogImages;
        $fogImage = ($fogImages | Where-Object name -match $currentImage)
        "Creating Deploy Task for fog host of id $($hostID) named $($fogHost.name)" | Out-Host;
        "Will deploy the assigned image $($fogImage.name) - $($fogImage.id) which will install the os $($fogImage.osname)" | Out-host;
        if ($PSCmdlet.ParameterSetName -eq 'now') {
            "No Time was specified, queuing the task to start now" | out-host;
            $jsonData = "{`"taskTypeID`": `"1`", `"shutdown`":`"0`",`"other2`":`"0`",`"other4`":`"1`",`"isActive`":`"1`" }";
        } else {
            "Start time of $($StartAtTime) specified, scheduling the task to start at that time" | out-host;
            $scheduleTime = Get-FogSecsSinceEpoch -scheduleDate $StartAtTime
            $runTime = get-date $StartAtTime -Format "yyyy-M-d HH:MM"
            $jsonData = @"
                    {
                        "name":"Deploy Task",
                        "type":"S",
                        "taskTypeID":"1",
                        "runTime":"$runTime",
                        "scheduleTime":"$scheduleTime",
                        "isGroupTask":"0",
                        "hostID":"$($hostId)",
                        "shutdown":"0",
                        "other2":"0",
                        "other4":"1",
                        "isActive":"1"
                    }
"@

        }
        return New-FogObject -type objecttasktype -coreTaskObject host -jsonData $jsonData -IDofObject "$hostId";
    }
    
}
function Set-FogInventory {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    param (
        $hostObj = (Get-FogHost),
        $jsonData = (Get-FogInventory -hostObj $hostObj)
    )

    begin {
        $inventoryApi = @{
            jsonData = $jsonData;
            Method = 'Post';
            uriPath = "inventory/new";
        }
    }

    process {
        Invoke-FogApi @inventoryApi -verbose;
    }

    end {
        return;
    }

}

function Set-FogServerSettings {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding(DefaultParameterSetName='prompt')]
    param (
        [parameter(ParameterSetName='default')]
        [string]$fogApiToken,
        [parameter(ParameterSetName='default')]
        [string]$fogUserToken,
        [parameter(ParameterSetName='default')]
        [string]$fogServer,
        [parameter(ParameterSetName='prompt')]
        [switch]$interactive
    )

    process {
        $settingsFile = Get-FogServerSettingsFile;
        $ServerSettings = Get-FogServerSettings;
        Write-Verbose "Current/old Settings are $($ServerSettings)";
        $helpTxt = @{
            fogApiToken = "fog API token found at http://fog-server/fog/management/index.php?node=about&sub=settings under API System";
            fogUserToken = "your fog user api token found in the user settings http://fog-server/fog/management/index.php?node=user&sub=list select your api enabled used and view the api tab";
            fogServer = "your fog server hostname or ip address to be used for created the url used in api calls default is fog-server or fogServer";
        }
        if($interactive -or $PSCmdlet.ParameterSetName -eq 'prompt') {
            ($serverSettings.psobject.properties).Name | ForEach-Object {
                $var = (Get-Variable -Name $_);
                if ($null -eq $var.Value -OR $var.Value -eq "") {
                    Set-Variable -name $var.Name -Value (Read-Host -Prompt "help message: $($helpTxt.($_))`nEnter the $($var.name)");
                }    
            }
            $serverSettings = @{
                fogApiToken = $fogApiToken;
                fogUserToken = $fogUserToken;
                fogServer = $fogServer;
            }
            $serverSettings | ConvertTo-Json | Out-File -FilePath $settingsFile -Encoding oem -Force;
        } elseif( #if all params are passed and not null create new settings object
            !([string]::IsNullOrEmpty($fogApiToken)) -AND
            !([string]::IsNullOrEmpty($fogUserToken)) -AND
            !([string]::IsNullOrEmpty($fogServer))
        ) {
            $serverSettings = @{
                fogApiToken = $fogApiToken;
                fogUserToken = $fogUserToken;
                fogServer = $fogServer;
            }
        } else {
            #check for some setting being passed but not all and set them individually
            if (!([string]::IsNullOrEmpty($fogApiToken))) {
                $ServerSettings.fogApiToken = $fogApiToken;
            }
            if (!([string]::IsNullOrEmpty($fogUserToken))) {
                $ServerSettings.fogUserToken = $fogUserToken;
            }
            if (!([string]::IsNullOrEmpty($fogServer))) {
                $ServerSettings.fogServer = $fogServer;
            }
            $serverSettings | ConvertTo-Json | Out-File -FilePath $settingsFile -Encoding oem -Force;
        }
        # If given paras are null just pulls from settings file
        # If they are not null sets the object to passed value
        
        

        Write-Verbose "making sure all settings are set";
        if ( $ServerSettings.fogApiToken -eq $helpTxt.fogApiToken -OR
            $ServerSettings.fogUserToken -eq $helpTxt.fogUserToken -OR 
            $ServerSettings.fogServer -eq $helpTxt.fogServer -or
            ([string]::IsNullOrEmpty($ServerSettings.fogApiToken)) -OR 
            ([string]::IsNullOrEmpty($ServerSettings.fogUserToken)) -OR
            ([string]::IsNullOrEmpty($ServerSettings.fogServer))
        ) {
            Write-Host -BackgroundColor Yellow -ForegroundColor Red -Object "a fog setting is either null or still set to its default help text, opening the settings file for you to set the settings"
            Write-Host -BackgroundColor Yellow -ForegroundColor Red -Object "This script will close after opening settings in notepad, please re-run command after updating settings file";
            if ($isLinux) {
                $editor = 'nano';
            }
            elseif($IsMacOS) {
                $editor = 'TextEdit';
            }
            else {
                $editor = 'notepad.exe';
            }
            Start-Process -FilePath $editor -ArgumentList "$SettingsFile" -NoNewWindow -PassThru;
            return;
        }
        Write-Verbose "Writing new Settings";
        $serverSettings | ConvertTo-Json | Out-File -FilePath $settingsFile -Encoding oem -Force;
        Write-Verbose "ensuring security is set"
        Set-FogServerSettingsFileSecurity $settingsFile;
        return (Get-Content $settingsFile | ConvertFrom-Json);
    }

}

function Set-FogServerSettingsFileSecurity {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    param (
        $settingsFile = (Get-FogServerSettingsFile)    
    )
    
    process {
        if ($isLinux -or $IsMacOS) {
            Write-Verbose "Setting linux permissions on api settings file so only owner has rw";
            chmod 600 $settingsFile
        } else {
            Write-Verbose "Setting read/write permissions for owner only on api settings file";
            #Get current ACL to file/folder
            $acl = Get-Acl $settingsFile
            #Disable inheritance and remove inherited permissions
            $acl.SetAccessRuleProtection($true,$false)
            #Remove all explict ACEs
            $acl.Access | ForEach-Object { $acl.RemoveAccessRule($_) }
            #Create ACE for owner with full control
            $ace = New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList $acl.Owner, "FullControl", "Allow"
            $acl.AddAccessRule($ace)
            #Save ACL to file/
            try {
                Set-Acl -Path $settingsFile -AclObject $acl
            } catch {
                icacls "$settingsFile" /reset /Q /C
                icacls "$settingsFile" /setowner $env:USERNAME /Q /C
                icacls "$settingsFile" /inheritance:r /Q /C
                icacls "$settingsFile" /grant "$env:USERNAME`:F" /Q /C
                icacls "$settingsFile" /remove 'System' /Q /C
                icacls "$settingsFile" /remove 'Administrators' /Q /C
            }
        }    
    }
    
}
function Set-FogSnapins {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    [Alias('Add-FogSnapins')]
    param (
        $hostid = ((Get-FogHost).id),
        $pkgList,
        [switch]$exactNames
    )

    process {
        Write-Verbose "Association snapins from package list with host";
        $snapins = Get-FogSnapins;
        # $urlPath = "snapinassociation/create"
        $curSnapins = Get-FogAssociatedSnapins -hostId $hostid;
        $result = New-Object System.Collections.Generic.List[Object];
        if ($null -ne $pkgList) {
            $pkgList | ForEach-Object {
                if ($exactNames) {
                    $json = @{
                        snapinID = "$((($snapins | Where-Object name -eq "$($_)").id))";
                        hostID = "$hostid"
                    };
                } else {
                    $json = @{
                        snapinID = "$((($snapins | Where-Object name -match "$($_)").id))";
                        hostID = "$hostid"
                    };
                }
                Write-Verbose "$_ is pkg snapin id found is $($json.snapinID)";
                if (($null -ne $json.SnapinID) -AND ($json.SnapinID -notin $curSnapins.id)) {
                    $json = $json | ConvertTo-Json;
                    $result.add((New-FogObject -type object -coreObject snapinassociation -jsonData $json));
                } elseif ($json.SnapinID -in $curSnapins.id) {
                    Write-Warning "$_ snapin of id $($json.SnapinID) is already associated with this host";
                } else {
                    Write-Warning "no snapin ID found for $_ pkg";
                }
                # Invoke-FogApi -Method POST -uriPath $urlPath -jsonData $json;
            }
        }
        return $result;
    }


}

function Start-FogSnapin {
    # .ExternalHelp FogApi-help.xml
    [CmdletBinding(DefaultParameterSetName='byId')]
    param (
        [parameter(ParameterSetName='byId')]
        [parameter(ParameterSetName='byName')]
        $hostID,
        [parameter(ParameterSetName='byName')]
        $snapinname,
        [parameter(ParameterSetName='byId')]
        $snapinId
    )
    
    
    process {
        $snapins = Get-FogSnapins;
        if  ($PSCmdlet.ParameterSetName -eq 'byName') {
            $snapin = $snapins | Where-Object name -eq $snapinname;
            $snapinId = $snapin.id
        } else {
            $snapin = $snapins | Where-Object id -eq $snapinId;
            $snapinname = $snapin.name
        }
        if ($null -eq $snapinId) {
            Write-Warning "No snapinid was found for the snapin $snapinName! not running any actions"
            return $null
        } else {
            $json = (@{
                "taskTypeID"='13';
                "deploySnapins"="$snapinId";
            } | ConvertTo-Json);
            $fogHost = Get-Foghost -hostID $hostID;
            "Deploying the snapin $snapinname to the host $($fogHost.name)" | out-host;
            New-FogObject -type objecttasktype -coreTaskObject host -jsonData $json -IDofObject "$hostID";
        }
    }
    
}
function Start-FogSnapins {
# .ExternalHelp FogApi-help.xml

    [CmdletBinding()]
    param (
        $hostid = ((Get-FogHost).id),
        $taskTypeid = '12'
    )

    begin {
        Write-Verbose "Stopping any queued snapin tasks";
        try {
            $tasks = Get-FogActiveTasks;
        } catch {
            $tasks = (Invoke-FogApi -Method GET -uriPath "task/active").tasks;
        }
        $tasks = $tasks | Where-Object { $_.type.id -match $taskTypeid} #filter task list to all snapins task.
        $taskID = (($tasks | Where-Object hostID -match $hostid).id);
        if ($null -ne $taskID) { #if active snapin tasks are found for the host cancel them, otherwise do nothing to tasks
            Write-Verbose "Found $($taskID.count) tasks deleting them now";
            $taskID | ForEach-Object{
                try {
                    Remove-FogObject -type objecttasktype -coreTaskObject task -IDofObject $_;
                } catch {
                    Invoke-FogApi -Method DELETE -uriPath "task/$_/cancel";
                }
            }
        }
        # $snapAssocs = Invoke-FogApi -uriPath snapinassociation -Method Get;
        # $snaps = $snapAssocs.snapinassociations | ? hostid -eq $hostid;
    }

    process {
        Write-Verbose "starting all snapin task for host";
        $json = (@{
            "taskTypeID"=$taskTypeid;
            "deploySnapins"=-1;
        } | ConvertTo-Json);
        New-FogObject -type objecttasktype -coreTaskObject host -jsonData $json -IDofObject $hostid;
    }

    end {
        Write-Verbose "Snapin tasks have been queued on the server";
        return;
    }

}

function Update-FogObject {
# .ExternalHelp FogApi-help.xml
    [CmdletBinding()]
    [Alias('Set-FogObject')]
    param (
        # The type of object being requested
        [Parameter(Position=0)]
        [ValidateSet("object")]
        [string]$type,
        # The json data for the body of the request
        [Parameter(Position=2)]
        [Object]$jsonData,
        # The id of the object to remove
        [Parameter(Position=3)]
        [string]$IDofObject,
        [Parameter(Position=4)]
        [string]$uri
    )

    DynamicParam { $paramDict = Set-DynamicParams $type; return $paramDict; }

    begin {
        $paramDict | ForEach-Object { New-Variable -Name $_.Keys -Value $($_.Values.Value);}
        Write-Verbose "Building uri and api call";
        if([string]::IsNullOrEmpty($uri)) {
            $uri = "$CoreObject/$IDofObject/edit";
        }

        $apiInvoke = @{
            uriPath=$uri;
            Method="PUT";
            jsonData=$jsonData;
        }
        $result = Invoke-FogApi @apiInvoke;
        return $result;
    }

}

function Export-FogImageDefinitions {
    <#
    .SYNOPSIS
    Create an export of image definitions for later import for syncing across sites or migrating to a new server
     
    .DESCRIPTION
    Creates .json files of image definitions on your server
     
    .PARAMETER exportPath
    Parameter description
     
    .EXAMPLE
    An example
     
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        $exportPath
    )
    
    
    process {
        if ($IsLinux -AND ($env:HOSTNAME -eq (Get-FogServerSettings).fogserver) -AND ([string]::IsNullOrEmpty($exportPath))) {
            "This is linux, is the fogserver set in the api settings and no export path was given" | Out-Host;
            "Assuming you want to create a /images/imageDefinitions folder for migrating your fog server images to another server" | out-host;
            $exportPath = "/images/imageDefinitions"
            if (!(Test-Path $exportPath)) {
                mkdir $exportPath;
            }
        }
        #get current image definitions from fog server
        $images = Get-FogImages;
        #Get any files currently in the export path
        $curExportFiles = (Get-ChildItem $exportPath\*.json);
        $curExports = New-Object System.Collections.Generic.list[system.object];
        $curExportFiles | ForEach-Object {
            $curExports.add((Get-content $_.FullName | ConvertFrom-Json))
        }
        # $imagesToExport = New-Object System.Collections.Generic.list[system.object];
        
        $images | ForEach-Object {
            if ($_.name -in $curExports.name) {
                Write-Verbose "Image of $($_.name) already has an export, checking if it is newer"
                $curExportDate = Get-date $curExports.deployed;
                $newImageDate = Get-Date $_.deployed
                if ($newImageDate -gt $curExportDate) {
                    Write-Verbose "The image has been captured more recently than the export"
                    $_ | ConvertTo-Json | Out-File -Encoding oem -FilePath "$exportPath\$($_.name).json" -force
                } else {
                    Write-Verbose "no need to export $($_.name) the exported definition is up to date"
                }
            } else {
                $_ | ConvertTo-Json | Out-File -Encoding oem -FilePath "$exportPath\$($_.name).json"
            }
        }


    }
    
}
function Get-DynmicParam {
<#
.SYNOPSIS
Gets the dynamic parameter for the main functions
 
.DESCRIPTION
Dynamically sets the correct tab completeable validate set for the coreobject, coretaskobject, coreactivetaskobject, or string to search
 
.PARAMETER paramName
the name of the parameter being dynamically set within the validate set
 
.PARAMETER position
the position to put the dynamic parameter in
 
#>


    [CmdletBinding()]
    param (
        [Parameter(Position=0)]
        [ValidateSet('coreObject','coreTaskObject','coreActiveTaskObject')]
        [string]$paramName,
        $position=1
    )
    begin {
        #initilzie objects
        $attributes = New-Object Parameter; #System.Management.Automation.ParameterAttribute;
        $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
        # $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        # Set attributes
        $attributes.Position = $position;
        $attributes.Mandatory = $true;

        $attributeCollection.Add($attributes)

        $coreObjects = @(
            "clientupdater", "dircleaner", "greenfog", "group", "groupassociation",
            "history", "hookevent", "host", "hostautologout", "hostscreensetting", "image",
            "imageassociation", "imagepartitiontype", "imagetype", "imaginglog", "inventory", "ipxe",
            "keysequence", "macaddressassociation", "module", "moduleassociation", "multicastsession",
            "multicastsessionsassociation", "nodefailure", "notifyevent", "os", "oui", "plugin",
            "powermanagement", "printer", "printerassociation", "pxemenuoptions", "scheduledtask",
            "service", "snapin", "snapinassociation", "snapingroupassociation", "snapinjob",
            "snapintask", "storagegroup", "storagenode", "task", "tasklog", "taskstate", "tasktype",
            "unisearch", "usercleanup", "usertracking", "virus"
        );
        $coreTaskObjects = @("group", "host", "multicastsession", "snapinjob", "snapintask", "task");
        $coreActiveTaskObjects = @("multicastsession", "scheduledtask", "snapinjob", "snapintask", "task");
    }

    process {
        switch ($paramName) {
            coreObject { $attributeCollection.Add((New-Object ValidateSet($coreObjects)));}
            coreTaskObject {$attributeCollection.Add((New-Object ValidateSet($coreTaskObjects)));}
            coreActiveTaskObject {$attributeCollection.Add((New-Object ValidateSet($coreActiveTaskObjects)));}
        }
        $dynParam = New-Object System.Management.Automation.RuntimeDefinedParameter($paramName, [string], $attributeCollection);
        # $paramDictionary.Add($paramName, $dynParam);
    }
    end {
        return $dynParam;
    }

}
function Set-DynamicParams {
<#
.SYNOPSIS
Sets the dynamic param dictionary
 
.DESCRIPTION
Sets dynamic parameters inside functions
 
.PARAMETER type
the type of parameter
 
#>


    [CmdletBinding()]
    param ($type)
    $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary;

    # Sub function for setting
    function Set-Param($paramName) {
        $param = Get-DynmicParam -paramName $paramName;
        $paramDictionary.Add($paramName, $param);
    }
    switch ($type) {
        object { Set-Param('coreObject');}
        objectactivetasktype { Set-Param('coreActiveTaskObject');}
        objecttasktype {Set-Param('coreTaskObject');}
        search {Set-Param('coreObject');}
    }
    return $paramDictionary;

}