PSCompletions.psm1

function PSCompletions {
    $arg = $args
    function _replace($data) {
        $__d__ = $data -join ''
        $__p__ = '\{\{(.*?(\})*)(?=\}\})\}\}'
        $matches = [regex]::Matches($__d__, $__p__)
        foreach ($match in $matches) {
            $__d__ = $__d__.Replace($match.Value, (Invoke-Expression $match.Groups[1].Value))
        }
        if ($__d__ -match $__p__) { $PSCompletions.fn_replace($__d__) }else { return $__d__ }
    }
    function param_error($flag, $cmd) {
        $res = if ($flag -eq 'min') { $PSCompletions.json.param_min }
        elseif ($flag -eq 'max') { $PSCompletions.json.param_max }
        else { $PSCompletions.json.param_err }
        $PSCompletions.fn_write((_replace ($res + "`n" + $PSCompletions.json.example.$cmd)))
    }
    function _list {
        if ($arg.Length -gt 2) {
            param_error 'max' 'list'
            return
        }
        if ($arg.Length -eq 2 -and $arg[1] -ne '--remote') {
            param_error 'err' 'list'
            return
        }
        $data = [System.Collections.Generic.List[System.Object]]@()
        if ($arg[1] -eq '--remote') {
            if (!($PSCompletions.fn_download_list())) { throw (_replace $PSCompletions.json.net_error) }
            $max_len = ($PSCompletions.list  | Measure-Object -Maximum Length).Maximum
            $PSCompletions.list | ForEach-Object {
                $status = if ($PSCompletions.comp_cmd.$_) { $PSCompletions.json.list_add_done }else { $PSCompletions.json.list_add }
                $data.Add(@{
                        content = "{0,-$($max_len + 3)} {1}" -f ($_, $status)
                        color   = 'Green'
                    })
            }
            $PSCompletions.fn_less_table($data, ('Completion', 'Status', $max_len), {
                    $PSCompletions.fn_write((_replace $PSCompletions.json.list_add_tip))
                })
        }
        else {
            $max_len = ($PSCompletions.comp_cmd.keys | Measure-Object -Maximum Length).Maximum
            $PSCompletions.comp_cmd.keys | ForEach-Object {
                $alias = if ($PSCompletions.comp_cmd.$_ -eq $_) { '' }else { $PSCompletions.comp_cmd.$_ }
                $data.Add(@{
                        content = "{0,-$($max_len + 3)} {1}" -f ($_, $alias)
                        color   = 'Green'
                    })
            }
            $PSCompletions.fn_less_table($data, ('Completion', 'Alias', $max_len))
        }
    }
    function _add {
        if ($arg.Length -lt 2) {
            param_error 'min' 'add'
            return
        }
        if (!($PSCompletions.fn_download_list())) { return }
        $arg[1..($arg.Length - 1)] | ForEach-Object {
            if ($_ -in $PSCompletions.list) {
                $PSCompletions.fn_add_completion($_)
            }
            else {
                $PSCompletions.fn_write((_replace $PSCompletions.json.add_error))
            }
        }
    }
    function _remove {
        if ($arg.Length -lt 2) {
            param_error 'min' 'rm'
            return
        }
        $arg[1..($arg.Length - 1)] | ForEach-Object {
            if ($_ -in $PSCompletions.comp_cmd.keys) {
                $dir = $PSScriptRoot + '\completions\' + $_
                Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue
                if (!(Test-Path($dir))) {
                    $PSCompletions.fn_write((_replace $PSCompletions.json.remove_done))
                }
            }
            else { $PSCompletions.fn_write((_replace $PSCompletions.json.remove_err)) }
        }
    }
    function _update {
        $comp_cmd = $PSCompletions.comp_cmd.keys | Where-Object { $_ -in $PSCompletions.list }
        function _do {
            $update_list = [System.Collections.Generic.List[string]]@()
            try {
                foreach ($_ in $comp_cmd) {
                    $url = $PSCompletions.url + '/completions/' + $_ + '/guid.txt'
                    $response = Invoke-WebRequest -Uri $url
                    if ($response.StatusCode -eq 200) {
                        $content = ($response.Content).Trim()
                        $guid = (Get-Content ($PSCompletions.path.completions + '\' + $_ + '\guid.txt') -Raw).Trim()
                        if ($guid -ne $content) { $update_list.Add($_) }
                    }
                }
            }
            catch {
                $PSCompletions.fn_write((_replace $PSCompletions.json.net_error))
                return
            }
            $update_list | Out-File $PSCompletions.path.update -Force -Encoding utf8
            $PSCompletions.update = $update_list
            if ($update_list) {
                $PSCompletions.fn_write((_replace $PSCompletions.json.update_has))
            }
            else {
                $PSCompletions.fn_write((_replace $PSCompletions.json.update_no))
            }
        }
        if ($arg.Length -eq 1) { _do }
        else {
            if ($arg[1] -eq '*') {
                foreach ($_ in $PSCompletions.update) {
                    $PSCompletions.fn_add_completion($_, $true, $true)
                }
            }
            else {
                $arg[1..($arg.Length - 1)] | ForEach-Object {
                    if ($_ -in $comp_cmd) {
                        $PSCompletions.fn_add_completion($_, $true, $true)
                    }
                    else {
                        $PSCompletions.fn_write((_replace $PSCompletions.json.update_error))
                    }
                }
            }
            _do
        }
    }
    function _search {
        if ($arg.Length -lt 2) {
            param_error 'min' 'search'
            return
        }
        elseif ($arg.Length -gt 2) {
            param_error 'max' 'search'
            return
        }
        if (!(($PSCompletions.fn_download_list()))) { return }
        $res = $PSCompletions.list | Where-Object { $_ -like $arg[1] }
        if ($res) {
            $PSCompletions.fn_less($res, 'Cyan')
        }
        else {
            $PSCompletions.fn_write((_replace $PSCompletions.json.search_err))
        }
    }
    function _which {
        if ($arg.Length -lt 2) {
            param_error 'min' 'which'
            return
        }
        $arg[1..($arg.Length - 1)] | ForEach-Object {
            if ($_ -in $PSCompletions.comp_cmd.keys) {
                Write-Output ($PSCompletions.path.completions + '\' + $_)
            }
            else {
                $PSCompletions.fn_write((_replace $PSCompletions.json.which_err))
            }
        }
    }
    function _alias {
        $cmd_list = @('add', 'rm', 'list', 'reset')
        if ($arg.Length -eq 1) {
            param_error 'min' 'alias'
            return
        }
        if ($arg[1] -notin $cmd_list) {
            $PSCompletions.fn_write((_replace $PSCompletions.json.param_errs))
            return
        }
        if ($arg[1] -eq 'add') {
            if ($arg.Length -lt 4) {
                param_error 'min' 'alias_add'
                return
            }
            elseif ($arg.Length -gt 4) {
                param_error 'max' 'alias_add'
                return
            }
        }
        if ($arg[1] -eq 'rm') {
            if ($arg.Length -lt 3) {
                param_error 'min' 'alias_rm'
                return
            }
        }
        if ($arg.Length -gt 2) {
            if ($arg[1] -eq 'list') {
                param_error 'max' 'alias_list'
                return
            }
            elseif ($arg[1] -eq 'reset') {
                param_error 'max' 'alias_reset'
                return
            }
        }
        if ($arg[1] -eq 'list') {
            $data = [System.Collections.Generic.List[System.Object]]@()
            $max_len = ($PSCompletions.comp_cmd.keys | Measure-Object -Maximum Length).Maximum
            $PSCompletions.comp_cmd.keys | Where-Object { $_ -ne $PSCompletions.comp_cmd.$_ } | ForEach-Object {
                $data.Add(@{
                        content = "{0,-$($max_len + 3)} {1}" -f ($_, $PSCompletions.comp_cmd.$_)
                        color   = 'Green'
                    })
            }
            $PSCompletions.fn_less_table($data, ('Completion', 'Alias', $max_len))
        }
        elseif ($arg[1] -eq 'add') {
            if ($arg[2] -in $PSCompletions.comp_cmd.Keys) {
                $cmd = (Get-Command).Name | Where-Object { $_ -eq $arg[-1] }
                $alias = (Get-Alias).Name | Where-Object { $_ -eq $arg[-1] }
                if ($cmd -or $alias) {
                    $PSCompletions.fn_write((_replace ($PSCompletions.json.param_err + "`n" + $PSCompletions.json.alias_add_err)))
                    return
                }
                $arg[3] | Out-File ($PSCompletions.path.completions + '\' + $arg[2] + '\alias.txt') -Force -Encoding utf8
                $PSCompletions.fn_write((_replace $PSCompletions.json.alias_add_done))
            }
            else {
                $PSCompletions.fn_write((_replace $PSCompletions.json.alias_err))
            }
        }
        elseif ($arg[1] -eq 'rm') {
            $rm_list = $arg[2..($arg.Length - 1)]
            $del_list = [System.Collections.Generic.List[string]]@()
            $error_list = [System.Collections.Generic.List[string]]@()
            $alias_list = @{}
            $PSCompletions.comp_cmd.Keys | ForEach-Object {
                $alias = $PSCompletions.comp_cmd.$_
                if ($_ -ne $alias) { $alias_list.$alias = $_ }
            }
            foreach ($item in $rm_list) {
                if ($item -in $alias_list.Keys) {
                    $del_list.Add($item)
                    Remove-Item ($PSCompletions.path.completions + '\' + $alias_list.$item + '\alias.txt') -Force -ErrorAction SilentlyContinue
                }
                else { $error_list.Add($item) }
            }
            if ($error_list) {
                $PSCompletions.fn_write((_replace ($PSCompletions.json.alias_rm_err)))
            }
            if ($del_list) {
                $PSCompletions.fn_write((_replace $PSCompletions.json.alias_rm_done))
            }
        }
        else {
            $null = $PSCompletions.fn_confirm($PSCompletions.json.alias_reset_confirm, {
                    $del_list = [System.Collections.Generic.List[string]]@()
                    $reset_list = $PSCompletions.comp_cmd.keys | Where-Object { $PSCompletions.comp_cmd.$_ -ne $_ }
                    $alias_list = @{}
                    foreach ($_ in $reset_list) {
                        $alias = $PSCompletions.comp_cmd.$_
                        $del_list.Add($alias)
                        $alias_list.$alias = $_
                        Remove-Item ($PSCompletions.path.completions + '\' + $_ + '\alias.txt') -Force -ErrorAction SilentlyContinue
                    }
                    $PSCompletions.fn_set_config('root_cmd', 'psc')
                    if ($del_list) {
                        $PSCompletions.fn_write((_replace $PSCompletions.json.alias_reset_done))
                    }
                })
        }
    }
    function _order {
        if ($arg.Length -le 2) {
            if ($arg[1] -ne 'reset') {
                param_error 'err' 'order'
            }
            else {
                param_error 'min' 'order'
            }
            return
        }
        $del_list = [System.Collections.Generic.List[string]]@()
        $err_list = [System.Collections.Generic.List[string]]@()
        if ($arg[2] -eq '*') {
            $PSCompletions.comp_cmd.keys | ForEach-Object {
                $dir = PSCompletions which $_
                Remove-Item ($dir + '\order.json') -Force -ErrorAction SilentlyContinue
                $del_list.Add($_)
            }
        }
        else {
            $arg[2..($arg.Length - 1)] | ForEach-Object {
                if ($_ -in $PSCompletions.comp_cmd.keys) {
                    $dir = PSCompletions which $_
                    Remove-Item ($dir + '\order.json') -Force -ErrorAction SilentlyContinue
                    $del_list.Add($_)
                }
                else {
                    $err_list.Add($_)
                }
            }
        }
        if ($del_list) {
            $PSCompletions.fn_write((_replace $PSCompletions.json.order_done))
        }
        if ($err_list) {
            $PSCompletions.fn_write((_replace $PSCompletions.json.order_err))
        }
    }

    function _config {
        if ($arg.Length -lt 2) {
            param_error 'min' 'config'
            return
        }
        elseif ($arg.Length -gt 3) {
            param_error 'max' 'config'
            return
        }
        if ($arg.Length -eq 2) {
            if ($arg[1] -in $PSCompletions.config.Keys) {
                Write-Output ($PSCompletions.config[$arg[1]])
            }
            else {
                if ($arg[1] -eq 'reset') {
                    $c = [ordered]@{
                        root_cmd = 'psc'
                        github   = 'https://github.com/abgox/PSCompletions'
                        gitee    = 'https://gitee.com/abgox/PSCompletions'
                        lang     = (Get-WinSystemLocale).name
                        update   = 1
                        LRU      = 5
                    }
                    $flag = $PSCompletions.fn_confirm($PSCompletions.json.config_reset, {
                            $c | ConvertTo-Json | Out-File "$($PSCompletions.path.root)/env.json"
                        })
                    if ($flag) {
                        $PSCompletions.config = $c
                        $PSCompletions.fn_write((_replace $PSCompletions.json.config_reset_done))
                    }
                    return
                }
                $PSCompletions.fn_write((_replace $PSCompletions.json.config_err))
            }
        }
        else {
            if ($arg[1] -in $PSCompletions.config.Keys) {
                if ($arg[1] -eq 'root_cmd') {
                    $cmd = (Get-Command).Name | Where-Object { $_ -eq $arg[2] }
                    $alias = (Get-Alias).Name | Where-Object { $_ -eq $arg[2] }
                    if ($cmd -or $alias) {
                        $PSCompletions.fn_write((_replace ($PSCompletions.json.param_err + "`n" + $PSCompletions.json.alias_add_err)))
                        return
                    }
                    $arg[2] | Out-File ($PSCompletions.path.completions + '\PSCompletions\alias.txt') -Force -Encoding utf8
                }
                elseif ($arg[1] -eq 'language') {
                    $PSCompletions.comp_data = [ordered]@{}
                }
                $PSCompletions.fn_set_config($arg[1], $arg[2])
                $PSCompletions.fn_write((_replace $PSCompletions.json.config_done))
            }
            else {
                $PSCompletions.fn_write((_replace $PSCompletions.json.config_err))
            }
        }
    }
    function _completion {
        if ($arg.Length -lt 4) {
            param_error 'min' 'completion'
            return
        }
        if ($arg.Length -gt 4) {
            param_error 'max' 'completion'
            return
        }
        if ($arg[1] -notin $PSCompletions.comp_cmd.keys) {
            $PSCompletions.fn_write((_replace $PSCompletions.json.comp_err))
            return
        }
        if ($arg[2] -ne 'language' -and !$PSCompletions.comp_config.$($arg[1]).$($arg[2])) {
            $PSCompletions.fn_write((_replace $PSCompletions.json.comp_conf_err))
            return
        }

        $comp_config = $PSCompletions.comp_config
        if (!$comp_config.($arg[1])) {
            $comp_config.($arg[1]) = @{}
        }
        $old_value = $comp_config.($arg[1]).($arg[2])
        $comp_config.($arg[1]).($arg[2]) = $arg[3]

        $config = @{}
        $config.ui = $PSCompletions.ui.config
        $config.color = $PSCompletions.ui.color
        $config.comp_config = $comp_config

        $config | ConvertTo-Json | Out-File $PSCompletions.path.config -Encoding utf8
        $PSCompletions.fn_write((_replace $PSCompletions.json.comp_done))
    }

    function _ui {
        if ($arg[1] -notin @('theme', 'style' , 'custom', 'menu')) {
            param_error 'err' 'ui'
            return
        }
        switch ($arg[1]) {
            'theme' {
                $theme_list = @('default', 'magenta')
                if (!$arg[2]) {
                    param_error 'min' 'ui_theme'
                    return
                }
                if ($arg[3]) {
                    param_error 'max' 'ui_theme'
                    return
                }
                if ($arg[2] -notin $theme_list) {
                    param_error 'err' 'ui_theme'
                    return
                }
                switch ($arg[2]) {
                    'magenta' {
                        $PSCompletions.ui.color = @{
                            item          = 'DarkGray'
                            item_back     = 'White'
                            selected      = 'white'
                            selected_back = 'DarkMagenta'
                            filter        = 'DarkMagenta'
                            filter_back   = 'White'
                            border        = 'DarkMagenta'
                            border_back   = 'White'
                            status        = 'DarkMagenta'
                            status_back   = 'White'
                            tip           = 'DarkGray'
                            tip_back      = 'White'
                        }
                    }
                    # default
                    Default {
                        $PSCompletions.ui.color = @{
                            item          = 'Gray'
                            item_back     = 'Black'
                            selected      = 'white'
                            selected_back = 'DarkGray'
                            filter        = 'Yellow'
                            filter_back   = 'Black'
                            border        = 'DarkGray'
                            border_back   = 'Black'
                            status        = 'Blue'
                            status_back   = 'Black'
                            tip           = 'Cyan'
                            tip_back      = 'Black'
                        }
                    }
                }
                $PSCompletions.fn_write((_replace $PSCompletions.json.ui_theme_done))
            }
            'style' {
                switch ($arg[2]) {
                    'double_line_rect_border' {
                        $PSCompletions.ui.config.line = @{
                            horizontal   = [string][char]9552
                            vertical     = [string][char]9553
                            top_left     = [string][char]9556
                            bottom_left  = [string][char]9562
                            top_right    = [string][char]9559
                            bottom_right = [string][char]9565
                        }
                        $PSCompletions.fn_write((_replace $PSCompletions.json.ui_style_done))
                    }
                    'single_line_rect_border' {
                        $PSCompletions.ui.config.line = @{
                            horizontal   = [string][char]9472
                            vertical     = [string][char]9474
                            top_left     = [string][char]9484
                            bottom_left  = [string][char]9492
                            top_right    = [string][char]9488
                            bottom_right = [string][char]9496
                        }
                        $PSCompletions.fn_write((_replace $PSCompletions.json.ui_style_done))
                    }
                    'single_line_round_border' {
                        $PSCompletions.ui.config.line = @{
                            horizontal   = [string][char]9472
                            vertical     = [string][char]9474
                            top_left     = [string][char]9581
                            bottom_left  = [string][char]9584
                            top_right    = [string][char]9582
                            bottom_right = [string][char]9583
                        }
                        $PSCompletions.fn_write((_replace $PSCompletions.json.ui_style_done))
                    }
                    Default {
                        $PSCompletions.fn_write((_replace $PSCompletions.json.ui_style_err))
                    }
                }

            }
            'custom' {
                if (!$arg[2]) {
                    param_error 'min' 'ui_custom'
                    return
                }
                if ($arg[5]) {
                    param_error 'max' 'ui_custom'
                    return
                }
                if ($arg[2] -notin @('color', 'line', 'config')) {
                    param_error 'err' 'ui_custom'
                    return
                }
                if ($arg[2] -eq 'color') {
                    $available_color = @(
                        'White', 'Black',
                        'Gray', 'DarkGray'
                        'Red', 'DarkRed',
                        'Green', 'DarkGreen',
                        'Blue', 'DarkBlue',
                        'Cyan', 'DarkCyan',
                        'Yellow', 'DarkYellow',
                        'Magenta', 'DarkMagenta'
                    )
                    if ($arg[4] -notin $available_color) {
                        $PSCompletions.fn_write((_replace $PSCompletions.json.color_err))
                        return
                    }
                    $old_value = $PSCompletions.ui.color.($arg[3])
                    $PSCompletions.ui.color.($arg[3]) = $arg[4]
                    $PSCompletions.fn_write((_replace $PSCompletions.json.color_done))
                }
                elseif ($arg[2] -eq 'line') {
                    # line
                    $old_value = $PSCompletions.ui.config.line.($arg[3])
                    $PSCompletions.ui.config.line.($arg[3]) = $arg[4]
                    $PSCompletions.fn_write((_replace $PSCompletions.json.line_done))
                }
                else {
                    # config
                    if ($arg[3] -eq 'filter_symbol') {
                        if ($arg[4].Length -ne 2) {
                            $PSCompletions.fn_write((_replace $PSCompletions.json.filter_symbol_err))
                            return
                        }
                    }
                    $old_value = $PSCompletions.ui.config.($arg[3])
                    $PSCompletions.ui.config.($arg[3]) = $arg[4]
                    $PSCompletions.fn_write((_replace $PSCompletions.json.ui_config_done))
                }
            }
            # default: menu
            Default {
                if (!$arg[2]) {
                    param_error 'min' 'ui_menu'
                    return
                }
                if ($arg[3]) {
                    param_error 'max' 'ui_menu'
                    return
                }
                if ($arg[2] -notin @('default', 'powershell')) {
                    param_error 'err' 'ui_menu'
                    return
                }
                if ($arg[2] -eq 'default') {
                    $PSCompletions.ui.config.enable_ui = 1
                }
                else {
                    $PSCompletions.ui.config.enable_ui = 0
                }
                $PSCompletions.fn_write((_replace $PSCompletions.json.ui_menu_done))
            }
        }

        $config = @{}
        $config.ui = $PSCompletions.ui.config
        $config.color = $PSCompletions.ui.color
        $config.comp_config = $PSCompletions.comp_config

        $config | ConvertTo-Json | Out-File $PSCompletions.path.config -Encoding utf8
    }
    function _help {
        $PSCompletions.fn_write((_replace $PSCompletions.json.description))
    }

    $need_init = $true
    switch ($arg[0]) {
        'list' {
            _list
            $need_init = $null
        }
        'add' {
            _add
        }
        'rm' {
            _remove
        }
        'update' {
            _update
        }
        'search' {
            _search
            $need_init = $null
        }
        'which' {
            _which
            $need_init = $null
        }
        'alias' {
            _alias
        }
        'order' {
            _order
        }
        'config' {
            _config
        }
        'completion' {
            _completion
        }
        'ui' {
            _ui
        }
        default {
            if ($arg.Length -eq 1) {
                $PSCompletions.fn_write((_replace $PSCompletions.json.cmd_error))
            }
            else { _help }
            $need_init = $null
        }
    }
    if ($need_init) {
        $PSCompletions.fn_init()
    }
}