#!/bin/bash

#####
##### Copyright (C) 2020-2021 Aloysius Indrayanto
#####
##### This file is part of the eCxx library, see LICENSE file for the license details.
#####


#####
##### Utility functions
#####

##### Print usage and exit
##### $1 = optional platform name
##### $2 = optional configuration type name/list
##### $3 = optional message; in case $3 == 'help', check if CONF_TYPE != 'help'
printUsageAndExit()
{
    ##### Clear screen
    clear

    ##### If no platform name is specified, list all supported platform names
    if [[ -z "$1" ]]; then
        local PLATFORM_LIST='avr|nodemcu|x86'
    ##### Otherwise list the specified platform name only
    else
        local PLATFORM_LIST="$1"
    fi

    ##### If no configuration type name/list specified, list the generic one
    if [[ -z "$2" ]]; then
        local CONFIG_TYPE_LIST='help|<configuration_type_to_find>'
    ##### Otherwise list the specified configuration type name only
    else
        local CONFIG_TYPE_LIST="${2// /|}"
    fi

    ##### Print an additional error message as needed
    if [[ ! -z "$3" ]]; then
        local ERR_MSG=''
        if [[ "$3" == 'help' ]]; then
            if [[ $CONF_TYPE != 'help' ]]; then
                local ERR_MSG='Invalid configuration type'
            fi
        else
            local ERR_MSG="$3"
        fi
        if [[ ! -z "$ERR_MSG" ]]; then
            echo -e "\n$ERR_MSG!"
        fi
    fi

    ##### Print usage and exit
    echo -e "\nFind the locations of the latest version of GCC and other build tools in this system.\n"
    echo -e "Usage:\n\n$0 [--with-makefile-setvar] ($PLATFORM_LIST) ($CONFIG_TYPE_LIST)\n"

    exit -1
}


##### Print the given argument; in case it contains the home directory name, replace it with with ${HOME}
##### $1 = path name
printArg_replaceHomeDirNameWithEnVar()
{
    if [[ "$1" != *"$HOME"* ]]; then
          echo -n $1
          return
    fi

    echo -n '${HOME}'
    echo -n "${1/$HOME/}"
}


##### Find non-standard GCC dependency library path(s) for LD_LIBRARY_PATH for the specified library name
##### $1 = library name
findNonStd_GCC_LD_LIBRARY_PATH()
{
    ##### Accumulated list of paths
    local ACC_PATH=''

    ##### Determine possible locations of the specified library
    local HOM=( `ls -d "$HOME"/"$1"*    2> /dev/null` )
    local LOC=( `ls -d /usr/local/"$1"* 2> /dev/null` )
    local OPT=( `ls -d /opt/"$1"*       2> /dev/null` )

    ##### Loop through the possible locations
    for I in "${HOM[@]}" "${LOC[@]}" "${OPT[@]}"; do
        ##### Check if the path does exist
        if [[ ! -d "$I" ]]; then
            continue
        fi
        ##### Accumulate the 64-bit library path as needed
        local LST=`ls "$I"/lib64/lib"$1"* 2> /dev/null`
        if [[ ! -z "$LST" ]]; then
            local ACC_PATH="$ACC_PATH:$I/lib64"
        fi
        ##### Accumulate the 32-bit library path as needed
        local LST=`ls "$I"/lib/lib"$1"* 2> /dev/null`
        if [[ ! -z "$LST" ]]; then
            local ACC_PATH="$ACC_PATH:$I/lib"
        fi
    done

    ##### Return the accumulated list of paths
    echo $ACC_PATH
}


##### Find all non-standard GCC dependency library paths for LD_LIBRARY_PATH
##### NOTE : This function cannot differentiate between various GCC target machine
findAllNonStd_GCC_LD_LIBRARY_PATH()
{
    ##### Accumulated list of paths
    local ACC_PATH=''

    ##### GMP
    local FPATH=$( findNonStd_GCC_LD_LIBRARY_PATH gmp )

    if [[ ! -z "$FPATH" ]]; then
        local ACC_PATH="$ACC_PATH:$FPATH"
    fi

    ##### ISL
    local FPATH=$( findNonStd_GCC_LD_LIBRARY_PATH isl )

    if [[ ! -z "$FPATH" ]]; then
        local ACC_PATH="$ACC_PATH:$FPATH"
    fi

    ##### MPC
    local FPATH=$( findNonStd_GCC_LD_LIBRARY_PATH mpc )

    if [[ ! -z "$FPATH" ]]; then
        local ACC_PATH="$ACC_PATH:$FPATH"
    fi

    ##### MPFR
    local FPATH=$( findNonStd_GCC_LD_LIBRARY_PATH mpfr )

    if [[ ! -z "$FPATH" ]]; then
        local ACC_PATH="$ACC_PATH:$FPATH"
    fi

    ##### ZSTD
    local FPATH=$( findNonStd_GCC_LD_LIBRARY_PATH zstd )

    if [[ ! -z "$FPATH" ]]; then
        local ACC_PATH="$ACC_PATH:$FPATH"
    fi

    ##### Return the accumulated list of paths
    echo $ACC_PATH
}


