# Author:       Li Junhao       l@x-cmd.com
# shellcheck    shell=sh        disable=SC2039,SC1090,SC3043,SC2263

# license:      GPLv3

# Section: bootrc
# shellcheck disable=SC2154

## log store
___X_CMD_LOG_STORE_DEFAULT="${___X_CMD_ROOT_LOG}/store"


## main
___x_cmd_log___return(){
    [ -n "$N" ] || return 1
    [ -n "$M" ] || return 1
    ___x_cmd log :"${N#:}" error -m "$M"
    return 1
}

# Ensure 130 is returned ...
___x_cmd_log___return_auto(){
    [ -n "$N" ] || return 1
    [ -n "$M" ] || return 1
    ___x_cmd_log_pr :"${N#:}" error -m "$M"

    case "$E" in
        130)        return 130   ;;
        *)          return 1     ;;
    esac
}

alias log:return='E="$?" ___x_cmd_log___return_auto || return'
alias log:panic='E="$?" ___x_cmd_log___return_auto || return'

alias log:exit='___x_cmd_log___return || exit'

# NOTICE: delibrate using || and enforce ___x_cmd_log___return return non-zero exit code
alias log:ret:1='___x_cmd_log___return || return 1 2>/dev/null || exit 1'
alias log:ret:64='___x_cmd_log___return || return 64 2>/dev/null || exit 64'


## exec
alias log:exec="___x_cmd_log exec"

## trace

alias x:trace='{ local ___X_CMD_LOG_DEBUG="${___X_CMD_LOG_DEBUG}"; local ___X_CMD_LOG_TRACE_LINE_LOCAL="${___X_CMD_LOG_TRACE_LINE_LOCAL}"; } 2>/dev/null; ___x_cmd_log trace'
alias log:trace='{ local ___X_CMD_LOG_DEBUG="${___X_CMD_LOG_DEBUG}"; local ___X_CMD_LOG_TRACE_LINE_LOCAL="${___X_CMD_LOG_TRACE_LINE_LOCAL}"; } 2>/dev/null; ___x_cmd_log trace'

alias x:trace:evex="___x_cmd_log trace_evex"
alias log:trace:evex="___x_cmd_log trace_evex"

alias x:trace:time="___x_cmd_log trace_time"
alias log:trace:time="___x_cmd_log trace_time"

## sub

alias log:sub:init="
    local x_LOG_FLAT=\"\${x_LOG_FLAT}\" 2>/dev/null 1>&2;
    local ___X_CMD_LOG_YML_INDENT=\"\${___X_CMD_LOG_YML_INDENT}\" 2>/dev/null 1>&2;
    local ___X_CMD_LOG_YML_PID_LIST=\"\${___X_CMD_LOG_YML_PID_LIST}\" 2>/dev/null 1>&2;
    ___x_cmd_log sub init"

alias log:sub:fini="___x_cmd_log sub fini"
alias log:sub:reset="___x_cmd_log sub reset"

# EndSection

# Section: printer

# TODO: To move xrc_log
___X_CMD_LOG_C_TF="${___X_CMD_LOG_C_TF:-""}"
export ___X_CMD_LOG_C_TF

___X_CMD_LOG__TIMESTAMP_FORMAT=      # "+%H:%M:%S"      # Enable Timestamp.

