
function Get-MimeType {
Param( 
    [Parameter(Mandatory)][String] $Extension
)

    $mimeType = "application/unknown";
    if ( $null -ne $Extension )
    {
        Switch ($Extension)
        {
            ".json" { $mimetype = "application/json" ; break }
            ".md" { $mimetype = "text/markdown" ; break }
            ".yml" { $mimetype = "text/yaml" ; break }
            ".rst" { $mimetype = "text/x-rst" ; break }
            ".7z" { $mimetype = "application/x-7z-compressed" ; break }
            ".mti" { $mimetype = "application/vnd.martilq.json" ; break }
            ".ttf" { $mimetype = $null ; break }
            ".eot" { $mimetype = $null ; break }
            ".woff" { $mimetype = $null ; break }
            ".woff2" { $mimetype = $null ; break }
            ".csv" { $mimetype = "text/csv" ; break }
            ".tsv" { $mimetype = "text/csv" ; break }
            Default {
                $drive = Get-PSDrive HKCR -ErrorAction SilentlyContinue;
                if ( $null -eq $drive )
                {
                    $drive = New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT
                }
                $ext = Get-ItemProperty HKCR:$Extension -ErrorAction SilentlyContinue;
                if ( $null -ne $ext) {
                    $mimeType = $ext."Content Type";
                }
            }
        }
    }

    return $mimeType

}


function New-MartiResource {
Param( 
    [Parameter(Mandatory)][String] $SourcePath,
    [String] $UrlPath = "",
    [switch] $ExcludeHash,
    [switch] $ExtendAttributes,
    [String] $LogPath

) 

    $Global:MartiErrorId = ""
    $script:LogPathName = $LogPath
 
    Write-Debug "Parameter: LogPath   Value: $LogPath "
    Open-Log
    Write-Log "Function 'New-MartiResource' parameters follow"
    Write-Log "Parameter: UrlPath   Value: $UrlPath "
    Write-Log "Parameter: SourcePath   Value: $SourcePath "
    Write-Log "Parameter: ExcludeHash   Value: $ExcludeHash "
    Write-Log ""


    if (Test-Path -Path $SourcePath -PathType Leaf) {
       
        $item = Get-Item -Path $SourcePath -Force 

        Write-Log "Define file $($item.FullName) "

        if ($ExcludeHash) {
            $hash = $null
        } else {
            $hash = New-MartiHash -Algorithm "SHA256" -FilePath $item.FullName
        }

        $lattribute =  Set-MartiResourceAttributes -Path $item.FullName -FileType $item.Extension.Substring(1) -ExtendedAttributes:$ExtendAttributes
        $expires = (Get-Date).AddYears(7)

        $oResource = [PSCustomObject]@{ 
            title = $item.Name.Replace($item.Extension, "")
            uid = (New-Guid).ToString()
            documentName = $item.Name
            issuedDate = Get-Date -f "yyyy-MM-ddTHH:mm:ss"
            modified = $item.LastWriteTime.ToString("yyyy-MM-ddTHH:mm:ss")
            expires = $expires.ToString("yyyy-MM-ddTHH:mm:ss") 
            state = "active"
            author = $null
            length = $item.Length
            hash = $hash

            description = $null
            url = $null
            structure = $null
            version = $version
            contentType = Get-MimeType($item.Extension)
            compression = $null
            encryption = $null

            attributes = $lattribute
        }

        if ($null -ne $UrlPath -and $UrlPath -ne "") {
            if ($UrlPath[$UrlPath.Length-1] -eq "/" -or $UrlPath[$UrlPath.Length-1] -eq "\\") {
                $oResource.url = $UrlPath.Replace("\\", "/") + $item.Name
            } else {
                $oResource.url = $UrlPath.Replace("\\", "/") + "/" + $item.Name
            }
        }
        
    } else {
        $Global:MartiErrorId = "MRI2001"
        $message = "Document '$SourcePath' not found or is a folder"
        Write-Log ($message + " " + $Global:MartiErrorId) 
        Close-Log
        throw $message
    }
    Close-Log

    return $oResource

}

function New-MartiHash{
    Param( 
        [Parameter(Mandatory)][String] $Algorithm,
        [String] $FilePath,
        [String] $Value = ""
    ) 

    if ($Value  -eq "" -and $FilePath -ne "") {
        $Value = (Get-FileHash -Path $FilePath -Algorithm $Algorithm).Hash
    }

    $oHash = [PSCustomObject]@{ 
        algo = $Algorithm
        value = $Value
        signed = $false
    }

    return $oHash
}
    
function New-Encryption{
Param( 
    [Parameter(Mandatory)][String] $Algorithm,
    [String] $Value

) 
    
    $oEncryption = [PSCustomObject]@{ 
        algo = $Algorithm
        value = $Value
    }

    return $oEncryption
}