##### Print all non-standard GCC dependency library paths for LD_LIBRARY_PATH
##### $1 = path list
printAllNonStd_GCC_LD_LIBRARY_PATH()
{
    ##### Remove leading :
    local LPATH="$1"

    while true
    do
        if [[ "${LPATH:0:1}" != ':' ]]; then
            break;
        fi
        local LPATH="${LPATH:1}"
    done

    ##### With Makefile set variable command
    if [[ ! "$WITH_MSV" -eq 0 ]]; then
        echo -n 'LD_PATH = LD_LIBRARY_PATH="$(GCC_DIR)/lib:$(GCC_DIR)/lib64:'
        printArg_replaceHomeDirNameWithEnVar "$LPATH"
        echo '"'
    ##### Without Makefile set variable command
    else
        printArg_replaceHomeDirNameWithEnVar "$LPATH"
        echo
    fi

    ##### Done!
    exit 0
}


##### Get Arduino IDE version
##### $1 = path
getArduinoIDEVersion()
{
    ##### Example Arduino version number
    #####     1.8.13
    #####     1.8.4
    #####     1.6.5-r3
    #####     1.6.5
    #####     1.6.0
    #####     1.6.0rc2
    #####     1.6.0rc1

    ##### Set the numeric version to zero first
    local VER_N=0

    ##### Separate the main version string with the patch (-r/rc) version string and check if it is an RC version
    local VER_S=`echo "$1" | grep 'arduino-' | cut -f3 -d/ -s | cut -f2 -d- -s`
    local VER_P=`echo "$1" | grep 'arduino-' | cut -f3 -d/ -s | cut -f3 -d- -s`

    local IS_RC=0
    if [[ ! -z `echo $VER_S | grep rc` ]] || [[ ! -z `echo $VER_P | grep rc` ]]; then
        local IS_RC=1
    fi

    if [[ -z "$VER_P" ]]; then
        local VER_P=`echo "$VER_S" | cut -f2 -dr -s`
        if [[ ! -z "$VER_P" ]]; then
            local VER_S=`echo "$VER_S" | cut -f1 -dr -s`
        fi
    fi

    local VER_P=`echo "$VER_P" | sed 's/[^0-9]*//g'`

    ##### If it is not an RC version and the patch version is zero, set it to maximum
    if [[ "$IS_RC" -eq 0 ]] && [[ -z $VER_P ]]; then
        local VER_P=9
    fi

    ##### Return the numeric version
    if [[ ! -z "$VER_S" ]]; then
        local VER_1=`echo "$VER_S" | cut -f1 -d. -s`
        local VER_2=`echo "$VER_S" | cut -f2 -d. -s`
        local VER_3=`echo "$VER_S" | cut -f3 -d. -s`
        local VER_N=`expr ${VER_1:-0} \* 100000 \+ ${VER_2:-0} \* 1000 \+ ${VER_3:-0} \* 10 \+ ${VER_P:-0}`
    fi

    echo $VER_N
}


##### Get GCC version matching the given architecture(s)
##### $1 = architecture list ; $2 = gcc path ; $3 = LD_LIBRARY_PATH
getGCCVersion()
{
    ##### Check if the executable file does exist
    if [[ ! -x "$2" ]]; then
        echo 0
        return
    fi

    ##### Get the Arduino IDE version (for AVR platform only)
    local AVER_N=0

    if [[ "$1" == avr ]]; then
        local AVER_N=$( getArduinoIDEVersion "$2" )
    fi

    ##### Process the LD_LIBRARY_PATH
    local LDLP="$LD_LIBRARY_PATH"
    if [[ ! -z "$3" ]]; then
        local LDLP="$LDLP:$3"
    fi

    ##### Check if the architecture does match
    local MACHINE=`LD_LIBRARY_PATH="$LDLP" "$2" -dumpmachine`
    local MCMATCH=0

    for I in $1; do
        if [[ "$MACHINE" == "$I"* ]]; then
            local MCMATCH=1
            break
        fi
    done

    if [[ "$MCMATCH" -eq 0 ]]; then
        echo 0
        return
    fi

    ##### Get and extract the version parts
    local VER_1=`LD_LIBRARY_PATH="$LDLP" "$2" -dumpfullversion -dumpversion | cut -f1 -d.`
    local VER_2=`LD_LIBRARY_PATH="$LDLP" "$2" -dumpfullversion -dumpversion | cut -f2 -d.`
    local VER_3=`LD_LIBRARY_PATH="$LDLP" "$2" -dumpfullversion -dumpversion | cut -f3 -d.`

    ##### Return the numeric version
    local VER_N=`expr $VER_1 \* 1000000000 \+ $VER_2 \* 10000000 + $VER_3 \* 100000 \+ $AVER_N`

    echo $VER_N
}