export ___X_CMD_LOG_C_DEBUG="${___X_CMD_LOG_C_DEBUG:-"\\033[2;35m"}"
export ___X_CMD_LOG_C_INFO="${___X_CMD_LOG_C_INFO:-"\\033[32m"}"
export ___X_CMD_LOG_C_WARN="${___X_CMD_LOG_C_WARN:-"\\033[1;33m"}"
export ___X_CMD_LOG_C_ERROR="${___X_CMD_LOG_C_ERROR:-"\\033[1;31m"}"

___X_CMD_LOG_C_TIMESTAMP_BEGIN="${___X_CMD_LOG_C_WARN:-"["}"
___X_CMD_LOG_C_TIMESTAMP_END="${___X_CMD_LOG_C_WARN:-"]"}"

___x_cmd_log_pr(){
    case "${2:?Provide level}" in
        debug)      eval "test -z \"\$___X_CMD_LOG_DEBUG\" -a 1 -lt \"\${___X_CMD_LOG__${1#:}:-\${___X_CMD_LOG_LEVEL_DEFAULT}}\"" || { local O="${1#:}"; shift 2; L=D C="$___X_CMD_LOG_C_DEBUG"                   ___x_cmd_log_0 "$@"; } ;;
        info)       eval "test -z \"\$___X_CMD_LOG_DEBUG\" -a 2 -lt \"\${___X_CMD_LOG__${1#:}:-\${___X_CMD_LOG_LEVEL_DEFAULT}}\"" || { local O="${1#:}"; shift 2; L=I C="$___X_CMD_LOG_C_INFO"  C_MSG="\\033[0m"  ___x_cmd_log_0 "$@"; } ;;
        warn)       eval "test -z \"\$___X_CMD_LOG_DEBUG\" -a 3 -lt \"\${___X_CMD_LOG__${1#:}:-\${___X_CMD_LOG_LEVEL_DEFAULT}}\"" || { local O="${1#:}"; shift 2; L=W C="$___X_CMD_LOG_C_WARN"  C_BG="\\033[7m"   ___x_cmd_log_0 "$@"; } ;;
        error)      eval "test -z \"\$___X_CMD_LOG_DEBUG\" -a 4 -lt \"\${___X_CMD_LOG__${1#:}:-\${___X_CMD_LOG_LEVEL_DEFAULT}}\"" || { local O="${1#:}"; shift 2; L=E C="$___X_CMD_LOG_C_ERROR" C_BG="\\033[7m"   ___x_cmd_log_0 "$@"; } ;;
        *)          eval "test -z \"\$___X_CMD_LOG_DEBUG\" -a 3 -lt \"\${___X_CMD_LOG__${1#:}:-\${___X_CMD_LOG_LEVEL_DEFAULT}}\"" || { local O="${1#:}"; shift 1; L=I C="$___X_CMD_LOG_C_INFO"  C_MSG="\\033[0m"  ___x_cmd_log_0 "$@"; } ;;
    esac
}

___x_cmd_log_pr_debug(){    L=D C="$___X_CMD_LOG_C_DEBUG"                   ___x_cmd_log_0 "$@" ;   }
___x_cmd_log_pr_info(){     L=I C="$___X_CMD_LOG_C_INFO"  C_MSG="\\033[0m"  ___x_cmd_log_0 "$@" ;   }
___x_cmd_log_pr_warn(){     L=W C="$___X_CMD_LOG_C_WARN"  C_BG="\\033[7m"   ___x_cmd_log_0 "$@" ;   }
___x_cmd_log_pr_error(){    L=E C="$___X_CMD_LOG_C_ERROR" C_BG="\\033[7m"   ___x_cmd_log_0 "$@" ;   }


### Printer for attribute

___X_CMD_LOG_OUTFILE=""
___x_cmd_log_0(){
    if [ -z "$___X_CMD_LOG_OUTFILE" ]; then
        ___x_cmd_log_0_ "$@"
    else
        ___x_cmd_log_0_ 2>"$___X_CMD_LOG_OUTFILE"
    fi
}

