alias help=murex-docs
alias builtin=fexec builtin
alias command=exec

function h {
    # Output the murex history log in a human readable format
    history -> foreach { -> set json line; out "$line[Index]: $line[Block]" } -> cast *
}

function aliases {
   # Output the aliases in human readable format
   runtime: --aliases -> formap name alias {
        $name -> sprintf: "%10s => ${esccli @alias}\n"
	} -> cast: str
}

test unit function aliases {
    "PreBlock": ({
        alias ALIAS_UNIT_TEST=example param1 param2 param3
    }),
    "StdoutRegex": "([- _0-9a-zA-Z]+ => .*?\n)+",
    "StdoutType": "str",
    "PostBlock": ({
        !alias ALIAS_UNIT_TEST
    })
}

autocomplete: set cd { [{
    "IncDirs": true
}] }

autocomplete: set mkdir { [{
    "IncDirs": true,
    "AllowMultiple": true
}] }

autocomplete: set rmdir { [{
    "IncDirs": true,
    "AllowMultiple": true
}] }

autocomplete: set exec { [
    {
        "IncFiles": true,
        "IncDirs": true,
        "IncExePath": true
    },
    {
        "NestedCommand": true
    }
] }

autocomplete: set format { [{
    "Dynamic": ({ runtime: --marshallers })
}] }

private autocomplete.data-types {
    # Returns all murex data-types compiled
    runtime: --readarray         -> format: str
    runtime: --readarraywithtype -> format: str
    runtime: --writearray        -> format: str
    runtime: --readmap           -> format: str
    runtime: --marshallers       -> format: str
    runtime: --unmarshallers     -> format: str
}

test unit private autocomplete.data-types {
    "StdoutRegex": (^(([a-z0-9]+|\*)\n)+),
    "StdoutType":  "str",
    "StdoutGreaterThan": 1,
    "StdoutIsArray": true
}

autocomplete: set cast { [{
    "Dynamic": ({ autocomplete.data-types })
}] }

autocomplete: set tout { [{
    "Dynamic": ({ autocomplete.data-types })
}] }

private autocomplete.variables.locals {
    # Returns all local variable names
    cast: str
    runtime: --variables -> formap k v { out $k }
}

private autocomplete.variables.globals {
    # Returns all global variable names
    cast: str
    runtime: --globals -> formap k v { out $k }
}

private autocomplete.variables {
    # Returns all global and local variable names
    cast: str
    autocomplete.variables.locals
    autocomplete.variables.globals
}

test unit private autocomplete.variables %{
    PreBlock: '{
        GLOBAL.MUREX_UNIT_TEST = "foobar"
    }'
    PostBlock: '{
        !global MUREX_UNIT_TEST
    }'
    StdoutRegex:       '([_a-zA-Z0-9]+\n)+'
    StdoutType:        str
    StdoutGreaterThan: 1
    StdoutIsArray:     true
}

autocomplete: set set { [{
    "Dynamic": ({ autocomplete.variables.locals }),
    "AnyValue": true
}] }

autocomplete: set !set { [{
    "Dynamic": ({ autocomplete.variables.locals })
}] }

autocomplete: set global { [{
    "Dynamic": ({ autocomplete.variables.globals }),
    "AnyValue": true
}] }

autocomplete: set !global { [{
    "Dynamic": ({ autocomplete.variables.globals })
}] }

autocomplete: set export { [
    {
        "Dynamic": ({ autocomplete.variables }),
        "AnyValue": true
    },
    {
        "IncDirs": true
    }
] }

autocomplete: set !export { [{
    "DynamicDesc": ({ runtime --exports })
}] }

autocomplete: set unset { [{
    "DynamicDesc": ({ runtime --exports })
}] }

autocomplete: set "[" { [{
    "AnyValue": true,
    "AllowMultiple": true,
    "ExecCmdline": true,
    "Dynamic": ({
        switch ${ get-type: stdin } {
            case * {
                #<stdin> -> tabulate -> [ 0: ] -> format json -> [ 0 ]
                <stdin> -> [ 0: ] -> format json -> [ 0 ] -> append "]"
            }

            case csv {
                <stdin> -> [ 0: ] -> format json -> [ 0 ] -> append "]"
            }
            
            case jsonl {
                <stdin> -> [ 0 ] -> set header
                $header -> cast utf8 -> [ 0 -1 ] -> set jsonl_format
                if { = jsonl_format==`[]` } then {
                    tout json $header -> append "]"
                }
            }

            catch {
                <stdin> -> formap k v { out $k } -> cast str -> append "]"
            }
        }
    })
}] }