##### Find the root directory of the latest x86 GCC version
##### $1 = LD_LIBRARY_PATH ; $2... = path list
findLatestX86GCCDir()
{
    ##### Extract the parameters
    local LD_PATH="$1"
    shift
    local AR_PATH=("$@")

    ##### Find the latest GCC version
    local GCC_PATH=''
    local GCC_VNUM=0

    for I in "${AR_PATH[@]}"; do
        ##### Get GCC version
        local GCC="$I/bin/gcc"
        local LIB="$I/lib:$I/lib64:$LD_PATH"
        local VER=$( getGCCVersion "i686 x86" "$GCC" "$LIB" )
        ##### Find the latest one
        if [[ "$VER" -gt "$GCC_VNUM" ]]; then
            local GCC_PATH="$I"
            local GCC_VNUM="$VER"
        fi
    done

    echo $GCC_PATH
}


##### Find the root directory of the latest AVR GCC version
##### $1 = LD_LIBRARY_PATH ; $2... = path list
findLatestAVRGCCDir()
{
    ##### Extract the parameters
    local LD_PATH="$1"
    shift
    local AR_PATH=("$@")

    ##### Find the latest GCC version
    local GCC_PATH=''
    local GCC_VNUM=0

    for I in "${AR_PATH[@]}"; do
        ##### Get GCC version
        local GCC="$I/bin/avr-gcc"
        local LIB="$I/lib:$I/lib64:$LD_PATH"
        local VER=$( getGCCVersion "avr" "$GCC" "$LIB" )
        ##### Find the latest one
        if [[ "$VER" -gt "$GCC_VNUM" ]]; then
            local GCC_PATH="$I"
            local GCC_VNUM="$VER"
        fi
    done

    echo $GCC_PATH
}


##### Find the root directory of the latest XTENSA GCC version
##### $1 = LD_LIBRARY_PATH ; $2... = path list
findLatestXTENSAGCCDir()
{
    ##### Extract the parameters
    local LD_PATH="$1"
    shift
    local AR_PATH=("$@")

    ##### Find the latest GCC version
    local GCC_PATH=''
    local GCC_VNUM=0

    for I in "${AR_PATH[@]}"; do
        ##### Get GCC version
        local GCC="$I/bin/xtensa-lx106-elf-gcc"
        local LIB="$I/lib:$I/lib64:$LD_PATH"
        local VER=$( getGCCVersion "xtensa" "$GCC" "$LIB" )
        ##### Find the latest one
        if [[ "$VER" -gt "$GCC_VNUM" ]]; then
            local GCC_PATH="$I"
            local GCC_VNUM="$VER"
        fi
    done

    echo $GCC_PATH
}


##### Get AVRDUDE version
##### $1 = avrdude path
getAVRDUDEVersion()
{
    ##### Check if the executable file does exist
    if [[ ! -x "$1" ]]; then
        echo 0
        return
    fi

    ##### Get the Arduino IDE version
    local AVER_N=$( getArduinoIDEVersion "$1" )

    ##### Get and extract the version parts
    local VER_S=`"$1" 2>&1 | sed -n -e 's/^.*version \([0-9\.\-]*\)\,.*/\1/p'`

    local VER_M=`echo $VER_S | cut -f1 -d- -s`
    local VER_P=`echo $VER_S | cut -f2 -d- -s`

    if [[ -z "$VER_M" ]]; then
        local VER_M="$VER_S"
    fi

    local VER_1=`echo $VER_M | cut -f1 -d.`
    local VER_2=`echo $VER_M | cut -f2 -d.`
    local VER_3=`echo $VER_M | cut -f3 -d.`

    ##### Return the numeric version
    local VER_N=`expr $VER_1 \* 1000000000000000000 \+ $VER_2 \* 10000000000000000 + ${VER_3:-0} \* 100000000000000 + ${VER_P:-0} \* 1000000 \+ $AVER_N`

    echo $VER_N
}


##### Find the root directory of the latest AVRDUDE version
##### $1 = path list
findLatestAVRDUDEDir()
{
    ##### Extract the parameters
    local AR_PATH=("$@")

    ##### Find the latest AVRDUDE version
    local AVD_PATH=''
    local AVD_VNUM=0

    for I in "${AR_PATH[@]}"; do
        ##### Get AVRDUDE version
        local AVD="$I/bin/avrdude"
        local VER=$( getAVRDUDEVersion "$AVD" )
        ##### Find the latest one
        if [[ "$VER" -gt "$AVD_VNUM" ]]; then
            local AVD_PATH="$I"
            local AVD_VNUM="$VER"
        fi
    done

    echo $AVD_PATH
}