function New-MartiChildItem
{
Param( 
    [Parameter(Mandatory)][String] $SourceFolder,
    [String] $Filter ="*",
    [String] $UrlPath,
    [switch] $Recurse,
    [switch] $ExtendAttributes,
    [switch] $ExcludeHash,
    [String] $LogPath

) 
    $script:LogPathName = $LogPath

    Write-Debug "Parameter: LogPath   Value: $LogPath "
    Open-Log
    Write-Log "Function 'New-MartiDefinition' parameters follow"
    Write-Log "Parameter: SourceFolder   Value: $SourceFolder "
    Write-Log "Parameter: Filter   Value: $Filter "
    Write-Log "Parameter: Recurse   Value: $Recurse "
    Write-Log "Parameter: ExtendAttributes   Value: $ExtendAttributes "
    Write-Log "Parameter: ExcludeHash   Value: $ExcludeHash "
    Write-Log ""

    $oMarti = New-MartiDefinition
    $lresource = $oMarti.resources

    $SourceFullName = (Get-Item -Path $SourceFolder).FullName

    Get-ChildItem $SourceFolder -Filter $Filter -Recurse:$Recurse -Force| Where-Object {!$_.PSIsContainer} | ForEach-Object {

        $oResource = New-MartiResource -SourcePath $_.FullName -UrlPath $UrlPath -LogPath $LogPath -ExtendAttributes:$ExtendAttributes -ExcludeHash:$ExcludeHash

        if ($null -ne $UrlPath -and $UrlPath -ne "") {
            $postfixName = $_.FullName.Replace($SourceFullName, "")
            if ($postfixName[0] -eq "/" -or $postfixName[0] -eq "`\" ){
                $postfixName = $postfixName.Substring(1, ($postfixName.Length-1))
            }
            if ($UrlPath[$UrlPath.Length-1] -eq "/" -or $UrlPath[$UrlPath.Length-1] -eq "`\") {
                $oResource.url = $UrlPath.Replace("`\", "/") + $postfixName.Replace("`\", "/")
            } else {
                $oResource.url = $UrlPath.Replace("`\", "/") + "/" + $postfixName.Replace("`\", "/")
            }
        }

        $lresource += $oResource
        
    }
    Write-Log "Captured $($lresource.Count) items"
    $oMarti.resources = $lresource

    Close-Log

    return $oMarti

}



function New-DefaultCsvAttributes {
       
    [System.Collections.ArrayList]$lattribute = @()

    $oAttribute = [PSCustomObject]@{
        category = "dataset"
        name = "header"
        function = "count"
        comparison = "NA"
        value = 1
    }
    $lattribute += $oAttribute
    
    $oAttribute = [PSCustomObject]@{
        category = "dataset"
        name = "footer"
        function = "count"
        comparison = "NA"
        value = 0
    }
    $lattribute += $oAttribute
    
    $oAttribute = [PSCustomObject]@{
        category = "format"
        name = "separator"
        function = "value"
        comparison = "NA"
        value = ","
    }
    $lattribute += $oAttribute
    
    $oAttribute = [PSCustomObject]@{
        category = "format"
        name = "columns"
        function = "value"
        comparison = "NA"
        value = ","
    }
    $lattribute += $oAttribute

    $oAttribute = [PSCustomObject]@{
        category = "dataset"
        name = "records"
        function = "count"
        comparison = "NA"
        value = 0
    }
    $lattribute += $oAttribute
    
    $oAttribute = [PSCustomObject]@{
        category = "dataset"
        name = "columns"
        function = "count"
        comparison = "NA"
        value = 0
    }
    $lattribute += $oAttribute
    
    return $lattribute
}


function New-DefaultJsonAttributes {
       
    [System.Collections.ArrayList]$lattribute = @()
    
    $oAttribute = [PSCustomObject]@{
        category = "format"
        name = "list"
        function = "offset"
        comparison = "NA"
        value = ","
    }
    $lattribute += $oAttribute
    
    $oAttribute = [PSCustomObject]@{
        category = "format"
        name = "columns"
        function = "value"
        comparison = "NA"
        value = ","
    }
    $lattribute += $oAttribute    

    $oAttribute = [PSCustomObject]@{
        category = "dataset"
        name = "records"
        function = "count"
        comparison = "NA"
        value = 0
    }    
    $lattribute += $oAttribute
    
    $oAttribute = [PSCustomObject]@{
        category = "dataset"
        name = "columns"
        function = "count"
        comparison = "NA"
        value = 0
    }
    $lattribute += $oAttribute
    
    return $lattribute
}

function New-DefaultZipAttributes {
    Param (
        [String] $CompressionType = "ZIP",
        [String] $Encryption = ""
    )
       
    [System.Collections.ArrayList]$lattribute = @()
    
    
    $oAttribute = [PSCustomObject]@{
        category = "format"
        name = "compression"
        function = "algo"
        comparison = "NA"
        value = $CompressionType
    }
    $lattribute += $oAttribute    
    
    $oAttribute = [PSCustomObject]@{
        category = "format"
        name = "encryption"
        function = "algo"
        comparison = "NA"
        value = $Encryption
    }
    $lattribute += $oAttribute    

    $oAttribute = [PSCustomObject]@{
        category = "dataset"
        name = "files"
        function = "count"
        comparison = "NA"
        value = 0
    }    
    $lattribute += $oAttribute
    
    
    return $lattribute
}

function Set-AttributeValueString {
    Param (
        # Attribute List
        [Parameter(Mandatory)]  [System.Collections.ArrayList] $Attributes,
        # Attribute Category
        [Parameter(Mandatory)] [String] $Category,
        # Attribute Key Name
        [Parameter(Mandatory)] [String] $Key,
        # Attribute Function
        [Parameter(Mandatory)] [String] $Function,
        # Attribute Value
        [Parameter(Mandatory)] [String] $Value,
        # Attribute Comparison
        [String] $Comparison = "EQ"
    )

    foreach ($item in $Attributes)
    {
        if ($item.category -eq $Category -and $item.name -eq $Key -and $item.function -eq $Function)
        {
            if ($item.comparison -eq "NA" -or $item.comparison -eq $Comparison) {
                $item.comparison = $Comparison
                $item.value = $Value
                return
            }
        }
    }

    # Add the attribute    
    $oAttribute = [PSCustomObject]@{
        category = $Category
        name = $Key
        function = $Function
        comparison = $Comparison
        value = $Value
    }    
    $Attributes += $oAttribute
    return
}


function Set-AttributeValueNumber {
    Param (
        # Attribute List
        [Parameter(Mandatory)]  [System.Collections.ArrayList] $Attributes,
        # Attribute Category
        [Parameter(Mandatory)] [String] $Category,
        # Attribute Key Name
        [Parameter(Mandatory)] [String] $Key,
        # Attribute Function
        [Parameter(Mandatory)] [String] $Function,
        # Attribute Value
        [Parameter(Mandatory)] [Decimal] $Value,
        # Attribute Comparison
        [String] $Comparison = "EQ"
    )

    foreach ($item in $Attributes)
    {
        if ($item.category -eq $Category -and $item.name -eq $Key -and $item.function -eq $Function)
        {
            if ($item.comparison -eq "NA" -or $item.comparison -eq $Comparison) {
                $item.comparison = $Comparison
                $item.value = $Value
                return
            }
        }
    }


    # Add the attribute    
    $oAttribute = [PSCustomObject]@{
        category = $Category
        name = $Key
        function = $Function
        comparison = $Comparison
        value = $Value
    }    
    $Attributes += $oAttribute
    return
}


function Set-MartiResourceAttributes {
    Param (
        # File path
        [Parameter(Mandatory)] [String] $Path,
        # File type
        [Parameter(Mandatory)] [String] $FileType,
        # Process the file for attributes
        [Switch] $ExtendedAttributes
    )


    if ($FileType -eq "CSV") {
        $lattribute = New-DefaultCsvAttributes

        if ($ExtendedAttributes) {
            $delimiter = ","
            $rowCount = 0
            $colCount = 0
            $csvData = Import-Csv $Path -Delimiter $delimiter 
            foreach ($datum in $csvData) {
                $cc = (Get-Member -InputObject $datum -type NoteProperty).count
                if ($colCount -lt $cc) {
                    $colCount = $cc
                }
                $rowCount += 1
            }
            Set-AttributeValueNumber -Attributes $lattribute -Key "records" -Category "dataset" -Function "count" -Value $rowCount
            Set-AttributeValueNumber -Attributes $lattribute -Key "columns" -Category "dataset" -Function "count" -Value $colCount
        }
    }


    if ($FileType -eq "TXT") {
        $lattribute = New-DefaultCsvAttributes

        if ($ExtendedAttributes) {
            $delimiter = "`t"
            $rowCount = 0
            $colCount = 0
            $csvData = Import-Csv $Path -Delimiter $delimiter 
            foreach ($datum in $csvData) {
                $cc = (Get-Member -InputObject $datum -type NoteProperty).count
                if ($colCount -lt $cc) {
                    $colCount = $cc
                }
                $rowCount += 1
            }
            Set-AttributeValueNumber -Attributes $lattribute -Key "records" -Category "dataset" -Function "count" -Value $rowCount
            Set-AttributeValueNumber -Attributes $lattribute -Key "columns" -Category "dataset" -Function "count" -Value $colCount
        }
    }

    if ($FileType -eq "JSON") {
        $lattribute = New-DefaultJsonAttributes
    }

    if ($FileType -eq "ZIP") {
        $lattribute = New-DefaultZipAttributes -CompressionType "ZIP"
        if ($ExtendedAttributes) {
            $shell = New-Object -Com Shell.Application
            $zipFile = $shell.NameSpace($Path)
            $items = $zipFile.Items()
            Set-AttributeValueNumber -Attributes $lattribute -Key "files" -Category "dataset" -Function "count" -Value $items.Count
        }
    }

    if ($FileType -eq "7Z") {
        $lattribute = New-DefaultZipAttributes -CompressionType "7Z"
    }

    if ($null -eq $lattribute) {
        [System.Collections.ArrayList]$lattribute = @()
    }

    return $lattribute
}




function Compare-MartiResource {
    Param( 
        [Parameter(Mandatory)][String] $DataSource,
        [Parameter(Mandatory)][PSCustomObject] $Resource,
        [String] $LogPath   
    ) 


    $script:LogPathName = $LogPath

    Write-Debug "Parameter: LogPath   Value: $LogPath "
    Open-Log
    Write-Log "Function 'Compare-MartiResource' parameters follow"
    Write-Log ""
    
    if ($null -eq $Resource) {
        $Global:MartiErrorId = "MRI2201"
        $message = "No resource definition supplied"
        Write-Log ($message + " " + $Global:MartiErrorId) 
        Close-Log
        throw $message
    }
        
    
    if ($null -eq $DataSource -or $DataSource -eq "") {
        $Global:MartiErrorId = "MRI2202"
        $message = "No document supplied"
        Write-Log ($message + " " + $Global:MartiErrorId) 
        Close-Log
        throw $message
    }

    if ($DataSource.Length -le 1000) {
        # Check if the name is a file        
        if (Test-Path -Path $DataSource) {
            $inputData = Get-Content -Path $DataSource -Raw
            Write-Host "Loading file $DataSource"
        } else {
            $inputData = $DataSource
        }
    } else {
        $inputData = $DataSource
    }

    $formatProcessed = $false
    [System.Collections.ArrayList]$lerror = @()

    if ($Resource.contentType -eq "text/csv") {
        $formatProcessed = $true

        $data = $inputData | ConvertFrom-Csv -Delim ','

        $columns = ($data | get-member -type NoteProperty).count
        $rows = @($data).count
        
        $Resource.attributes | ForEach-Object {

            if ($_.category -eq "dataset" -and $_.name -eq "records" -and $_.function -eq "count" -and $_.comparison -eq "EQ") {
                
                if ($_.value -ne $rows) {
                    $oError = [PSCustomObject]@{
                        id = "MRI2203"
                        message = "Row count does not match"
                        found = "$rows"
                        expected = "$($_.value)"
                    }
                    $lerror += $oError
                }
            }

            if ($_.category -eq "dataset" -and $_.name -eq "columns" -and $_.function -eq "count" -and $_.comparison -eq "EQ") {

                if ($_.value -ne $columns) {
                    $oError = [PSCustomObject]@{
                        id = "MRI2204"
                        message = "Column count does not match"
                        found = "$columns"
                        expected = "$($_.value)"
                    }
                    $lerror += $oError
                }
            }
            
        }


    } 
    
    
    if ($Resource.contentType -eq "application/json") {
        $formatProcessed = $true

        $data = $inputData | ConvertFrom-Json
        
        $rows = @($data.data.monitor).count
        $item = $data.data.monitor[0]
        $columns = ($item | get-member -type NoteProperty).count

        $Resource.attributes | ForEach-Object {

            if ($_.category -eq "dataset" -and $_.name -eq "records" -and $_.function -eq "count" -and $_.comparison -eq "EQ") {
                
                if ($_.value -ne $rows) {
                    $oError = [PSCustomObject]@{
                        id = "MRI2203"
                        message = "Row count does not match"
                        found = "$rows"
                        expected = "$($_.value)"
                    }
                    $lerror += $oError
                }
            }

            if ($_.category -eq "dataset" -and $_.name -eq "columns" -and $_.function -eq "count" -and $_.comparison -eq "EQ") {

                if ($_.value -ne $columns) {
                    $oError = [PSCustomObject]@{
                        id = "MRI2204"
                        message = "Column count does not match"
                        found = "$columns"
                        expected = "$($_.value)"
                    }
                    $lerror += $oError
                }
            }
            
        }


    } 


    if (!$formatProcessed) {
        $Global:MartiErrorId = "MRI2203"
        $message = "Data format not supported"
        Write-Log ($message + " " + $Global:MartiErrorId) 
        Close-Log
        throw $message
    }
    
    $status = "OK"
    if ($lerror.Count -gt 0) {
        $status = "ERROR"
    }
    $oResult = [PSCustomObject]@{
        status = $status
        errors = $lerror
    }

    Close-Log
    return $oResult
}