autocomplete: set "[[" { [{
    "AnyValue": true,
    "ExecCmdline": true,
    "AutoBranch": true,
    "Dynamic": ({ -> struct-keys -> append "]]" })
} ]}

private autocomplete.config.get.apps {
    # Returns all app names for the 'app' field in `config`
    config: -> formap k v { out $k } -> cast: str -> msort
}

test unit private autocomplete.config.get.apps {
    "StdoutRegex": (shell),
    "StdoutType":  "str",
    "StdoutGreaterThan": 1,
    "StdoutIsArray": true
}

private autocomplete.config.get.keys {
    # Returns all keys for the 'app' field in `config`
    config -> [ $PARAMS[0] ] -> formap k v { out $k } -> cast: str
}

test unit private autocomplete.config.get.keys {
    "Parameters": [ "shell" ],
    "StdoutRegex": (prompt),
    "StdoutType":  "str",
    "StdoutGreaterThan": 1,
    "StdoutIsArray": true
}

autocomplete: set config { [{
    "Flags": [ "get", "set", "eval", "define", "default" ],
    "FlagValues": {
        "get": [
            { "Dynamic": ({ autocomplete.config.get.apps }) },
            { "Dynamic": ({ autocomplete.config.get.keys $PARAMS[1] }) }
        ],               
        "set": [
            { "Dynamic": ({ autocomplete.config.get.apps }) },
            { "Dynamic": ({ autocomplete.config.get.keys $PARAMS[1] }) },
            { "Dynamic": ({
				switch {
					case { ${ config -> [[ /$PARAMS[1]/$PARAMS[2]/Data-Type ]] } == "bool" } {
						ja [true,false]
					}

					case { config -> [ $PARAMS[1] ] -> [ $PARAMS[2] ] -> [ <!null> Options ] } {
						config -> [ $PARAMS[1] ] -> [ $PARAMS[2] ] -> [ Options ]
					}
					
                	catch {
						out ${ config -> [ $PARAMS[1] ] -> [ $PARAMS[2] ] -> [ Default ]}
					}
				}
			}) }
        ],
        "eval": [
            { "Dynamic": ({ autocomplete.config.get.apps }) },
            { "Dynamic": ({ autocomplete.config.get.keys $PARAMS[1] }) }
        ],
        "default": [
            { "Dynamic": ({ autocomplete.config.get.apps }) },
            { "Dynamic": ({ autocomplete.config.get.keys $PARAMS[1] }) }
        ]
    }
}] }

autocomplete: set test-autocomplete { [{
    "Flags": [ "111", "222", "333", "444" ],
    "FlagValues": {
        "111": [
            { "Dynamic": ({ out aaa\nbbb\nccc\n$PARAMS }) },
            { "Dynamic": ({ out ddd\neee\nfff\n$PARAMS }) }
        ],               
        "222": [
            { "Dynamic": ({ out ggg\nhhh\niii\n$PARAMS }) },
            { "Dynamic": ({ out jjj\nkkk\nlll\n$PARAMS }) }
        ],
        "333": [
            { "Dynamic": ({ out mmm\nnnn\nooo\n$PARAMS }) },
            { "Dynamic": ({ out ppp\nqqq\nrrr\n$PARAMS }) }
        ],
        "444": [
            { "Dynamic": ({ out sss\nttt\nuuu\n$PARAMS }) },
            { "Dynamic": ({ out vvv\nwww\nxxx\n$PARAMS }) }
        ]
    }
}] }

autocomplete: set !config { [
    { "Dynamic": ({ autocomplete.config.get.apps }) },
    { "Dynamic": ({ autocomplete.config.get.keys $PARAMS[0] }) }
] }


autocomplete: set event { [
    {
        "Dynamic": ({ runtime: --events -> formap k v { out $k } })
    }
] }