##### Get AVR DFP version
##### $1 = DFP path
getAVRDFPVersion()
{
    ##### Check if the directory does exist
    if [[ ! -d "$1" ]]; then
        echo 0
        return
    fi

    ##### Get and extract the version parts
    local VER_S=`echo $1 | cut -f2 -d_ -s`

    if [[ -z "$VER_S" ]]; then
        echo 0
        return
    fi

    local VER_1=`echo $VER_S | cut -f2 -d.`
    local VER_2=`echo $VER_S | cut -f3 -d.`
    local VER_3=`echo $VER_S | cut -f4 -d.`

    ##### Return the numeric version
    local VER_N=`expr $VER_1 \* 1000000 \+ $VER_2 \* 10000 + ${VER_3:-0}`

    echo $VER_N
}


##### Find the root directory of the latest AVR DFP version
##### $1 = path list
findLatestAVRDFPDir()
{
    ##### Extract the parameters
    local AR_PATH=("$@")

    ##### Find the latest AVR DFP version
    local DFP_PATH=''
    local DFP_VNUM=0

    for I in "${AR_PATH[@]}"; do
        ##### Get AVR DFP version
        local VER=$( getAVRDFPVersion "$I" )
        ##### Find the latest one
        if [[ "$VER" -gt "$DFP_VNUM" ]]; then
            local DFP_PATH="$I"
            local DFP_VNUM="$VER"
        fi
    done

    echo $DFP_PATH
}


##### Get UPDIPROG version
##### $1 = UPDIPROG path
getUPDIPROGVersion()
{
    ##### Check if the executable file does exist
    if [[ ! -x "$1" ]]; then
        echo 0
        return
    fi

    ##### Get and extract the version parts
    local VER_S=`"$1" | sed -n -e 's/^.*Ver\. \([0-9\.]*\) (.*/\1/p'`

    if [[ -z "$VER_S" ]]; then
        echo 0
        return
    fi

    local VER_1=`echo $VER_S | cut -f1 -d.`
    local VER_2=`echo $VER_S | cut -f2 -d.`
    local VER_3=`echo $VER_S | cut -f3 -d.`

    ##### Return the numeric version
    local VER_N=`expr $VER_1 \* 10000 \+ $VER_2 \* 100 + ${VER_3:-0}`

    echo $VER_N
}


##### Find the executable path of the latest UPDIPROG version
##### $1 = path list
findLatestUPDIPROGExec()
{
    ##### Extract the parameters
    local AR_PATH=("$@")

    ##### Find the latest UPDIPROG version
    local UPR_PATH=''
    local UPR_VNUM=0

    for I in "${AR_PATH[@]}"; do
        ##### Get AVRDUDE version
        local AVD="$I/updiprog"
        local VER=$( getUPDIPROGVersion "$AVD" )
        ##### Find the latest one
        if [[ "$VER" -gt "$UPR_VNUM" ]]; then
            local UPR_PATH="$I"
            local UPR_VNUM="$VER"
        fi
    done

    echo $UPR_PATH/updiprog
}


##### Get Arduino ESP8266 package version
##### $1 = package path
getArduinoESP8266PackageVersion()
{
    ##### Check if the directory does exist
    if [[ ! -d "$1" ]]; then
        echo 0
        return
    fi

    ##### Get and extract the version parts
    local VER_L=`ls -d $1/hardware/esp8266/* 2> /dev/null`
    local VER_S=`echo ${VER_L##*/}`

    if [[ -z "$VER_S" ]]; then
        echo 0
        return
    fi

    local VER_1=`echo $VER_S | cut -f1 -d.`
    local VER_2=`echo $VER_S | cut -f2 -d.`
    local VER_3=`echo $VER_S | cut -f3 -d.`

    ##### Return the numeric version
    local VER_N=`expr $VER_1 \* 1000000 \+ $VER_2 \* 10000 + ${VER_3:-0}`

    echo $VER_N
}


##### Find the root directory of the latest Arduino ESP8266 package version
##### $1 = path list
findLatestArduinoESP8266PackageDir()
{
    ##### Extract the parameters
    local AR_PATH=("$@")

    local PKG_PATH=''
    local PKG_VNUM=0

    ##### Find the latest ESP8266 package version
    for I in "${AR_PATH[@]}"; do
        ##### Get ESP8266 package version
        local VER=$( getArduinoESP8266PackageVersion "$I" )
        ##### Find the latest one
        if [[ "$VER" -gt "$PKG_VNUM" ]]; then
            local PKG_PATH="$I"
            local PKG_VNUM="$VER"
        fi
    done

    local VER_L=`ls -d $PKG_PATH/hardware/esp8266/* 2> /dev/null`
    local VER_S=`echo ${VER_L##*/}`

    echo $VER_S
}


