#!/usr/bin/env bash
command_run_mmprof() {
    if [[ "$_RUN_MODE" != "mapprof" ]]; then
        map_error_msg "run mode must be 'mapprof'"
        return 255
    fi
    if [[ -z "$_RUN_NODEID" ]]; then
        map_error_msg "target nodeid must be specified in mode 'mapprof'"
        return 255
    fi

    # profile dir for given node
    local dir_node_prof="$DIR_PROJECT_CLUSTER/$_RUN_NODEID/prof"
    [[ -d $dir_node_prof ]] || {
        map_error_msg "missing profile directory for node $_RUN_NODEID"
        map_error_msg "make sure they are downloaded OR generated using:"
        map_error_msg " ./orthologer -t <node todo file> -r POSTPROC"
        map_error_msg "with POSTPROC_LABELS=\"ogmmprof\" in orthologer_conf.sh."
        return 255
    }

    # get all profiles
    local mmprof_files=( $(find $dir_node_prof -name "*.mmprof" 2>/dev/null ) )
    if [[ ${#mmprof_files[@]} -eq 0 ]]; then
        map_error_msg "no mmseqs profiles were found for node $_RUN_NODEID in directory $dir_node_prof"
        map_error_msg "make sure they are downloaded OR generated using:"
        map_error_msg " ./orthologer -t <node todo file> -r POSTPROC"
        map_error_msg "with POSTPROC_LABELS=\"ogmmprof\" in orthologer_conf.sh."
        return 255
    fi
    local nprofiles=${#mmprof_files[@]}

    # create PWC for profile alignments
    local dir_node_pwc="$DIR_PROJECT_CLUSTER/$_RUN_NODEID/PWC/$_RUN_TAXID"
    mkdir -p $dir_node_pwc || map_error_msg "failed creating PWC directory for node $_RUN_NODEID at $dir_node_pwc"

    #
    local ec=0
    map_info_msg "------------------------------------"
    map_info_msg "Start RUN with parameters:"
    map_info_msg "mode           $_RUN_MODE"
    map_info_msg "label          $_RUN_LABEL"
    map_info_msg "user taxid     $_RUN_TAXID"
    map_info_msg "nodeid         $_RUN_NODEID"
    map_info_msg "N(profiles)    $nprofiles"
    map_info_msg "------------------------------------"

    #------------------------------------------------------------
    # prepare run
    #------------------------------------------------------------
    # create log file
    local logfile=$(map_runlog_name)

    DRY_RUN=0
    [[ $_RUN_GO -eq 0 ]] && {
        DRY_RUN=1
        map_info_msg "add 'go' to the command line to actually run"
    }
    [[ $_RUN_CHECK -eq 1 ]] && {
        map_info_msg "will make full check that all partial results exists"
    }

    # makes sure the pipeline env is correct
    map_check_pipeline || {
        map_error_msg "pipeline is not setup, missing $CHECK_MISSING"
        return 255
    }
    
    # check all tools
    verbose_info_msg "precheck..."
    precheck_run || return 255

    #------------------------------------------------------------
    # Run the mapping.
    #------------------------------------------------------------
    local T0map=$(time_nsec)
    # initialise todo file
    verbose_info_msg "create todo"
    is_dryrun || map_init_todo || return 255

    # check if ready to run
    verbose_info_msg "check if ready for run..."
    is_dryrun || map_ready_to_run || return 255

    # get todo file name
    PL_TODO=$(map_get_todo)

    # update todo variables
    is_dryrun || update_todo 1

    
    # redo this as TODO files have been changed
    is_dryrun || export_decl_doall
    
    # and the label
    local todolabel=$(file_basename $PL_TODO)

    is_dryrun && return 0

    # move to pipeline
    cd $DIR_PROJECT || { map_error_msg "failed entering $DIR_PROJECT"; return 255; }
    
    # make sure input profile is there
    local inpog="Cluster/${_RUN_NODEID}.${EXT_CLUSTER}"
    [[ -f $inpog ]] || { map_error_msg "could not find source clustering $inpog"; return 255; }
    
    # subset of final clustering, contains ONLY genes from the mapped taxid
    local mapped_og="Results/${todolabel}.${EXT_CLUSTER}"
    local result_odb="Results/${todolabel}.${EXT_CLUSTER}.odb"
    local result_hits="Results/${todolabel}.${EXT_CLUSTER}.hits"
    local result_desc="Results/${todolabel}.${EXT_CLUSTER}.desc"
    local result_annot="Results/${todolabel}.${EXT_CLUSTER}.annotations"
    local result_fs="Results/${todolabel}.fs"
    local result_fsmap="Results/${todolabel}.fs.maptxt"

    # same but with a directory name that makes sense for user (relative DIR_PROJECT)
    local mapped_og_ref=$(trim_name "$(pwd)/$mapped_og" "$DIR_PROJECT")
    local result_odb_ref=$(trim_name "$(pwd)/$result_odb" "$DIR_PROJECT")
    local result_hits_ref=$(trim_name "$(pwd)/$result_hits" "$DIR_PROJECT")
    local result_desc_ref=$(trim_name "$(pwd)/$result_desc" "$DIR_PROJECT")
    local result_annot_ref=$(trim_name "$(pwd)/$result_annot" "$DIR_PROJECT")
    local result_fs_ref=$(trim_name "$(pwd)/$result_fs" "$DIR_PROJECT")
    local result_fsmap_ref=$(trim_name "$(pwd)/$result_fsmap" "$DIR_PROJECT")
    
    # output cluster
    [[ -f $result_annot ]] && {
        map_info_msg "done - target clustering already exists : $result_annot_ref"
        map_write_metadata || { map_error_msg "failed writing metadata!"; return 255; }
        return 0
    }

    # flag if no user taxid was clustered
    local noData=0

    # run pipeline
    # note:
    # - stderr (2) : all output from orthologer.sh, then redirected to logfile
    # - stdout (1) : all messages outside orthologer.sh
    #
    map_info_msg "run pipeline; logfile at $(trim_name $logfile $DIR_PROJECT)"
    {
        #-------------------------------------------------------------------------------------------------------------
        # make sure directories are present
        #-------------------------------------------------------------------------------------------------------------
        # first force alignment label to MMSEQS
        set_alignment_label MMSEQS
        [[ -d $DIR_MMSEQS ]] || { map_error_msg "mapping mode requires DIR_MMSEQS to be set properly (dir containing the mmseqs exec)"; return 255; }
        
        map_info_msg "pipeline start setup"
        is_dryrun || pipeline -t "$PL_TODO" -C 1>&2
        [[ $? -eq 0 ]] || { map_error_msg "failed setting up the pipeline - see logfile $(trim_name $logfile $DIR_PROJECT)" 2>&1; return 255; }

        # force checking for missing files up to FORMATDB
        if (( _RUN_CHECK > 0 || FORCE_NO_STEP_CHECK>0 )); then
            map_info_msg "reset done flag to MAKE MISSING for all steps before ALIGNMENT"
            is_dryrun || pipeline -t "$PL_TODO" -f ALIGNMENT-:3 1>&2
            ec=$?
        fi
        [[ $ec -eq 0 ]] || { map_error_msg "failed setting up the pipeline - see logfile $(trim_name $logfile $DIR_PROJECT)" 2>&1; return 255; }
	    map_info_msg "pipeline setup passed"

        #-------------------------------------------------------------------------------------------------------------
        # run all steps upto FORMATDB
        #-------------------------------------------------------------------------------------------------------------
        map_info_msg "pipeline pass 1 : run pipeline"
        map_info_msg "run all before ALIGNMENT for taxid $_RUN_TAXID, todo $PL_TODO"
        ec=0
        # note that the todo file only contains the user taxid
        is_dryrun || pipeline -t "$PL_TODO" -r ALIGNMENT- 1>&2 || ec=$?
        [[ $ec -eq 0 ]] || { map_error_msg "failed running pipeline before ALIGNMENT - see logfile $(trim_name $logfile $DIR_PROJECT)" 2>&1; return 255; }

	    map_info_msg "pipeline pass 1 : done"
    
        #-------------------------------------------------------------------------------------------------------------
        # map against profiles
        #-------------------------------------------------------------------------------------------------------------        
        map_info_msg "pipeline pass 2 : map against profiles"
        local fsrc="$DIR_PROJECT_SEQUENCES/${_RUN_TAXID}.${EXT_FASTA_SELECTED}.mm"                
        [[ -f "$fsrc" ]] || {
            map_error_msg "input fasta file does not exists - $fsrc"
            return 255
        }

        # set step id although it's not quite correct - required in order to run step_per_label()
        set_stepid POSTPROC
        load_done_flag || return 255
        step_info_msg "align to $nprofiles profiles"

        local saved_SKIP_REMAKE_CHECK=$SKIP_REMAKE_CHECK
        local minjobs=$(get_step_minjobs)
        local fmm=
        local ftrg=
        local mmprof_label=
        local nproc=0
        local nadded=0
        local nskip=0
        local nskipMax=10
        local ec=0
        if ! is_dryrun; then
            reset_joblists
            for fmm in ${mmprof_files[@]}; do
                mmprof_label=$(basename "$fmm")
                label="${_RUN_TAXID}_${mmprof_label%.*}"
                ftrg="$dir_node_pwc/${label}.align"

                remake_target "$ftrg" "$fmm"
                ec=$?
                [[ $ec -eq 0 ]] || {
                    nskip=$((nskip+1))
                    nproc=$((nproc+1))
                    if (( nskip < nskipMax )); then
                        step_info_msg "skipping $label - step id ($STEP_ID) done code = $STEP_DONE_CODE , remake code $ec"
                    fi
                    if (( nskip == nskipMax )); then
                        step_info_msg "..."
                    fi
                    continue
                }

                SRCFILES+=($fmm)
                TRGFILES+=($ftrg)

                # set JOB specs <id,fsrc,fmm,ftrg>
                JOBARGS+=("$nadded,$fsrc,$fmm,$ftrg")
                JOBLABELS+=( "profali-$label" )
                JOBSUBLBL+=( "$JOBLOGNOSPEC" )
                nadded=$((nadded+1))
                if (( nadded == minjobs )); then
                    nproc=$((nproc+nadded))
                    step_info_msg "submitting a batch of $nadded cluster ids, processed $nproc/$nprofiles , last label $label"
                    # third argument flags that it should NOT call init_step() and step_done()
                    #HERE - run_ogmmprof_score
                    # generates pairwise files in dir_node_pwc
                    # PWC/$_RUN_TAXID/${label}.align
                    # NOTE counters nproc,nadded, define minjobs (see postproc_ogmmprof)
                    SKIP_REMAKE_CHECK=1
                    step_per_label POSTPROC "${DIR_ORTHOLOGER_BIN}/run_ogmmprof_score.sh" "0"
                    SKIP_REMAKE_CHECK=$saved_SKIP_REMAKE_CHECK

                    nadded=0
                    reset_joblists
                fi
            done
            if (( nadded > 0 )); then
                nproc=$((nproc+nadded))
                step_info_msg "submitting the last batch of $nadded cluster ids, processed $nproc/$nprofiles , last label $label"
                # third argument flags that it should NOT call init_step() and step_done()
                #HERE - run_ogmmprof_score
                # generates pairwise files in dir_node_pwc
                # PWC/$_RUN_TAXID/${label}.align
                # NOTE counters nproc,nadded, define minjobs (see postproc_ogmmprof)
                SKIP_REMAKE_CHECK=1
                step_per_label POSTPROC "${DIR_ORTHOLOGER_BIN}/run_ogmmprof_score.sh" "0"
                SKIP_REMAKE_CHECK=$saved_SKIP_REMAKE_CHECK
                
                nadded=0
                reset_joblists
            fi

        fi
        
        #-------------------------------------------------------------------------------------------------------------
        # Extract the best mappings from the alignments
        #-------------------------------------------------------------------------------------------------------------
        # At this point the directory Cluster/<nodeid>/<taxid>/<taxid>_<clid>at<nodeid>.align
        # * read cluster file for nodeid
        # * read alignment files, sort by fasta geneid and score/evalue
        #
        map_info_msg "pipeline pass 3 : extract results"
        if ! is_dryrun; then
            local fhits=
            tmp_make fhits "hits"
            map_info_msg "extracting mapped genes"
            "${DIR_ORTHOLOGER_BIN}/extract_mapped_from_prof.py" --nodeid $_RUN_NODEID --taxid $_RUN_TAXID --srcdir $DIR_PROJECT_CLUSTER --output $fhits
            [[ $? -eq 0 ]] || { map_error_msg "failed mapping taxid $_RUN_TAXID on $_RUN_NODEID"; return 255; }
            
            # check if empty
            hasData=1
            grep -q "^#EMPTY" $fhits && hasData=0

            # create full cluster file with mapped
            map_info_msg "create full cluster file with internal id's ($mapped_og)"
            {
                header_cluster && echo
                {
                    grep -v '^#' "$inpog"
                    (( hasData == 1 )) && grep -v '^#' "$fhits"
                } | sort -n
            } > "$mapped_og"

            map_info_msg "create hits file with true id's ($result_hits_ref)"
            if (( hasData == 1 )); then
                
                # remap hits
                local f="$DIR_PROJECT_RAWDATA/${_RUN_TAXID}.fs.maptxt"
                #map_info_msg "use gene id map: $f"
                #map_info_msg "command: $PYTHON3 $DIR_ORTHOLOGER/bin/remap.py -f $fhits -m $f --oglevel $_RUN_NODEID --type 1 --outsep '\t'"
                {
                    header_cluster && echo
                    $PYTHON3 $DIR_ORTHOLOGER/bin/remap.py -f "$fhits" -m "$f" --oglevel "$_RUN_NODEID" --type 1 --outsep '\t' | grep -v '^#' | sort -n
                } > "$result_hits"
                [[ $? -eq 0 ]] || { map_error_msg "failed mapping ids for taxid $_RUN_TAXID"; return 255; }
            else
                cp "$fhits" "$result_hits" || return 255
            fi
            chmod 644 "$result_hits"
            map_info_msg "generated cluster file with only user genes at $result_hits_ref"
            
            #---------------------------------------------
            # create cluster description file
            local hasDesc=0
            if (( hasData == 1 )); then
                local fdescriptions=$MAP_ORTHODB_DATA/$DBI_DESCRIPTIONS/${_RUN_NODEID}.tsv
                if [[ -f $fdescriptions ]]; then
                    hasDesc=1
                    # extract all OG's (all but first line, first column)
                    local patternOGs=""
                    tmp_make patternOGs "patternOGs"

                    # The awk statement prints OG id preceeded by a '^' to mark the beginning of the line.
                    # It only processes non-empty lines.
                    # The sed statement prints all lines except the first one.
                    cat "$result_hits" | awk '!/^\s*$/{print "^"$1}' | sed -n '1d;p' | sort -u > "$patternOGs"
                    
                    # grep OG's from description tsv file
                    {
                        header_description && echo
                        grep -f "$patternOGs" "$fdescriptions" | sort -n
                    } > "$result_desc"
                fi
            fi
            # check if description is found
            if (( hasDesc == 0 )); then
                if (( hasData == 0 )); then
                    map_warn_msg "no data found"
                    echo "# no data" > $result_desc
                else
                    map_warn_msg "no description file found for clade ${_RUN_NODEID}"
                    map_warn_msg "file at: $fdescriptions"
                    echo "# missing OG descriptions for clade ${_RUN_NODEID}" > $result_desc
                fi
            fi
            map_info_msg "generated description file at $result_desc_ref"

            #---------------------------------------------
            # merge user and description file
            if (( hasDesc == 0 )); then
                cp $result_hits $result_annot
            else
                {
                    ###header_cluster && printf '\t' && header_description_noclid && echo
                    $PYTHON3 $DIR_ORTHOLOGER/bin/create_annotated_clusters.py --fhits $result_hits --fdesc $result_desc
                } > $result_annot
            fi
            map_info_msg "generated annotated file at $result_annot_ref"
            
            #---------------------------------------------
            # remap all ids in the full result file
            local orgTodo="todo/${_RUN_NODEID}.todo"
            local allMaps=""
            tmp_make allMaps "allMaps"
            {
                while read line; do
                    local f="Rawdata/${line}.fs.maptxt"
                    [[ -f $f ]] || continue
                    echo "$f"
                done < "$orgTodo"
                echo "Rawdata/${_RUN_TAXID}.fs.maptxt"
            } > "$allMaps"

            {
                header_cluster && echo
                $PYTHON3 $DIR_ORTHOLOGER/bin/remap.py -f "$mapped_og" -n $allMaps --oglevel "$_RUN_NODEID" --type 1 --outsep '\t' | grep -v '^#' | sort -n
            } > "$result_odb"
            [[ $? -eq 0 ]] || { map_error_msg "failed mapping all ids, file $result_odb_ref"; return 255; }
            chmod 644 "$result_odb"
            map_info_msg "generated cluster file with all genes at $result_odb_ref"

            #---------------------------------------------
            # rename fasta files and save in Results
            #---------------------------------------------
            map_info_msg "copying relevant fasta/map to Results"
            cp -f "Rawdata/imported_${_RUN_TAXID}" "$result_fs" || { map_error_msg "failed copying Rawdata/imported_${_RUN_TAXID} to $result_fs"; return 255; }
        fi
        
        map_info_msg "pipeline pass 3 : done"
        
        #-------------------------------------------------------------------------------------------------------------
        # check that results make sense
        #-------------------------------------------------------------------------------------------------------------
        map_info_msg "check result"
        if ! is_dryrun; then
            # nr of mapped genes
            local nmapped=$(grep -c '^' $result_hits)
            nmapped=$((nmapped-1))

            # nr of genes at input
            local inputFastaMap="Rawdata/${_RUN_TAXID}.${EXT_FASTA_MAPTXT}"
            local ngenes=0
            if [[ -f $inputFastaMap ]]; then
                ngenes=$(grep -c '^' $inputFastaMap)
            else
                map_error_msg "missing input fasta map at $inputFastaMap"
            fi
            local f="$(calc 100.0*$nmapped/$ngenes)"
            local lines=()
            lines+=("Run mode               : $_RUN_MODE")
            lines+=("Mapping node id        : $_RUN_NODEID")
            lines+=("Run label              : $todolabel")
            lines+=("N(taxids node)         : $(grep -v "^#" -c "$PL_TODO")")
            lines+=("N(genes input)         : $ngenes")
            lines+=("N(genes mapped)        : $nmapped")
            lines+=("f(genes mapped) [%]    : $f")
            lines+=("Time, map (secs)       : $(delta_time $T0map)")
            
            local L
            map_info_msg "----------------------------------------------------------"
            for L in "${lines[@]}"; do
                map_info_msg "$L"
            done
            map_info_msg "----------------------------------------------------------"
            
            local fresult="Results/${todolabel}.summary.txt"
            #if [[ -f $fresult ]]; then
            #    fresult="${fresult}.1"
            #    map_info_msg "earlier result file already exists - write with new name"
            #fi
            for L in "${lines[@]}"; do
                echo "$L"
            done > $fresult

            map_info_msg "summary in $fresult"
            map_write_metadata || { map_error_msg "failed writing metadata!"; return 255; }
        fi
        
    } 2> $logfile
    
    map_info_msg "pipeline done"
    (( noData == 1 )) && map_warn_msg "no user data was mapped!"
    map_info_msg "final result at: $result_annot_ref"
    return 0
}