autocomplete: set !event { [
    {
        "Dynamic": ({ runtime: --events -> formap k v { out $k } -> msort })
    },
    {
        "Dynamic": ({ runtime: --events -> [ $ARGS[1] ] -> formap k v { out $k } -> msort })
    }
] }

private autocomplete.alias {
    # Returns a map of all alises and the code they execute
    runtime: --aliases -> formap --jmap name value { $name } { out @value }
}

test unit private autocomplete.alias {
    "StdoutRegex": (jobs),
	"StdoutType":  "json",
    "StdoutGreaterThan": 1,
    "StdoutIsMap": true
}

autocomplete: set !alias { [{
    "DynamicDesc": ({ autocomplete.alias }),
    "ListView": true
}] }

private autocomplete.functions {
    # Returns a map of all murex public functions
    runtime: --functions -> formap --jmap k v { $k } { out: $v[summary] }
}

test unit private autocomplete.functions {
    "PreBlock": ({
        function unit.test.autocomplete.functions {
            out "This is only a dummy function for testing"
        }
    }),
    "PostBlock": ({
        !function unit.test.autocomplete.functions
    }),
    "StdoutRegex": (unit.test.autocomplete.functions),
	"StdoutType":  "json",
    "StdoutIsMap": true,
    "StdoutGreaterThan": 1
}

autocomplete: set !function { [{
    "DynamicDesc": ({ autocomplete.functions }),
    "ListView": true
} ]}

private autocomplete.privates {
    # Returns a map of all murex private functions
    runtime: --privates -> struct-keys: 3 -> regexp m,/.*?/.*?/, -> foreach --jmap private { $private } { runtime: --privates -> [[ $private/Summary ]] }
}

test unit private autocomplete.privates {
	"StdoutType":  "json",
    "StdoutGreaterThan": 1,
    "StdoutIsMap": true
}

private autocomplete.builtins {
    # Returns a map of all murex builtins
    runtime --builtins -> foreach --jmap builtin { $builtin } { murex-docs --summary $builtin }
}

test unit private autocomplete.builtins {
	"StdoutType":  "json",
    "StdoutGreaterThan": 1,
    "StdoutIsMap": true
}

autocomplete: set autocomplete { [{
    "Flags": [ "get", "set" ],
    "FlagValues": {
        "get": [{
            "Dynamic": ({
                runtime: --autocomplete -> formap: cmd ! { out $cmd } -> cast: str
            })
        }]
    }
}] }

autocomplete: set debug { [{
    "Flags": ["on", "off"]
}] }

autocomplete: set murex-package {
    [{
        "FlagsDesc": {
            "install": "Installs a package from a user defined URI",
            "update":  "Updates all installed packages",
            "import":  "Import packages described in a backup package DB from user defined URI or local path",
            "enable":  "Enables a previously disabled package or module",
            "disable": "Disables a previously enabled package or module",
            "reload":  "Reloads all enabled modules",
            "status":  "Returns the version status of locally installed packages",
            "list":    "Returns a list of indexed packages/modules (eg what's enabled or disabled)",
            "cd":      "Changes working directory to a package's install location"
        },
        "FlagValues": {
            "import": [{
                "IncFiles": true
            }],
            "enable": [{
                "DynamicDesc": ({ murex-package: list disabled }),
                "ListView": true,
                "AutoBranch": true
            }],
            "disable": [{
                "DynamicDesc": ({ murex-package: list enabled }),
                "ListView": true,
                "AutoBranch": true
            }],
            "list": [{
                "Flags": [ "enabled", "disabled", "loaded", "not-loaded", "packages" ]
            }],
            "cd": [{
                "Dynamic": ({ murex-package: list packages })
            }]
        }
    }]
}

alias: builtins=runtime --builtins

private: test.alias.builtins {
    # Wrapper function around the alias for `builtins` for unit testing
    builtins
}

test: unit private test.alias.builtins {
    "StdoutRegex": ("[a-z0-9]+",),
    "StdoutType":  "json",
    "StdoutGreaterThan": 1,
    "StdoutIsArray": true
}

autocomplete: set murex-docs { [{
    "Dynamic": ({ builtins }),
	"Flags": [ "--summary" ],
	"FlagValues": {
		"--summary": [{
			"Dynamic": ({ builtins })
		}]
	}
}] }