#####
##### Main script entry point
#####

##### Check if the user gives the correct number of arguments
if [[ ! -n "$1" ]] || [[ ! -n "$2" ]] || [[ $# > 3 ]]; then
    printUsageAndExit
fi

##### Check if the first parameter is --with-makefile-setvar
WITH_MSV=0

if [[ "$1" == '--with-makefile-setvar' ]]; then
    WITH_MSV=1
    shift
fi

##### Get and check the platform name
PLATFORM=$1
shift

if [[ $PLATFORM != 'avr' ]] && [[ $PLATFORM != 'nodemcu' ]] && [[ $PLATFORM != 'x86' ]]; then
    printUsageAndExit '' '' 'Invalid platform'
fi

##### Get the configuration type and check if it is empty
CONF_TYPE=$1
shift

if [[ -z "$CONF_TYPE" ]]; then
    printUsageAndExit '' '' 'No configuration type specified'
fi

##### Check for extra parameter(s)
if [[ $# > 0 ]]; then
    printUsageAndExit '' '' 'Too many arguments'
fi


#####
##### Platform - x86
#####
execFindToolsX86()
{
    ##### Check the configuration type name
    if [[ $CONF_TYPE != 'gcc_dir'           ]] &&
       [[ $CONF_TYPE != 'nonstd_gcc_ldpath' ]];
    then
        printUsageAndExit x86 'gcc_dir nonstd_gcc_ldpath' help
        exit -1
    fi
    ##### Find all non-standard LD_LIBRARY_PATH
    local NSTDLPATH=$( findAllNonStd_GCC_LD_LIBRARY_PATH )
    ##### GCC root directory
    if [[ $CONF_TYPE == 'gcc_dir' ]]; then
        ##### Determine possible locations of GCC
        local LOC1=( `echo /usr /usr/local              ` )
        local LOC3=( `ls -d /usr/local/gcc* 2> /dev/null` )
        local LOC3=( `ls -d /usr/share/gcc* 2> /dev/null` )
        local LOC4=( `ls -d /opt/gcc*       2> /dev/null` )
        local LOC2=( `ls -d "$HOME"/gcc*    2> /dev/null` )
        ##### Find the latest GCC version
        local GCC=$( findLatestX86GCCDir "$NSTDLPATH" "${LOC1[@]}" "${LOC2[@]}" "${LOC3[@]}" "${LOC4[@]}" )
        ##### With Makefile set variable command
        if [[ ! "$WITH_MSV" -eq 0 ]]; then
            echo -n 'GCC_DIR = '
        fi
        ##### With and without Makefile set variable command
        printArg_replaceHomeDirNameWithEnVar $GCC
        echo
        ##### Done!
        exit 0
    fi
    ##### All non-standard GCC dependency library paths for LD_LIBRARY_PATH
    if [[ $CONF_TYPE == 'nonstd_gcc_ldpath' ]]; then
        printAllNonStd_GCC_LD_LIBRARY_PATH "$NSTDLPATH"
    fi
}

if [[ $PLATFORM == 'x86' ]]; then
    execFindToolsX86
fi


#####
##### Platform - AVR
#####
execFindToolsAVR()
{
    ##### Check the configuration type name
    if [[ $CONF_TYPE != 'gcc_dir'           ]] &&
       [[ $CONF_TYPE != 'avrdude_dir'       ]] &&
       [[ $CONF_TYPE != 'avrdude_conf'      ]] &&
       [[ $CONF_TYPE != 'dfp_dir'           ]] &&
       [[ $CONF_TYPE != 'updiprog'          ]] &&
       [[ $CONF_TYPE != 'nonstd_gcc_ldpath' ]];
    then
        printUsageAndExit avr 'gcc_dir avrdude_dir avrdude_conf dfp_dir updiprog nonstd_gcc_ldpath' help
        exit -1
    fi
    ##### Find all non-standard LD_LIBRARY_PATH
    local NSTDLPATH=$( findAllNonStd_GCC_LD_LIBRARY_PATH )
    ###### GCC root directory
    if [[ $CONF_TYPE == 'gcc_dir' ]]; then
        ##### Determine possible locations of GCC
        local LOC1=( `echo /usr /usr/local                                                  ` )
        local LOC2=( `ls -d /usr/local/avr-gcc*                                 2> /dev/null` )
        local LOC3=( `ls -d /usr/share/avr-gcc*                                 2> /dev/null` )
        local LOC4=( `ls -d /opt/avr-gcc*                                       2> /dev/null` )
        local LOC5=( `ls -d "$HOME"/avr-gcc*                                    2> /dev/null` )
        local LOC6=( `ls -d /usr/local/arduino*/hardware/tools/avr              2> /dev/null` )
        local LOC7=( `ls -d /usr/share/arduino*/hardware/tools/avr              2> /dev/null` )
        local LOC8=( `ls -d /opt/arduino*/hardware/tools/avr                    2> /dev/null` )
        local LOC9=( `ls -d "$HOME"/arduino*/hardware/tools/avr                 2> /dev/null` )
        local LOCA=( `ls -d "$HOME"/.arduino15/packages/arduino/tools/avr-gcc/* 2> /dev/null` )
        ##### Find the latest GCC version
        local GCC=$( findLatestAVRGCCDir "$NSTDLPATH" "${LOC1[@]}" "${LOC2[@]}" "${LOC3[@]}" "${LOC4[@]}" "${LOC5[@]}" "${LOC6[@]}" "${LOC7[@]}" "${LOC8[@]}" "${LOC9[@]}" "${LOCA[@]}" )
        ##### With Makefile set variable command
        if [[ ! "$WITH_MSV" -eq 0 ]]; then
            echo -n 'GCC_DIR = '
        fi
        ##### With and without Makefile set variable command
        printArg_replaceHomeDirNameWithEnVar $GCC
        echo
        ##### Done!
        exit 0
    fi
    ###### AVRDUDE root directory or configuration file
    if [[ $CONF_TYPE == 'avrdude_dir' ]] || [[ $CONF_TYPE == 'avrdude_conf' ]]; then
        ##### Determine possible locations of AVRDUDE
        local LOC1=( `echo /usr /usr/local                                                  ` )
        local LOC3=( `ls -d /usr/local/arduino*/hardware/tools/avr              2> /dev/null` )
        local LOC3=( `ls -d /usr/share/arduino*/hardware/tools/avr              2> /dev/null` )
        local LOC3=( `ls -d /opt/arduino*/hardware/tools/avr                    2> /dev/null` )
        local LOC4=( `ls -d "$HOME"/arduino*/hardware/tools/avr                 2> /dev/null` )
        local LOC5=( `ls -d "$HOME"/.arduino15/packages/arduino/tools/avrdude/* 2> /dev/null` )
        ##### Find the latest AVRDUDE version
        local AVD=$( findLatestAVRDUDEDir "${LOC1[@]}" "${LOC2[@]}" "${LOC3[@]}" "${LOC4[@]}" "${LOC5[@]}" )
        ##### AVRDUDE root directory
        if [[ $CONF_TYPE == 'avrdude_dir' ]]; then
            ##### With Makefile set variable command
            if [[ ! "$WITH_MSV" -eq 0 ]]; then
                echo -n 'AVRDUDE_ROOT_DIR = '
                if [[ $AVD != '/usr' ]] && [[ $AVD != '/usr/local' ]]; then
                    printArg_replaceHomeDirNameWithEnVar $AVD
                fi
                echo
            ##### Without Makefile set variable command
            else
                printArg_replaceHomeDirNameWithEnVar $AVD
                echo
            fi
        ###### AVRDUDE configuration file
        else
            ##### Find the configuration file
            local AVD_CONF=''
            if [[ -f "$AVD/etc/avrdude.conf" ]]; then
                local AVD_CONF="$AVD/etc/avrdude.conf"
            elif [[ -f "$AVD/etc/avrdude/avrdude.conf" ]]; then
                local AVD_CONF="$AVD/etc/avrdude/avrdude.conf"
            elif [[ $AVD == '/usr' ]] || [[ $AVD != '/usr/local' ]]; then
                if [[ -f "/etc/avrdude.conf" ]]; then
                    local AVD_CONF="/etc/avrdude.conf"
                elif [[ -f "/etc/avrdude/avrdude.conf" ]]; then
                    local AVD_CONF="/etc/avrdude/avrdude.conf"
                fi
            fi
            ##### With Makefile set variable command
            if [[ ! "$WITH_MSV" -eq 0 ]]; then
                echo -n 'AVRDUDE_CONF = '
            fi
            ##### With and without Makefile set variable command
            printArg_replaceHomeDirNameWithEnVar $AVD_CONF
            echo
        fi
        ##### Done!
        exit 0
    fi
    ###### Device pack directory
    if [[ $CONF_TYPE == 'dfp_dir' ]]; then
        ##### Determine possible locations of AVR DFP
        local BASE=Atmel.ATmega_DFP
        local LOCS=( /usr /usr/share /etc /usr/local /usr/local/share /usr/local/etc /opt "$HOME" )
        local LOC1=()
        for I in "${LOCS[@]}"; do
            local LOCL=( `ls -d $I/$BASE* 2> /dev/null` )
            local LOC1=( "${LOC1[@]}" "${LOCL[@]}" )
        done
        local LOC2=( `ls -d "$HOME"/Arduino*/$BASE*                               2> /dev/null` )
        local LOC3=( `ls -d "$HOME"/Arduino*/tools/$BASE*                         2> /dev/null` )
        local LOC4=( `ls -d "$HOME"/Arduino*/libraries/$BASE*                     2> /dev/null` )
        local LOC5=( `ls -d "$HOME"/arduino*/$BASE*                               2> /dev/null` )
        local LOC6=( `ls -d "$HOME"/arduino*/hardware/$BASE*                      2> /dev/null` )
        local LOC7=( `ls -d "$HOME"/arduino*/hardware/arduino/$BASE*              2> /dev/null` )
        local LOC8=( `ls -d "$HOME"/arduino*/hardware/arduino/avr/$BASE*          2> /dev/null` )
        local LOC9=( `ls -d "$HOME"/arduino*/tools/$BASE*                         2> /dev/null` )
        local LOCA=( `ls -d "$HOME"/.arduino15/$BASE*                             2> /dev/null` )
        local LOCB=( `ls -d "$HOME"/.arduino15/packages/$BASE*                    2> /dev/null` )
        local LOCC=( `ls -d "$HOME"/.arduino15/packages/*/$BASE*                  2> /dev/null` )
        local LOCD=( `ls -d "$HOME"/.arduino15/packages/*/hardware/$BASE*         2> /dev/null` )
        local LOCE=( `ls -d "$HOME"/.arduino15/packages/*/hardware/avr/$BASE*     2> /dev/null` )
        local LOCF=( `ls -d "$HOME"/.arduino15/packages/*/hardware/megaavr/$BASE* 2> /dev/null` )
        local LOCG=( `ls -d "$HOME"/.arduino15/packages/*/tools/$BASE*            2> /dev/null` )
        local LOCH=( `ls -d "$HOME"/.arduino15/packages/*/tools/avr-gcc/$BASE*    2> /dev/null` )
        ##### Find the latest DFP version
        local DFP=$( findLatestAVRDFPDir "${LOC1[@]}" "${LOC2[@]}" "${LOC3[@]}" "${LOC4[@]}" "${LOC5[@]}" "${LOC6[@]}" "${LOC7[@]}" "${LOC8[@]}" "${LOC9[@]}" "${LOCA[@]}" "${LOCB[@]}" "${LOCC[@]}" "${LOCD[@]}" "${LOCE[@]}" "${LOCF[@]}" "${LOCG[@]}" "${LOCH[@]}" )
        ##### With Makefile set variable command
        if [[ ! "$WITH_MSV" -eq 0 ]]; then
            echo -n 'DFP_DIR = '
        fi
        ##### With and without Makefile set variable command
        printArg_replaceHomeDirNameWithEnVar $DFP
        echo
        ##### Done!
        exit 0
    fi
    ###### UPDIPROG location
    if [[ $CONF_TYPE == 'updiprog' ]]; then
        ##### Determine possible locations of UPDIPROG
        local LOC1=( `echo /usr/bin /usr/local/bin /opt "$HOME" ` )
        local LOC2=( `ls -d /usr/local/updiprog* 2> /dev/null   ` )
        local LOC3=( `ls -d /usr/share/updiprog* 2> /dev/null   ` )
        local LOC4=( `ls -d /opt/updiprog*       2> /dev/null   ` )
        local LOC5=( `ls -d "$HOME"/updiprog*    2> /dev/null   ` )
        ##### Find the latest UPDIPROG version
        local UPDIPROG=$( findLatestUPDIPROGExec "${LOC1[@]}" "${LOC2[@]}" "${LOC3[@]}" "${LOC4[@]}" "${LOC5[@]}" )
        ##### With Makefile set variable command
        if [[ ! "$WITH_MSV" -eq 0 ]]; then
            echo -n 'UPDIPROG = '
        fi
        ##### With and without Makefile set variable command
        printArg_replaceHomeDirNameWithEnVar $UPDIPROG
        echo
        ##### Done!
        exit 0
    fi
    ##### All non-standard GCC dependency library paths for LD_LIBRARY_PATH
    if [[ $CONF_TYPE == 'nonstd_gcc_ldpath' ]]; then
        printAllNonStd_GCC_LD_LIBRARY_PATH "$NSTDLPATH"
    fi
}

if [[ $PLATFORM == 'avr' ]]; then
    execFindToolsAVR
fi


#####
##### Platform - NodeMCU
#####
execFindToolsNodeMCU()
{
    ##### Check the configuration type name
    if [[ $CONF_TYPE != 'package_root'      ]] &&
       [[ $CONF_TYPE != 'package_version'   ]] &&
       [[ $CONF_TYPE != 'custom_gcc_dir'    ]] &&
       [[ $CONF_TYPE != 'nonstd_gcc_ldpath' ]];
    then
        printUsageAndExit nodemcu 'package_root package_version custom_gcc_dir nonstd_gcc_ldpath' help
        exit -1
    fi
    ##### Find all non-standard LD_LIBRARY_PATH
    local NSTDLPATH=$( findAllNonStd_GCC_LD_LIBRARY_PATH )
    ###### Arduino ESP8266 package root directory
    if [[ $CONF_TYPE == 'package_root' ]]; then
        ##### Determine the possible location of the root package directory
        local PKG=`echo "$HOME"/.arduino15/packages`
        local LOC=( `ls -d "$PKG"/esp8266* 2> /dev/null` )
        ##### Check if it contains any Arduino ESP8266 package
        local VER=$( findLatestArduinoESP8266PackageDir "${LOC[@]}" )
        if [[ -z "$VER" ]]; then
            local PKG=''
        fi
        ##### With Makefile set variable command
        if [[ ! "$WITH_MSV" -eq 0 ]]; then
            echo -n 'ROOT_ARDUINO_PACKAGE_DIR = '
        fi
        #### With and without Makefile set variable command
        printArg_replaceHomeDirNameWithEnVar $PKG
        echo
        #### Done!
        exit 0
    fi
    ##### Arduino ESP8266 package version
    if [[ $CONF_TYPE == 'package_version' ]]; then
        ##### Determine possible locations of the ESP8266 package directories
        local LOC=( `ls -d "$HOME"/.arduino15/packages/esp8266* 2> /dev/null` )
        ##### Find the latest Arduino ESP8266 package version
        local VER=$( findLatestArduinoESP8266PackageDir "${LOC[@]}" )
        ##### With Makefile set variable command
        if [[ ! "$WITH_MSV" -eq 0 ]]; then
            echo -n 'PACKAGE_VERSION = '
        fi
        #### With and without Makefile set variable command
        echo $VER
        #### Done!
        exit 0
    fi
    ##### Custom GCC directory
    if [[ $CONF_TYPE == 'custom_gcc_dir' ]]; then
        ##### Determine possible locations of GCC
        local LOC1=( `ls -d /usr/local/xtensa-lx106-elf*                                             2> /dev/null` )
        local LOC2=( `ls -d /usr/share/xtensa-lx106-elf*                                             2> /dev/null` )
        local LOC3=( `ls -d /opt/xtensa-lx106-elf*                                                   2> /dev/null` )
        local LOC4=( `ls -d "$HOME"/xtensa-lx106-elf*                                                2> /dev/null` )
        local LOC5=( `ls -d /usr/local/{i686,x86_64}-linux-gnu.xtensa-lx106-elf-*.*/xtensa-lx106-elf 2> /dev/null` )
        local LOC6=( `ls -d /usr/share/{i686,x86_64}-linux-gnu.xtensa-lx106-elf-*.*/xtensa-lx106-elf 2> /dev/null` )
        local LOC7=( `ls -d /opt/{i686,x86_64}-linux-gnu.xtensa-lx106-elf-*.*/xtensa-lx106-elf       2> /dev/null` )
        local LOC8=( `ls -d "$HOME"/{i686,x86_64}-linux-gnu.xtensa-lx106-elf-*.*/xtensa-lx106-elf    2> /dev/null` )
        ##### Find the latest GCC version
        local GCC=$( findLatestXTENSAGCCDir "$NSTDLPATH" "${LOC1[@]}" "${LOC2[@]}" "${LOC3[@]}" "${LOC4[@]}" "${LOC5[@]}" "${LOC6[@]}" "${LOC7[@]}" "${LOC8[@]}" )
        ##### With Makefile set variable command
        if [[ ! "$WITH_MSV" -eq 0 ]]; then
            echo -n 'CUSTOM_GCC_DIR = '
        fi
        ##### With and without Makefile set variable command
        printArg_replaceHomeDirNameWithEnVar $GCC
        echo
        ##### Done!
        exit 0
    fi
    ##### All non-standard GCC dependency library paths for LD_LIBRARY_PATH
    if [[ $CONF_TYPE == 'nonstd_gcc_ldpath' ]]; then
        printAllNonStd_GCC_LD_LIBRARY_PATH "$NSTDLPATH"
    fi
}

if [[ $PLATFORM == 'nodemcu' ]]; then
    execFindToolsNodeMCU
fi