___x_cmd_log_0_(){
    local IFS=" "

    # ${___X_CMD_TRACE_LINE_LOCAL+<${___X_CMD_TRACE_LINE_LOCAL#"$O"}>
    local track="${___X_CMD_TRACE_LINE_LOCAL}"
    [ -z "$track" ] || track="<${track#"$O/"}>"

    local x_=""
    case "$1" in
        -*)         ___x_cmd_log_0_advanced_ "$@" ;;
        *)          x_="$*" ;;
    esac

    local pid_list_str=""
    [ -z "${___X_CMD_LOG_YML_PID_LIST}" ] || pid_list_str=" $___X_CMD_LOG_YML_PID_LIST"

    local timestamp=""
    [ -z "$___X_CMD_LOG__TIMESTAMP_FORMAT" ] || timestamp=" $(___x_cmd_cmds date "${___X_CMD_LOG__TIMESTAMP_FORMAT}")"

    if [ -n "$___X_CMD_LOG_C_TF" ] || [ -t 2 ]; then
        [ -z "$timestamp" ] || timestamp="${___X_CMD_LOG_C_TIMESTAMP_BEGIN}${timestamp}${___X_CMD_LOG_C_TIMESTAMP_END}"
        printf  "%s${timestamp}%s ${C}${C_BG}%s\033[0m${C}|%s$track: ${C_MSG}%s\033[0m\n" \
                "${___X_CMD_LOG_YML_INDENT}-" "${pid_list_str}" "$L" "$O" "$x_"
    else
        printf  "%s${timestamp}%s %s|%s: %s\n" \
                "${___X_CMD_LOG_YML_INDENT}-" "${pid_list_str}" "$L" "$O" "$x_"
    fi >&2
}

# x:error -c "$code" -t "error-tag" --time "$time" -e os,arch,abc
# x:error -e "$error-tag" -m "error msg" -c "$code" --time "$time" -e os,arch,abc

# Using outerr to record the
# x log run :scotty -- cmd1 arg1 arg2 arg3
# Using outerr to record, then make into the log stream


___x_cmd_log_0_advanced_(){
    local code=     # single line
    local more=
    local help=     # single line.

    local sub=

    local str_more="more:"
    if [ -n "$___X_CMD_LOG_C_TF" ] || [ -t 2 ]; then
        str_more="${___X_CMD_UNSEENCHAR_033}[36m${str_more}${___X_CMD_UNSEENCHAR_033}[0m"
    fi

    while [ $# -gt 0 ]; do
        case "$1" in
            -c)
                    code="${2}"
                    shift 2 || return $? ;;
            -h)
                    help="${2}"
                    shift 2 || return $? ;;
            --m:*)
                    ___X_CMD_LOG_0_INDENT="${___X_CMD_LOG_YML_INDENT}    "\
                        ___x_cmd_log_0_ml_ "${2}"
                    more="${more}${___X_CMD_LOG_YML_INDENT}    ${1#--m:}: ${x_}
"
                    shift 2 || return $?
                    x_=
                    ;;
            -m)     ___X_CMD_LOG_0_INDENT="${___X_CMD_LOG_YML_INDENT}  " \
                    ___x_cmd_log_0_ml_ "$2"

                    shift 2 || return $?
                    ;;

            --y:*)
                    ___X_CMD_LOG_0_INDENT="${___X_CMD_LOG_YML_INDENT}    "\
                        ___x_cmd_log_0_yml_ "${2}"
                    more="${more}${___X_CMD_LOG_YML_INDENT}    ${1#--y:}: ${x_}
"
                    shift 2 || return $?
                    x_=
                    ;;
            -y)     ___X_CMD_LOG_0_INDENT="${___X_CMD_LOG_YML_INDENT}  " \
                    ___x_cmd_log_0_yml_ "$2"

                    shift 2 || return $?
                    ;;

            -S)
                    sub="S:"
                    shift 1
                    ;;
            --)     break ;;
            --*)
                    more="${more}${___X_CMD_LOG_YML_INDENT}    ${1#--}: ${2}