private: autocomplete.aliases.and.builtins {
    # Returns a list of aliases and builtins
    runtime: --aliases -> formap k ! { out: $k } -> cast str
    builtins -> format str
}

test: unit private autocomplete.aliases.and.builtins {
    "StdoutType":  "str",
    "StdoutGreaterThan": 1,
    "StdoutIsArray": true
}

autocomplete: set summary { [{
    "IncExePath": true,
    "Dynamic": ({
        autocomplete.aliases.and.builtins
    })
}] }

config: eval shell safe-commands {
    -> alter --merge / ([
        "builtins", "jobs"
    ])
}

!if { man-summary atom } then {
    summary atom "Github Atom - Text editor / IDE"
}

!if { man-summary code } then {
    summary code "Microsoft Visual Studio Code - Text editor / IDE"
}

function sprintf {
    # This is a wrapper around the OS's default `printf`, replacing the now deprecated builtin of the same name
    -> set params
    switch {
        case  { $params -> ! } { err: "No parameters passed via STDIN" }
        case  { $ARGS[1]-> ! } { err: "No string supplied to print"    }
        catch {
            printf "$ARGS[1]\n" @params
        }
    }
}

summary docgen "Document generator for murex development"

autocomplete set docgen { [
    {
        "AllowMultiple": true,
        "Optional": true,
        "FlagsDesc": {
            "-panic": "Write a stack trace on error",
            "-readonly": "Don't write output to disk. Use this to test the config",
            "-verbose": "Verbose output (all log messages inc warnings)",
            "-version": "Output docgen version number and exit",
            "-warning": "Display warning messages (will also return a non-zero exit status if warnings found)"
        }
    },
    {
        "FlagsDesc": {
            "-config": "Location of the base docgen config file"
        },
        "FlagValues": {
            "-config": [{
                "IncFiles": true
            }]
        }
    }
] }

config define open image {
    "Description":  "Which mode to render images to the terminal.",
    "DataType":     "str",
    "Default":      "auto",
    "Options":      [ "auto", "compatible", "kitty", "iterm", "terminology", "sixel" ]
}

openagent set image {
    $ARGS[1] -> set file
    config get open image -> set mode

    if { = mode==`auto` } {
        switch {
            case { $TMUX }                        { set mode=compatible }
            case { $ITERM_PROFILE }               { set mode=iterm }
            case { $TERM_PROGRAM == "iTerm.app" } { set mode=iterm }
            case { $KITTY_WINDOW_ID }             { set mode=kitty }
            case { $TERM == "xterm-kitty" }       { set mode=kitty }
            case { $TERMINOLOGY }                 { set mode=terminology }
            catch                                 { set mode=compatible }
        }
    }

    # If Kitty but running inside a screen / tmux session, fallback to compatible.
    # We do this because tmux doesn't support reporting screen sizes via the TIOCGWINSZ ioctl.
    if { and { = mode==`kitty` } { = TERM==`screen` } } {
        set mode=compatible
    }

    switch $mode {
        case compatible {
            open-image $file
        }

        case kitty {
            try {
                kitty icat $file
            }
            catch {
                err "Error running `kitty`. Please check `kitty` is installed."
            }
        }

        case iterm {
            if { $TERM =~ '^(screen|tmux)' } then {
                if { ${tmux show -gw allow-passthrough} == "allow-passthrough on" } then {
                    osc = '{ESC}Ptmux;{ESC}{ESC}]'
                    st  = '{BELL}{ESC}\'
                } else {
                    open-image $file
                    break switch
                }
            } else {
                osc = '{ESC}]'
                st  = '{BELL}'
            }

            out "$(osc)1337;File=inline=1:${base64 -i $file -o -}$(st)"
        }

        case terminology {
            try {
                tycat -c $file
            }
            catch {
                err "Error running `tycat`. Please check `terminology` is installed."
            }
        }

        case sixel {
            try {
                img2sixel $file
            }
            catch {
                err "Error running `img2sixel`. Please check `libsixel` is installed."
            }
        }

        catch {
            out "Invalid rendering method. Please define in `config set open image` - 'auto' is recommended."
        }
    }
}
