#!/usr/bin/env bash

# Processing TED-LIUM2 ASR Datasets

# Copyright 2023 Natural Language Processing Laboratory 
# Xu Chen (xuchenneu@163.com)

# Set bash to 'debug' mode, it will exit on :
# -e 'error', -u 'undefined variable', -o ... 'error in pipeline', -x 'print commands',
set -e
#set -u
set -o pipefail
export PYTHONIOENCODING=UTF-8

eval=1
time=$(date "+%m%d_%H%M")

stage=1
stop_stage=4

######## hardware ########
# devices
device=(0)
gpu_num=2
update_freq=1

root_dir=/opt/tiger
#  data_root_dir=/mnt/bd/data-model
data_root_dir=/mnt/bn/nas-xc-1

code_dir=${root_dir}/s2t
pwd_dir=$PWD

# dataset
src_lang=en
lang=${src_lang}
dataset=tedlium2
data_tag=asr

task=speech_to_text
vocab_type=unigram
vocab_size=10000
speed_perturb=1
lcrm=0
tokenizer=0
use_raw_audio=0

. ./local/parse_options.sh || exit 1;

use_specific_dict=0
specific_prefix=st
specific_dir=${root_dir}/data/mustc/st
asr_vocab_prefix=spm_unigram10000_st_share

data_model_subfix=${dataset}/${data_tag}
org_data_dir=${data_root_dir}/data/${dataset}
data_dir=${data_root_dir}/data/${data_model_subfix}
train_split=train
valid_split=dev
test_split=test
test_subset=dev,test

# exp
sub_tag=
exp_prefix=$(date "+%m%d")
# exp_subfix=${ARNOLD_JOB_ID}_${ARNOLD_TASK_ID}_${ARNOLD_TRIAL_ID}
extra_tag=
extra_parameter=
exp_tag=baseline
exp_name=

# config
train_config=base,ctc
data_config=config.yaml

# training setting
fp16=1
max_tokens=100000
step_valid=0

# decoding setting
dec_model=checkpoint_best.pt
cer=0
ctc_infer=0
ctc_self_ensemble=0
ctc_inter_logit=0
n_average=10
beam_size=5
len_penalty=1.0
single=0
epoch_ensemble=0
best_ensemble=1
infer_parameters=

if [[ ${speed_perturb} -eq 1 ]]; then
    data_dir=${data_dir}_sp
    exp_prefix=${exp_prefix}_sp
fi
if [[ ${lcrm} -eq 1 ]]; then
    data_dir=${data_dir}_lcrm
    exp_prefix=${exp_prefix}_lcrm
fi
if [[ ${use_specific_dict} -eq 1 ]]; then
    data_dir=${data_dir}_${specific_prefix}
    exp_prefix=${exp_prefix}_${specific_prefix}
fi
if [[ ${tokenizer} -eq 1 ]]; then
    data_dir=${data_dir}_tok
    exp_prefix=${exp_prefix}_tok
fi
if [[ ${use_raw_audio} -eq 1 ]]; then
    data_dir=${data_dir}_raw
    exp_prefix=${exp_prefix}_raw
fi
if [[ "${vocab_type}" == "char" ]]; then
    data_dir=${data_dir}_char
    exp_prefix=${exp_prefix}_char
fi
if [[ ! -d /mnt/bd/data-model && -d /mnt/bd/data-model2 ]]; then
    sudo ln -s /mnt/bd/data-model2/ /mnt/bd/data-model
fi
if [[ ! -d ${data_dir} ]]; then
    echo "No feature dir ${data_dir}"
    exit
fi

# setup nccl envs
export NCCL_IB_DISABLE=0
export NCCL_IB_HCA=$ARNOLD_RDMA_DEVICE:1
export NCCL_IB_GID_INDEX=3
export NCCL_SOCKET_IFNAME=eth0