"
                    shift 2 || return $?
                    ;;
            *)      break;;
        esac
    done

    [ -z "$1" ] || x_="${x_}${1}"

    [ -z "$help" ] || {
        local str_help="help:"
        if [ -n "$___X_CMD_LOG_C_TF" ] || [ -t 2 ]; then
            str_help="${___X_CMD_UNSEENCHAR_033}[31m${str_help}${___X_CMD_UNSEENCHAR_033}[0m"
        fi
        x_="$x_
${___X_CMD_LOG_YML_INDENT}  ${str_help} ${help}"
    }

    [ -z "$code" ] || {
        local str_code="code:"
        if [ -n "$___X_CMD_LOG_C_TF" ] || [ -t 2 ]; then
            str_code="${___X_CMD_UNSEENCHAR_033}[36m${str_code}${___X_CMD_UNSEENCHAR_033}[0m"
        fi
        # TODO: If code not eq 0, use red color.
        x_="$x_
${___X_CMD_LOG_YML_INDENT}  ${str_code} ${code}"
    }

    [ -z "$more" ] || {
        local str_more="more:"
        if [ -n "$___X_CMD_LOG_C_TF" ] || [ -t 2 ]; then
            str_more="${___X_CMD_UNSEENCHAR_033}[36m${str_more}${___X_CMD_UNSEENCHAR_033}[0m"
        fi
        # TODO: colorize key
        x_="${x_}
${___X_CMD_LOG_YML_INDENT}  ${str_more}
${more%?}"
    }

    [ -z "$sub" ] || {
        x_="${x_}
${___X_CMD_LOG_YML_INDENT}  S:"
    }
}

# Mode: multiple line mode
___x_cmd_log_0_ml_(){
    local indent="${___X_CMD_LOG_0_INDENT}  ";  x_="|"
    local line; while IFS= read -r line; do
        x_="${x_}
${indent}${line}"
    done <<A
$1
A
}

___x_cmd_log_0_yml_(){
    local indent="${___X_CMD_LOG_0_INDENT}  ";  x_=""
    local line; while IFS= read -r line; do
        if [ -z "$x_" ]; then
            x_="${line} "
        else
            x_="${x_}
${indent}${line}"
        fi
    done <<A
$1
A
}



# EndSection

# Section: util
___x_cmd_log_level_code_to_level_name(){
    case "${1:?Provide code}" in
        1)      level_name=debug ;;
        2)      level_name=info ;;
        3)      level_name=warn ;;
        4)      level_name=error ;;
        5)      level_name=none ;;
        *)      level_name=info ;;
    esac
}

___X_CMD_LOG_LEVEL_DEFAULT=2
___x_cmd_log_level_default(){
    case "${1:?Provide name}" in
        d|debug|1)  ___X_CMD_LOG_LEVEL_DEFAULT=1 ;;
        i|info|2)   ___X_CMD_LOG_LEVEL_DEFAULT=2 ;;
        w|warn|3)   ___X_CMD_LOG_LEVEL_DEFAULT=3 ;;
        e|error|4)  ___X_CMD_LOG_LEVEL_DEFAULT=4 ;;
        n|none|5)   ___X_CMD_LOG_LEVEL_DEFAULT=5 ;;
        *)
            ___x_cmd_log_pr x error "Fail to set default level for unknown [level name=$1]"
            return 1
    esac
}

___x_cmd_log_init(){
    ___x_cmd_log_init_inner "$@" 1 2 3 4
}

___x_cmd_log_init_inner(){
    local logger_name="${1:?Provide logger name}"
    shift

    local var="___X_CMD_LOG__$logger_name"

    local code="
$var=\${$var:-2}
___X_CMD_LOG__0_$logger_name=

${logger_name}_log(){     ___x_cmd_log_pr $logger_name \"\$@\";   }
"

    local level_name
    local level_code; for level_code in "$@"; do
        ___x_cmd_log_level_code_to_level_name "$level_code"
        code="$code
alias ${logger_name}:$level_name='test -z \"\$___X_CMD_LOG_DEBUG\" -a $level_code -lt \"\${___X_CMD_LOG__${logger_name}:-2}\" || O=${logger_name} ___x_cmd_log_pr_${level_name}'
"
    done

    eval "$code"
}

# EndSection

___x_cmd_log___main() {
    case "$1" in
        :*)         ___x_cmd_log_pr "$@" ;;
        init)       shift;      ___x_cmd_log_init           "$@" ;;

        *)          [ -n "$___X_CMD_LOG_OTHER_LOADED" ]     ||  xrc:mod log/lib/run
                    ___x_cmd_log____other        "$@" ;;
    esac
}

___x_cmd_log(){
    ___x_cmd_log___main "$@"
}

___x_cmd_log_init log

# xrc setmain ___x_cmd_log
