#!/bin/sh
# hiraidadm auto-completion shell script

# 冒号左切除函数 COMPREPLY 自动补全在有冒号的情况下会把分隔符前后所有内容全部补全
# 达不到补后缀的效果，故而需要切分前缀多余内容
sp_ltrim_colon_completions()
{
    local i=${#COMPREPLY[*]}
    ((i == 0)) && return 0
    if [[ $1 == *:* && $COMP_WORDBREAKS == *:* ]]; then
        # Remove colon-word prefix from COMPREPLY items
        local colon_word=${1%"${1##*:}"}
        COMPREPLY=("${COMPREPLY[@]}")
        while ((i-- > 0)); do
            COMPREPLY[i]=${COMPREPLY[i]#"$colon_word"}
        done
    fi
}

sp_ltrim_equal_completions()
{
    local i=${#COMPREPLY[*]}
    ((i == 0)) && return 0
    if [[ $1 == *=* && $COMP_WORDBREAKS == *=* ]]; then
        local equal_word=${1%"${1##*=}"}
        COMPREPLY=("${COMPREPLY[@]}")
        while ((i-- > 0)); do
            COMPREPLY[i]=${COMPREPLY[i]#"$equal_word"}
        done
    fi
}

# hiraidadm 补全函数
function __hiraidadm() {
    local spadmsups spadmsups_dfx spadmsups_rel
    # 自动补全脚本在打包时将按需保留需要的原始支持命令行，创建新文件 _hiraidadm
    spadmsups_rel="
hiraidadm show ctrlcount
hiraidadm show log path=x out_file=x
hiraidadm show allctrl
hiraidadm help
hiraidadm -h
hiraidadm --help
hiraidadm cx create vd raidx name=x drives=x size=x size=max_left pdpersg=x susize=x susize=default wcache=WB wcache=WT wcache=WBE rcache=No_Ahead rcache=Ahead init=fast init=front init=background pdcache=on pdcache=off pdcache=default rg=x
hiraidadm cx set jbod=rawdrive jbod=off
hiraidadm cx set ehsp sw=on sw=off
hiraidadm cx set precopy sw=on sw=off
hiraidadm cx set bgrate func=rebuild func=sanitize func=bginit func=copyback func=precopy func=ccheck func=patrolread rate=high rate=medium rate=low
hiraidadm cx set ccheck period=x rate=low rate=medium rate=high repair=on repair=off
hiraidadm cx set patrolread period=x rate=low rate=medium rate=high drivenum=x
hiraidadm cx set copyback sw=on sw=off
hiraidadm cx set smart_polling period=x
hiraidadm cx set bootwithpinnedcache sw=on sw=off
hiraidadm cx set slowdrive type=raid type=rawdrive sw=on sw=off ratio=x media=ssd media=hdd susize=256 susize=1024 l1=x l2=x l3=x default
hiraidadm cx set dump
hiraidadm cx set kickdrive period=x sum=x
hiraidadm cx set repair policy=default policy=confirm
hiraidadm cx set loglevel val=fatal val=error val=warn val=print val=info val=debug
hiraidadm cx set drivefc mode=high mode=low media=hdd media=ssd
hiraidadm cx set dphstartmod sas0=0 sas0=1 sas1=0 sas1=1 sas2=0 sas2=1 sas3=0 sas3=1 sas4=0 sas4=1
hiraidadm cx set dha sw=on sw=off
hiraidadm cx set quedepth type=rawdrive type=raid sashdd=x sasssd=x satahdd=x satassd=x
hiraidadm cx set vdfc mode=high mode=low mode=medium
hiraidadm cx set merge sw=on sw=off timeout=default timeout=x count=default count=x
hiraidadm cx set vdmerge vd=x stripelen=default stripelen=x startlen=default startlen=x mergelen=default mergelen=x
hiraidadm cx set tspfreq mode=low mode=high
hiraidadm cx clear fncfg guid=0xy
hiraidadm cx import fncfg guid=0xy
hiraidadm cx reset defaultconfig noprompt
hiraidadm cx update fw file=x
hiraidadm cx active fw type=cold type=hot
hiraidadm cx show repair_process
hiraidadm cx show repair_policy
hiraidadm cx show error
hiraidadm cx show config
hiraidadm cx show pdlist
hiraidadm cx show vdlist
hiraidadm cx show bgtask
hiraidadm cx show hspslot
hiraidadm cx show patrolread
hiraidadm cx show rglist
hiraidadm cx show fncfg
hiraidadm cx show bst
hiraidadm cx show copyback
hiraidadm cx show precopy
hiraidadm cx show ehsp
hiraidadm cx show bootwithpinnedcache
hiraidadm cx show jbod
hiraidadm cx show ccheck
hiraidadm cx show smart_polling
hiraidadm cx show phy_cfg
hiraidadm cx show boot
hiraidadm cx show status
hiraidadm cx show info
hiraidadm cx show elabel
hiraidadm cx show queue
hiraidadm cx show pinnedcache
hiraidadm cx show loglevel
hiraidadm cx show adc
hiraidadm cx show scaplearn
hiraidadm cx show backupcap
hiraidadm cx show drivefc
hiraidadm cx show slowdrive
hiraidadm cx show dphstartmod
hiraidadm cx show dhacfg
hiraidadm cx show kickdrive
hiraidadm cx show quedepth
hiraidadm cx show vdfc
hiraidadm cx show merge
hiraidadm cx show vdmerge vd=all vd=x
hiraidadm cx show tspfreq
hiraidadm cx show inactivefw
hiraidadm cx get log path=x
hiraidadm cx get dump path=x type=flash type=ram
hiraidadm cx get nandlog path=x id=x
hiraidadm cx start repair type=writehole type=pinnedcache vdid=x type=restore type=drop
hiraidadm cx start patrolread delay=x period=x rate=low rate=medium rate=high drivenum=x
hiraidadm cx start ccheck delay=x period=x rate=low rate=medium rate=high repair=on repair=off
hiraidadm cx stop patrolread
hiraidadm cx stop ccheck
hiraidadm cx:sc show supercap
hiraidadm cx:sc show selflearn
hiraidadm cx:sc set alarm temp=x
hiraidadm cx:sc set learn cycle=x starttime=x
hiraidadm cx:vdx delete noprompt flushcache=yes flushcache=no
hiraidadm cx:vdx delete pinnedcache
hiraidadm cx:vdx show
hiraidadm cx:vdx show pdarray
hiraidadm cx:vdx show ccheck
hiraidadm cx:vdx show inconsistent
hiraidadm cx:vdx show sanitize
hiraidadm cx:vdx set access policy=RW policy=RO
hiraidadm cx:vdx set wcache mode=WB mode=WT mode=WBE
hiraidadm cx:vdx set rcache mode=No_Ahead mode=Ahead
hiraidadm cx:vdx set name val=x
hiraidadm cx:vdx set boot sw=on sw=off
hiraidadm cx:vdx set pdcache sw=on sw=off sw=default
hiraidadm cx:vdx expand size=x size=max_left
hiraidadm cx:vdx start sanitize mode=simple mode=normal mode=thorough
hiraidadm cx:vdx start ccheck delay=x
hiraidadm cx:vdx stop sanitize
hiraidadm cx:vdx stop ccheck
hiraidadm cx:vdx clear name
hiraidadm cx:rgx show
hiraidadm cx:rgx show pdarray
hiraidadm cx:rgx set pdcache sw=on sw=off sw=default
hiraidadm cx:ex show phy
hiraidadm cx:ex:sx set phy func=hardreset func=linkreset func=disable func=enable func=speed func=enableloop func=disableloop sp=1.5G sp=3G sp=6G sp=12G
hiraidadm cx:ex:sx set phy_cfg sw=on sw=off speed=1.5G speed=3G speed=6G speed=12G
hiraidadm cx:ex:sx set hspslot sw=on sw=off
hiraidadm cx:ex:sx set pdcache sw=on sw=off
hiraidadm cx:ex:sx set free
hiraidadm cx:ex:sx set hotspare rg=x
hiraidadm cx:ex:sx set jbod
hiraidadm cx:ex:sx set boot sw=on sw=off
hiraidadm cx:ex:sx set led type=err type=locate sw=on sw=off
hiraidadm cx:ex:sx set online
hiraidadm cx:ex:sx set offline
hiraidadm cx:ex:sx active fw
hiraidadm cx:ex:sx show
hiraidadm cx:ex:sx show sanitize
hiraidadm cx:ex:sx show erase
hiraidadm cx:ex:sx show bst slba=x num=x
hiraidadm cx:ex:sx show smart
hiraidadm cx:ex:sx show pdcache
hiraidadm cx:ex:sx show patrolread
hiraidadm cx:ex:sx show phyerror
hiraidadm cx:ex:sx show driveerror
hiraidadm cx:ex:sx show iocnt
hiraidadm cx:ex:sx stop patrolread
hiraidadm cx:ex:sx stop erase
hiraidadm cx:ex:sx start patrolread delay=x
hiraidadm cx:ex:sx start sanitize mode=cryptoerase mode=overwrite mode=blockerase
hiraidadm cx:ex:sx start erase mode=simple mode=normal mode=thorough pa=11100000 pb=11110000
hiraidadm cx:ex:sx passthru scsi type=read type=write type=set cdblen=x cdb=x file=x data_len=x
hiraidadm cx:ex:sx update fw file=x
hiraidadm cx:ex:sx clear phyerror
"
    if [[ $spadmsups_rel != "" ]]; then
        spadmsups=$spadmsups_rel
    else
        spadmsups=$spadmsups_dfx
    fi

    # 备份分隔符
    local old_IFS="${IFS}"
    local old_COMP_WORDBREAKS="${COMP_WORDBREAKS}"

    local ipt_words=()             # 取代 COMP_WORDS，按照需要的分隔符分隔输入
    local ipt_cword=0              # 同上 取代 COMP_CWORD
    local objflag=1                # 判定当前是否针对某个卡级对象的命令
    local paraflag=0               # 进入参数匹配标志位，若检查到=号，则表明已到末尾匹配参数的环节，
                                   # 将采取参数名匹配而不是整命令行前缀匹配
    local pre_match_len=0          # 前缀匹配长度，依靠 = 号识别

# ---------> 用户输入预处理阶段
    # 按照空格预处理用户输入 ipt_words ~ COMP_WORDS  ipt_cword ~ COMP_CWORD
    for ((i = 0; i < COMP_POINT; i++)); do
        if [[ "${COMP_LINE:i:1}" = '=' ]] && ((paraflag==0)); then
            paraflag=1                          # 后续的匹配将采用 prev 匹配
            pre_match_len=$ipt_cword
        fi
        if [[ ${COMP_LINE:i:1} = ' ' ]]; then
            ((ipt_cword++))
            continue
        fi
        ipt_words[ipt_cword]+=${COMP_LINE:i:1}
    done

    if ((paraflag==0)); then
        pre_match_len=$ipt_cword
    fi

    if ((paraflag==0)) && [[ ${COMP_LINE:${#COMP_LINE}-1:1} != " " ]]; then # 上面方式只能识别中间空格，末尾有字符需单独测试
        ((pre_match_len++))
    fi

    local ipt_dup=( ${ipt_words[*]} )
    ipt_dup[0]=${ipt_dup[0]##*/}        # 删除路径前缀
    # 输入预处理，将 c0 等替换为 cx 等，便于前缀匹配
    if [[ ${ipt_dup[1]} =~ c[0-9]+:e[0-9]+:s[0-9]+ ]]; then
        ipt_dup[1]="cx:ex:sx"
    elif [[ ${ipt_dup[1]} =~ c[0-9]+:e[0-9]+:.* ]]; then
        ipt_dup[1]=${ipt_dup[1]//c*:e*:/"cx:ex:"}
    elif [[ ${ipt_dup[1]} =~ c[0-9]+:e[0-9]+ ]]; then
        ipt_dup[1]=${ipt_dup[1]//c*:e*/"cx:ex"}
    elif [[ ${ipt_dup[1]} =~ c[0-9]+:vd[0-9]+ ]]; then
        ipt_dup[1]="cx:vdx"
    elif [[ ${ipt_dup[1]} =~ c[0-9]+:rg[0-9]+ ]]; then
        ipt_dup[1]="cx:rgx"
    elif [[ ${ipt_dup[1]} =~ c[0-9]+:sc ]]; then
        ipt_dup[1]="cx:sc"
    elif [[ ${ipt_dup[1]} =~ c[0-9]+:.* ]]; then
        ipt_dup[1]="${ipt_dup[i]//c*:/"cx:"}"
    elif [[ ${ipt_dup[1]} =~ c[0-9]+.* ]]; then
        ipt_dup[1]="${ipt_dup[1]//c*/"cx"}"
    else
        objflag=0               # 如果未进入任何分支，则初步不是卡的对象
    fi

    # 前缀匹配，最终结果为将用户的输入转换为上方支持的形式
    local iptmatch="${ipt_dup[0]}"
    for ((i = 1; i < ${pre_match_len} && i < 4; i++)); do
        if [[ ${ipt_dup[i]} =~ raid[0-9]+$ ]]; then
            iptmatch+=" raidx"
            continue
        fi
        iptmatch+=" ${ipt_dup[i]}"
    done
    if [[ ${COMP_LINE:${#COMP_LINE}-1:1} = ' ' ]]; then # 如果用户最后输入的有空格，则追加空格
            iptmatch+=" "                               # 防止饥饿匹配
    fi

# ---------> 匹配能支持的命令阶段
    IFS=$'\n'
    local spadmsarr=($spadmsups)   # 以回车符做区分，得到每条能单独执行的命令
    local matchadms=()             # 接收本次能匹配的命令行
    local adm
    # 支持参数获取主循环，进行前缀匹配
    for adm in ${spadmsarr[*]}
    do
        if [[ "$adm" =~ ${iptmatch} ]]; then # 前缀比较，找到 对象 + 主命令 + 子命令 满足的命令（不匹配参数层面）
            matchadms+=($adm)
        fi
    done

    local sup_adms=()               # 支持的主子命令数组（接收 show 、phy等主子命令）
    if ((paraflag == 0)); then      # 处理多条命令候选；通过当前输入下标取出可匹配的参数
        for adm in ${matchadms[*]}
        do
            IFS=$' ' # 不要随意设置，否则shell会把有些回车的地方都解释为空格，或其他非预期结果
            local cmds_arr=($adm)       # 将支持的命令按空格分隔成数组

            if [[ "${cmds_arr[ipt_cword]}" =~ .*=.* ]] || [[ ${cmds_arr[ipt_cword]} = "" ]]; then
                continue                        # 为空或有等于号都舍去，此处不处理参数
            fi

            sup_adms+=( ${cmds_arr[ipt_cword]} )    # 能支持的字符必然数组下标相同，故而直接取出
        done
    fi

# 尝试匹配 主子命令
    IFS=$'\n'
    COMPREPLY=()
    local cur="${ipt_words[ipt_cword]}"
    if [ ${#sup_adms[*]} -eq 1 ] && [[ ${sup_adms[0]} = "raidx" ]]; then # 专门处理特殊参数raidx,因为它不具有raid=x的范式，需特殊处理
        compopt -o nospace
        sup_adms[0]="raid"
    fi
    COMPREPLY=( $(compgen -W "${sup_adms[*]}" -- ${cur}) )
    if [[ $cur = c ]] && (($ipt_cword == 1)); then # 将对象补全放置到最后补全
        COMPREPLY=( )
    fi
    sp_ltrim_colon_completions "$cur"       # 切除冒号左边重复值
    if ((${#COMPREPLY[*]} != 0)); then
        return 0
    fi

# 尝试匹配 参数，默认情况下此时只有一个命令支持，故只取0下标元素
    compopt -o nospace # 临时关闭自动补全加空格

    local sup_para_vals=()
    local param
    local ipt_paras=()             # 用户已经输入的参数名，去重

    IFS=$' '
    sup_para_vals=( ${matchadms[0]} ) # 首先获取所有支持的参数键值对  2 筛选是否已有完成的参数 3 若有则在支持键值对内删除相关键值对

    for ((i = $pre_match_len-1; i < ${#ipt_words[*]}; i++)); do # 将用户输入的参数去和支持的参数做比较，用于下一部去重
        for param in ${sup_para_vals[*]}
        do
            if [[ ${param} =~ ^size=* ]]; then
                if [[ ${ipt_words[i]} =~ ^size=[0-9]+$ ]] || [[ ${ipt_words[i]} = "size=max_left" ]]; then
                    ipt_paras+=( "${param%"${param##*=}"}" )
                fi
            elif [[ ${param} =~ ^susize=* ]]; then
                if [[ ${ipt_words[i]} =~ ^susize=[0-9]+$ ]] || [[ ${ipt_words[i]} = "susize=default" ]]; then
                    ipt_paras+=( "${param%"${param##*=}"}" )
                fi
            elif [[ ${param} =~ .*=x || ${param} =~ .*=0xy ]] && # param=x  0xy 对于参数的值非固定的，只查找参数名即可
                [[ "${param%"${param##*=}"}" = "${ipt_words[i]%"${ipt_words[i]##*=}"}" ]]; then
                ipt_paras+=( "${param%"${param##*=}"}" )  # 记录参数名+等于号
            elif [[ ${param} = ${ipt_words[i]} ]]; then # 对于参数的值固定的，寻找已完全输入的参数，否则作为可匹配项
                ipt_paras+=( "${param%"${param##*=}"}" )
            fi
        done
    done

    for ((i = 0; i < ${#sup_para_vals[*]}; i++)); do            # 根据ipt_paras去除掉sup中已有参数键值对
        if [[ ${sup_para_vals[i]} =~ .*path.* || ${sup_para_vals[i]} =~ .*file.* ]]; then
            compopt -o default
        fi

        for param in ${ipt_paras[*]} # param 参数名+=           # 如用户输入了：init=fast 则同类的init=front、init=back..等应该去掉
        do
            if [[ ${sup_para_vals[i]} =~ ^${param}* ]]; then
                sup_para_vals[i]=""
                break
            fi
        done
        if [[ ${sup_para_vals[i]} =~ .*=x ]]; then
            sup_para_vals[i]=${sup_para_vals[i]%"${sup_para_vals[i]##*=}"}
        elif [[ ${sup_para_vals[i]} =~ .*=0xy ]]; then
            sup_para_vals[i]="${sup_para_vals[i]%"${sup_para_vals[i]##*=}"}0x"
        elif [[ ${sup_para_vals[i]} =~ .*=.* ]]; then
            sup_para_vals[i]="${sup_para_vals[i]} "
        else
            sup_para_vals[i]=""
        fi
    done

    cur="${ipt_words[ipt_cword]}"
    IFS=$'\t\n'
    COMPREPLY=( $(compgen -W "${sup_para_vals[*]}" -- ${cur}) )
    sp_ltrim_equal_completions "$cur"       # 切除冒号左边重复值

    if ((${#COMPREPLY[*]} != 0)); then
        return 0
    fi

    if ((${#COMPREPLY[@]} == 0)) && (($ipt_cword == 1)); then # 没有可用匹配项，并且处于对象处理位置，补全对象
        local supcxarr=( "$cur" "$cur:vdx" "$cur:rgx" "$cur:sc" "$cur:ex" "$cur:ex:sx" )
        local supcx_ex_arr=( "${cur}:s" )

        if [[ $cur =~ c[0-9]+$ ]] || [[ $cur =~ c[0-9]+:$ ]] ; then # 如果只输入c0等的情况
            COMPREPLY=( $(compgen -W "${supcxarr[*]}" -- ${cur}) )
        elif [[ $cur =~ c[0-9]+:e[0-9]+$ ]] || [[ $cur =~ c[0-9]+:e[0-9]+:$ ]] ; then # 如果只输入c0等的情况
            COMPREPLY=( $(compgen -W "${supcx_ex_arr[*]}" -- ${cur}) )
        fi
        sp_ltrim_colon_completions "$cur" # 自动补全时把候选中已有冒号前缀都去掉
    fi
    if ((${#COMPREPLY[*]} != 0)); then
        return 0
    fi

    compopt +o nospace
    COMP_WORDBREAKS="${old_COMP_WORDBREAKS}"
    IFS="${old_IFS}"
}

complete -F __hiraidadm hiraidadm