HOSTS=$ARNOLD_WORKER_HOSTS
HOST=(${HOSTS//,/ })
HOST_SPLIT=(${HOST//:/ })
PORT=${HOST_SPLIT[1]}
INIT_METHOD="tcp://${ARNOLD_WORKER_0_HOST}:${ARNOLD_WORKER_0_PORT}"
DIST_RANK=$((ARNOLD_ID * ARNOLD_WORKER_GPU))

export PATH=$PATH:${code_dir}/scripts
. ./local/parse_options.sh || exit 1;

if [[ -z ${exp_name} ]]; then
    config_string=${train_config//,/_}
    exp_name=${exp_prefix}_${config_string}_${exp_tag}
    if [[ -n ${extra_tag} ]]; then
        exp_name=${exp_name}_${extra_tag}
    fi
    if [[ -n ${exp_subfix} ]]; then
        exp_name=${exp_name}_${exp_subfix}
    fi
fi
model_dir=${code_dir}/checkpoints/${data_model_subfix}/${sub_tag}/${exp_name}

echo "stage: $stage"
echo "stop_stage: $stop_stage"
cd ${code_dir}

if [ ${stage} -le -1 ] && [ ${stop_stage} -ge -1 ]; then
    echo "stage -1: Data Download"
    # pass
fi

if [ ${stage} -le 0 ] && [ ${stop_stage} -ge 0 ]; then
    ### Task dependent. You have to make data the following preparation part by yourself.
    ### But you can utilize Kaldi recipes in most cases
    echo "stage 0: ASR Data Preparation"
    if [[ ! -e ${data_dir} ]]; then
        mkdir -p ${data_dir}
    fi

    cmd="python3 ${code_dir}/examples/speech_to_text/prep_audio_data.py
        --data-root ${org_data_dir}
        --output-root ${data_dir}
        --task asr
        --src-lang ${src_lang}
        --splits ${valid_split},${test_split},${train_split}
        --vocab-type ${vocab_type}
        --vocab-size ${vocab_size}"

    if [[ ${use_raw_audio} -eq 1 ]]; then
        cmd="$cmd
        --raw"
    fi
    if [[ ${use_specific_dict} -eq 1 ]]; then
        cp -r ${specific_dir}/${asr_vocab_prefix}.* ${data_dir}
        cmd="$cmd
        --asr-prefix ${asr_vocab_prefix}"
    fi
    if [[ ${speed_perturb} -eq 1 ]]; then
        cmd="$cmd
        --speed-perturb"
    fi
    if [[ ${lcrm} -eq 1 ]]; then
        cmd="$cmd
        --lowercase-src
        --rm-punc-src"
    fi
    if [[ ${tokenizer} -eq 1 ]]; then
        cmd="$cmd
        --tokenizer"
    fi

    echo -e "\033[34mRun command: \n${cmd} \033[0m"
    [[ $eval -eq 1 ]] && eval ${cmd}
fi

echo "stage 1: env configure"
if [[ `pip list | grep fairseq | wc -l` -eq 0 ]]; then 
    pip3 install -e ${code_dir} -i https://bytedpypi.byted.org/simple  --no-build-isolation --default-timeout=10000
fi

if [[ -d /mnt/bn/nas-xc-1/checkpoints && ! -d ${code_dir}/checkpoints ]]; then
    ln -s /mnt/bn/nas-xc-1/checkpoints ${code_dir}
fi

# if [ ${stage} -le 1 ] && [ ${stop_stage} -ge 1 ]; then
#     ln_data_dir=`echo ${data_dir} | sed -e "s#${data_root_dir}#${code_dir}#"`
#     echo ${ln_data_dir}
#     mkdir -p ${ln_data_dir}
#     ln -s ${data_dir}/../* ${ln_data_dir}
#     rm -r ${ln_data_dir}

#     hdfs_path=`echo ${data_dir} | sed -e "s#${data_root_dir}#hdfs://haruna/home/byte_arnold_lq_mlnlc/user/xuchen/#"`
#     hdfs dfs -get ${hdfs_path} ${ln_data_dir}
#     sed -i -e "s#${data_root_dir}#${code_dir}#" ${ln_data_dir}/config*
#     data_dir=${ln_data_dir}

# fi

if [ ${stage} -le 2 ] && [ ${stop_stage} -ge 2 ]; then
    echo "stage 2: ASR Network Training"
    [[ ! -d ${data_dir} ]] && echo "The data dir ${data_dir} is not existing!" && exit 1;

    if [[ -z ${device} || ${#device[@]} -eq 0 ]]; then
		if [[ ${gpu_num} -eq 0 ]]; then
			device=""
		else
            source ./local/utils.sh
            device=$(get_devices $gpu_num 0)
		fi
    fi

    echo -e "data=${data_dir} model=${model_dir}"

    if [[ ! -d ${model_dir} ]]; then
        mkdir -p ${model_dir}
    else
        echo "${model_dir} exists."
    fi

    cp -f ${pwd_dir}/`basename ${BASH_SOURCE[0]}` ${model_dir}
    cp -f ${pwd_dir}/train.sh ${model_dir}

    extra_parameter="${extra_parameter}
        --train-config ${pwd_dir}/conf/basis.yaml"
    cp -f ${pwd_dir}/conf/basis.yaml ${model_dir}
    config_list="${train_config//,/ }"
    idx=1
    for config in ${config_list[@]}
    do
        config_path=${pwd_dir}/conf/${config}.yaml
        if [[ ! -f ${config_path} ]]; then
            echo "No config file ${config_path}"
            exit
        fi
        cp -f ${config_path} ${model_dir}

        extra_parameter="${extra_parameter}
        --train-config${idx} ${config_path}"
        idx=$((idx + 1))
    done

    cmd="python3 -u ${code_dir}/fairseq_cli/train.py
        ${data_dir}
        --config-yaml ${data_config}
        --task ${task}
        --max-tokens ${max_tokens}
        --skip-invalid-size-inputs-valid-test
        --update-freq ${update_freq}
        --log-interval 100
        --save-dir ${model_dir}
        --tensorboard-logdir ${model_dir}"

	if [[ -n ${extra_parameter} ]]; then
        cmd="${cmd}
        ${extra_parameter}"
    fi
	if [[ ${gpu_num} -gt 0 ]]; then
		cmd="${cmd}
        --distributed-world-size $gpu_num
        --ddp-backend no_c10d"
        #--distributed-init-method ${INIT_METHOD}
        #--distributed-rank ${DIST_RANK}"
	fi
    if [[ $fp16 -eq 1 ]]; then
        cmd="${cmd}
        --fp16"
    fi
    if [[ $step_valid -eq 1 ]]; then
        validate_interval=1
        save_interval=1
        no_epoch_checkpoints=0
        save_interval_updates=500
        keep_interval_updates=10
    fi
    if [[ -n $no_epoch_checkpoints && $no_epoch_checkpoints -eq 1 ]]; then
        cmd="$cmd
        --no-epoch-checkpoints"
    fi
    if [[ -n $validate_interval ]]; then
        cmd="${cmd}
        --validate-interval $validate_interval "
    fi
    if [[ -n $save_interval ]]; then
        cmd="${cmd}
        --save-interval $save_interval "
    fi
    if [[ -n $save_interval_updates ]]; then
        cmd="${cmd}
        --save-interval-updates $save_interval_updates"
        if [[ -n $keep_interval_updates ]]; then
        cmd="${cmd}
        --keep-interval-updates $keep_interval_updates"
        fi
    fi

    echo -e "\033[34mRun command: \n${cmd} \033[0m"

    # save info
    log=./history.log
    echo "${time} | ${data_dir} | ${exp_name} | ${model_dir} " >> $log
    tail -n 50 ${log} > tmp.log
    mv tmp.log $log
    # export CUDA_VISIBLE_DEVICES=${device}

    log=${model_dir}/train.log
    cmd="${cmd} 2>&1 | tee -a ${log}"
    #cmd="nohup ${cmd} >> ${log} 2>&1 &"
    if [[ $eval -eq 1 ]]; then
        # tensorboard
        if [[ -z ${ARNOLD_TENSORBOARD_CURRENT_PORT} ]]; then
            port=6666
        else
            port=${ARNOLD_TENSORBOARD_CURRENT_PORT}
        fi
        tensorboard --logdir ${model_dir} --port ${port} --bind_all &
    
        echo "${cmd}" > ${model_dir}/cmd
		eval $cmd
		#sleep 2s
		#tail -n "$(wc -l ${log} | awk '{print $1+1}')" -f ${log}
	fi
fi

if [ ${stage} -le 3 ] && [ ${stop_stage} -ge 3 ]; then
    echo "stage 3: ASR Decoding"
    dec_models=
    if [[ ${single} -eq 1 ]]; then
        dec_models=${dec_model}
    fi
    if [[ ${n_average} -ne 1 ]]; then
        # Average models
        if [[ ${epoch_ensemble} -eq 1 ]]; then
            avg_model=avg_epoch${n_average}_checkpoint.pt

            if [[ ! -f ${model_dir}/${avg_model} ]]; then
                cmd="python3 ${code_dir}/scripts/average_checkpoints.py
                --inputs ${model_dir}
                --num-epoch-checkpoints ${n_average}
                --output ${model_dir}/${avg_model}"
                echo -e "\033[34mRun command: \n${cmd} \033[0m"
                [[ $eval -eq 1 ]] && eval $cmd
            fi
            dec_models+=(${avg_model})
        fi
        if [[ ${best_ensemble} -eq 1 ]]; then
            avg_model=avg_best${n_average}_checkpoint.pt

            if [[ ! -f ${model_dir}/${avg_model} ]]; then
                cmd="python3 ${code_dir}/scripts/average_checkpoints.py
                --inputs ${model_dir}
                --num-best-checkpoints ${n_average}
                --output ${model_dir}/${avg_model}"
                echo -e "\033[34mRun command: \n${cmd} \033[0m"
                [[ $eval -eq 1 ]] && eval $cmd
            fi
            dec_models+=(${avg_model})
        fi
	fi

    if [[ -z ${device} || ${#device[@]} -eq 0 ]]; then
		if [[ ${gpu_num} -eq 0 ]]; then
			device=""
		else
            source ./local/utils.sh
            device=$(get_devices $gpu_num 0)
		fi
    fi
    # export CUDA_VISIBLE_DEVICES=${device}

    for dec_model in ${dec_models[@]}; do
        suffix=beam${beam_size}_alpha${len_penalty}_tokens${max_tokens}
        model_str=`echo $dec_model | sed -e "s#checkpoint##" | sed "s#.pt##"`
        suffix=${suffix}_${model_str}
        if [[ -n ${cer} && ${cer} -eq 1 ]]; then
            suffix=${suffix}_cer
        else
            suffix=${suffix}_wer
        fi
        if [[ ${ctc_infer} -eq 1 ]]; then
            suffix=${suffix}_ctc
        fi
        if [[ ${ctc_self_ensemble} -eq 1 ]]; then
            suffix=${suffix}_ensemble
        fi
        if [[ ${ctc_inter_logit} -ne 0 ]]; then
            suffix=${suffix}_logit${ctc_inter_logit}
        fi

        suffix=`echo $suffix | sed -e "s#__#_#"`
        result_file=${model_dir}/decode_result_${suffix}
        [[ -f ${result_file} ]] && rm ${result_file}

        test_subset=${test_subset//,/ }
        for subset in ${test_subset[@]}; do
            subset=${subset}
            cmd="python3 ${code_dir}/fairseq_cli/generate.py
            ${data_dir}
            --config-yaml ${data_config}
            --gen-subset ${subset}
            --task speech_to_text
            --path ${model_dir}/${dec_model}
            --results-path ${model_dir}
            --max-tokens ${max_tokens}
            --beam ${beam_size}
            --lenpen ${len_penalty}
            --scoring wer"

            if [[ ${cer} -eq 1 ]]; then
                cmd="${cmd}
            --wer-char-level"
            fi
            if [[ ${ctc_infer} -eq 1 ]]; then
                cmd="${cmd}
            --ctc-infer"
            fi
            if [[ ${ctc_self_ensemble} -eq 1 ]]; then
                cmd="${cmd}
            --ctc-self-ensemble"
            fi
            if [[ ${ctc_inter_logit} -ne 0 ]]; then
                cmd="${cmd}
            --ctc-inter-logit ${ctc_inter_logit}"
            fi
            if [[ -n ${infer_parameters} ]]; then
                cmd="${cmd}
            ${infer_parameters}"
            fi

            echo -e "\033[34mRun command: \n${cmd} \033[0m"

            if [[ $eval -eq 1 ]]; then
                src_ctc_file=translation-${subset}.txt.ctc
                if [[ -f ${model_dir}/${src_ctc_file} ]]; then
                    rm ${model_dir}/${src_ctc_file}
                fi

                cd ${code_dir}
                eval $cmd
                echo "" >> ${result_file}
                tail -n 2 ${model_dir}/generate-${subset}.txt >> ${result_file}
                mv ${model_dir}/generate-${subset}.txt ${model_dir}/generate-${subset}-${suffix}.txt
                mv ${model_dir}/translation-${subset}.txt ${model_dir}/translation-${subset}-${suffix}.txt

                cd ${pwd_dir}
                trans_file=translation-${subset}-${suffix}.txt
                if [[ ${ctc_infer} -eq 1 && -f ${model_dir}/${src_ctc_file} ]]; then
                    ref_file=${model_dir}/${subset}.${src_lang}
                    if [[ ! -f ${ref_file} ]]; then
                        python3 ./local/extract_txt_from_tsv.py ${data_dir}/${subset}.tsv ${ref_file} "src_text"
                    fi
                    if [[ -f ${ref_file} ]]; then
                        src_ctc=$(mktemp -t temp.record.XXXXXX)
                        cd ./local
                        ./cal_wer.sh ${model_dir} ${subset} ${trans_file} ${src_ctc_file} ${ref_file} > ${src_ctc}
                        cd ..

                        echo "CTC WER" >> ${result_file}
                        tail -n 2 ${src_ctc} >> ${result_file}

                        src_bleu=$(mktemp -t temp.record.XXXXXX)
                        cd local
                        ./cal_ctc_bleu.sh ${model_dir} ${subset} ${trans_file} ${src_ctc_file} ${ref_file} ${tokenizer} ${src_lang} > ${src_bleu}
                        cd ..
                        cat ${src_bleu} >> ${result_file}

                        rm ${src_ctc} ${src_bleu}
                    else
                        echo "No reference for source language."
                    fi
                fi
            fi
        done
        echo
        cat ${result_file}
    done
fi

# if [ ${stage} -le 4 ] && [ ${stop_stage} -ge 4 ]; then
#     echo "Stage 4: Upload model and log"
#     echo "Path: hdfs://haruna/home/byte_arnold_lq_mlnlc/user/xuchen/s2t/checkpoints/${data_model_subfix}/${exp_name}"
#     hdfs dfs -mkdir -p hdfs://haruna/home/byte_arnold_lq_mlnlc/user/xuchen/s2t/checkpoints/${data_model_subfix}
#     hdfs dfs -put -f ${model_dir} hdfs://haruna/home/byte_arnold_lq_mlnlc/user/xuchen/s2t/checkpoints/${data_model_subfix}
# fi
