#!/bin/bash

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


#####
##### Common variables
#####
CBA_STATE_FILE=/tmp/eCxx_check_build_all.state
CBA_CHECKCOUNT=0
CBA_DO_MIN_CHK=0

SHADOW_CONFIG_FILE=Makefile.shadow.config


#####
##### Check if this script is run from the original source directory (not a shadow directory)
#####
if [[ -L "$0" ]] || [[ -L src ]] || [[ -L include ]]; then
    clear
    echo -e "\nThis script must be run from the original source code directory root.\n"
    echo -e "This directory (`pwd`) appears to be a shadow directory!\n"
    exit -1
fi

if [[ ! -d src ]] || [[ ! -d include ]]; then
    clear
    echo -e "\nThis script must be run from the original source code directory root.\n"
    echo -e "This directory (`pwd`) does not appear to be the original source code directory root!\n"
    exit -1
fi


#####
##### Helper functions
#####
checkBuild()
{
    ##### Remove possible trailing whitespace(s) from the shadow configuration file
    sed -i 's/[ \t]*$//' "$SHADOW_CONFIG_FILE"

    ##### Check if the build check using the current configuration has already been completed successfully
    local ALREADY_EXECUTED=0

    local CONFIG_DETAIL_ES=`cat "$SHADOW_CONFIG_FILE" | tr '\r\n' ' '`
    local CONFIG_DETAIL_ES=`printf '%q' "$CONFIG_DETAIL_ES"`

    if [[ -f "$CBA_STATE_FILE" ]]; then
        ##### Check the marker from the state file
        local CHECK_STX=`cat "$CBA_STATE_FILE" | grep -F "$CONFIG_DETAIL_ES"`
        ##### Check if already done
        if [[ ! -z "$CHECK_STX" ]]; then
            local ALREADY_EXECUTED=1
        fi
    fi

    ##### Simply return if already executed
    if [[ $ALREADY_EXECUTED -eq 1 ]]; then
        return
    fi

    ##### Clean all (preserve the shadow configuration file)
    mv $SHADOW_CONFIG_FILE      $SHADOW_CONFIG_FILE.save
    make -k allclean > /dev/null
    mv $SHADOW_CONFIG_FILE.save $SHADOW_CONFIG_FILE

    ##### Build all
    make all_nx
    local RETVAL=$?

    ##### Clean all (preserve the shadow configuration file)
    mv $SHADOW_CONFIG_FILE      $SHADOW_CONFIG_FILE.save
    make -k allclean > /dev/null
    mv $SHADOW_CONFIG_FILE.save $SHADOW_CONFIG_FILE

    ##### Check for error
    if [[ $RETVAL -ne 0 ]]; then
        echo
        cat "$SHADOW_CONFIG_FILE"
        echo
        exit $RETVAL
    fi

    ##### If the build check is completed successfully write the marker to the state file
    echo "$CONFIG_DETAIL_ES" >> "$CBA_STATE_FILE"

    ##### Increment the counter value
    CBA_CHECKCOUNT=`expr $CBA_CHECKCOUNT + 1`
}


#####
##### Check for x86 platform
#####
execCheckX86()
{
    ##### Build tool locations
    local X86_GCC_SYS='/usr'            #  4.8.5 (el7)
    local X86_GCC_610='/opt/gcc-6.1.0'  #  6.1.0
    local X86_GCC_750='/opt/gcc-7.5.0'  #  7.5.0
    local X86_GCC_A10='/opt/gcc-10.1.0' # 10.1.0

    local X86_GCC_LDP='/opt/isl-0.16.1/lib'

    ##### Prepare the build check specification list
    if [[ CBA_DO_MIN_CHK -eq 1 ]]; then
        local X86_CHECK_LIST=(
            c++11 "$X86_GCC_SYS"

            c++14 "$X86_GCC_A10"
            c++17 "$X86_GCC_A10"
        )
    else
        local X86_CHECK_LIST=(
            c++11 "$X86_GCC_SYS"

            c++11 "$X86_GCC_610"
            c++14 "$X86_GCC_610"

            c++11 "$X86_GCC_750"
            c++14 "$X86_GCC_750"

            c++11 "$X86_GCC_A10"
            c++14 "$X86_GCC_A10"
            c++17 "$X86_GCC_A10"
        )
    fi

    local X86_CHECK_ELEN=2

    ##### Loop trough the build check specification list
    for (( I=0; I<${#X86_CHECK_LIST[@]}; I+=$X86_CHECK_ELEN )); do
        ##### Extract the values
        local CPP_VER=${X86_CHECK_LIST[    $I        ]}
        local GCC_DIR=${X86_CHECK_LIST[ (( $I + 1 )) ]}
        local LD_PATH='LD_LIBRARY_PATH="$(GCC_DIR)/lib:$(GCC_DIR)/lib64:'$X86_GCC_LDP'"'
        ##### Generate the shadow configuration file
        rm -f "$SHADOW_CONFIG_FILE"
        echo "PLATFORM = x86     " >> "$SHADOW_CONFIG_FILE"
        echo "CPP_VER  = $CPP_VER" >> "$SHADOW_CONFIG_FILE"
        echo "GCC_DIR  = $GCC_DIR" >> "$SHADOW_CONFIG_FILE"
        echo "LD_PATH  = $LD_PATH" >> "$SHADOW_CONFIG_FILE"
        ##### Check build with the above configuration
        checkBuild
    done
}


#####
##### Check for AVR platform
#####
execCheckAVR()
{
    ##### Build tool locations
    local AVR_GCC_492='/usr'                                   # 4.9.2 (el7.lux)
    local AVR_GCC_730='/opt/arduino-1.8.13/hardware/tools/avr' # 7.3.0

    local AVR_DFP_DIR='${HOME}/Atmel.ATmega_DFP.1.5.362'

    ##### Prepare the build check specification list
    if [[ CBA_DO_MIN_CHK -eq 1 ]]; then
        local AVR_CHECK_LIST=(
            atmega32    16000000UL c++11 "$AVR_GCC_492" ''
            atmega128   16000000UL c++11 "$AVR_GCC_492" ''

            atmega328p   8000000UL c++11 "$AVR_GCC_492" ''
            atmega328pb 16000000UL c++14 "$AVR_GCC_730" "$AVR_DFP_DIR"

            atmega324p  16000000UL c++11 "$AVR_GCC_492" ''
            atmega1284p 20000000UL c++11 "$AVR_GCC_492" ''

            atmega2560  16000000UL c++11 "$AVR_GCC_492" ''
            atmega2560  16000000UL c++14 "$AVR_GCC_730" ''

            atmega32u4  16000000UL c++11 "$AVR_GCC_492" ''

            atmega4808  16000000UL c++11 "$AVR_GCC_730" "$AVR_DFP_DIR"
            atmega4808  16000000UL c++17 "$AVR_GCC_730" "$AVR_DFP_DIR"
        )
    else
        local AVR_CHECK_LIST=(
            atmega32    16000000UL c++11 "$AVR_GCC_492" ''
            atmega32    16000000UL c++14 "$AVR_GCC_730" ''
            atmega128   16000000UL c++11 "$AVR_GCC_492" ''
            atmega128   16000000UL c++14 "$AVR_GCC_730" ''

            atmega328p   8000000UL c++11 "$AVR_GCC_492" ''
            atmega328p   8000000UL c++11 "$AVR_GCC_730" ''
            atmega328p   8000000UL c++14 "$AVR_GCC_730" ''
            atmega328p   8000000UL c++17 "$AVR_GCC_730" ''

            atmega168pb 16000000UL c++11 "$AVR_GCC_730" "$AVR_DFP_DIR"
            atmega328pb 16000000UL c++11 "$AVR_GCC_730" "$AVR_DFP_DIR"
            atmega328pb 16000000UL c++14 "$AVR_GCC_730" "$AVR_DFP_DIR"

            atmega324p  16000000UL c++11 "$AVR_GCC_492" ''
            atmega324p  16000000UL c++14 "$AVR_GCC_730" ''

            atmega1284p 20000000UL c++11 "$AVR_GCC_492" ''
            atmega1284p 20000000UL c++14 "$AVR_GCC_730" ''

            atmega2560  16000000UL c++11 "$AVR_GCC_492" ''
            atmega2560  16000000UL c++14 "$AVR_GCC_730" ''
            atmega2560  16000000UL c++17 "$AVR_GCC_730" ''

            atmega32u4  16000000UL c++11 "$AVR_GCC_492" ''
            atmega32u4  16000000UL c++14 "$AVR_GCC_730" ''

            at90usb1286 16000000UL c++11 "$AVR_GCC_492" ''
            at90usb1286 16000000UL c++14 "$AVR_GCC_730" ''
            at90usb1286 16000000UL c++17 "$AVR_GCC_730" ''

            atmega4808  16000000UL c++11 "$AVR_GCC_730" "$AVR_DFP_DIR"
            atmega4808  16000000UL c++14 "$AVR_GCC_730" "$AVR_DFP_DIR"
            atmega4808  16000000UL c++17 "$AVR_GCC_730" "$AVR_DFP_DIR"

            atmega4809  16000000UL c++11 "$AVR_GCC_730" "$AVR_DFP_DIR"
            atmega4809  16000000UL c++14 "$AVR_GCC_730" "$AVR_DFP_DIR"
            atmega4809  16000000UL c++17 "$AVR_GCC_730" "$AVR_DFP_DIR"
        )
    fi

    local AVR_CHECK_ELEN=5

    ##### Loop trough the build check specification list
    for (( I=0; I<${#AVR_CHECK_LIST[@]}; I+=$AVR_CHECK_ELEN )); do
        ##### Extract the values
        local AVR_MCU=${AVR_CHECK_LIST[    $I        ]}
        local CPU_CLK=${AVR_CHECK_LIST[ (( $I + 1 )) ]}
        local CPP_VER=${AVR_CHECK_LIST[ (( $I + 2 )) ]}
        local GCC_DIR=${AVR_CHECK_LIST[ (( $I + 3 )) ]}
        local DFP_DIR=${AVR_CHECK_LIST[ (( $I + 4 )) ]}
        ##### Generate the shadow configuration file
        rm -f "$SHADOW_CONFIG_FILE"
        echo "PLATFORM = AVR     " >> "$SHADOW_CONFIG_FILE"
        echo "AVR_MCU  = $AVR_MCU" >> "$SHADOW_CONFIG_FILE"
        echo "F_CPU    = $CPU_CLK" >> "$SHADOW_CONFIG_FILE"
        echo "CPP_VER  = $CPP_VER" >> "$SHADOW_CONFIG_FILE"
        echo "GCC_DIR  = $GCC_DIR" >> "$SHADOW_CONFIG_FILE"
        echo "LD_PATH  =         " >> "$SHADOW_CONFIG_FILE"
        echo "DFP_DIR  = $DFP_DIR" >> "$SHADOW_CONFIG_FILE"
        ##### Check build with the above configuration
        checkBuild
    done
}


#####
##### Check for NodeMCU platform
#####
execCheckNodeMCU()
{
    ##### Build tool locations
    local NMC_ROOT_ARDUINO_PACKAGES_DIR='${HOME}/.arduino15/packages'
    local NMC_CUSTOM_GCC_DIR_OLDLIB_220='/opt/xtensa-lx106-elf-10.2.0'
    local NMC_CUSTOM_GCC_DIR_NEWLIB_400='/opt/x86_64-linux-gnu.xtensa-lx106-elf-dba3cc8.201223/xtensa-lx106-elf'

    ##### Prepare the build check specification list
    if [[ CBA_DO_MIN_CHK -eq 1 ]]; then
        local NMC_CHECK_LIST=(
            2.5.0 c++11 ''                               'ESP-01' 0 0

            0.0.0 c++11 "$NMC_CUSTOM_GCC_DIR_OLDLIB_220" ''       1 0

            0.0.0 c++17 "$NMC_CUSTOM_GCC_DIR_NEWLIB_400" ''       1 1
        )
    else
        local NMC_CHECK_LIST=(
            2.5.0 c++11 ''                               ''       0 0
            2.5.0 c++11 ''                               'ESP-01' 0 0

            2.7.4 c++11 ''                               ''       0 0
            2.7.4 c++11 ''                               'ESP-01' 0 0

            0.0.0 c++11 "$NMC_CUSTOM_GCC_DIR_OLDLIB_220" ''       1 0
            0.0.0 c++17 "$NMC_CUSTOM_GCC_DIR_OLDLIB_220" ''       1 0

            0.0.0 c++14 "$NMC_CUSTOM_GCC_DIR_NEWLIB_400" ''       1 1
            0.0.0 c++17 "$NMC_CUSTOM_GCC_DIR_NEWLIB_400" ''       1 1
        )
    fi

    local NMC_CHECK_ELEN=6

    ##### Loop trough the build check specification list
    for (( I=0; I<${#NMC_CHECK_LIST[@]}; I+=$NMC_CHECK_ELEN )); do
        ##### Extract the values
        local PKG_VER=${NMC_CHECK_LIST[    $I        ]}
        local CPP_VER=${NMC_CHECK_LIST[ (( $I + 1 )) ]}
        local GCC_DIR=${NMC_CHECK_LIST[ (( $I + 2 )) ]}
        local ALT_BRD=${NMC_CHECK_LIST[ (( $I + 3 )) ]}
        local IP6_ENF=${NMC_CHECK_LIST[ (( $I + 4 )) ]}
        local NEW_LIB=${NMC_CHECK_LIST[ (( $I + 5 )) ]}
        local ARD_DIR=$NMC_ROOT_ARDUINO_PACKAGES_DIR
        ##### Generate the shadow configuration file
        rm -f "$SHADOW_CONFIG_FILE"
        echo "PLATFORM                 = NodeMCU " >> "$SHADOW_CONFIG_FILE"
        echo "PACKAGE_VERSION          = $PKG_VER" >> "$SHADOW_CONFIG_FILE"
        echo "ROOT_ARDUINO_PACKAGE_DIR = $ARD_DIR" >> "$SHADOW_CONFIG_FILE"
        echo "CPP_VER                  = $CPP_VER" >> "$SHADOW_CONFIG_FILE"
        echo "CUSTOM_GCC_DIR           = $GCC_DIR" >> "$SHADOW_CONFIG_FILE"
        echo "LD_PATH                  =         " >> "$SHADOW_CONFIG_FILE"
        echo "ALT_BOARD                = $ALT_BRD" >> "$SHADOW_CONFIG_FILE"
        echo "LWIP_IPV6                = $IP6_ENF" >> "$SHADOW_CONFIG_FILE"
        echo "USE_NEWLIB               = $NEW_LIB" >> "$SHADOW_CONFIG_FILE"
        ##### Check build with the above configuration
        checkBuild
    done
}


#####
##### Perform the build checks
#####

##### Check if the first parameter is 'clean' or 'minimum'
echo
if ( [[ -n "$1" ]] && [[ "$1" != 'clean' ]] && [[ "$1" != 'minimum' ]] ) || [[ $# > 1 ]]; then
    clear
    echo -e "\nPerform eCxx build checks for all supported platforms with various configurations.\n"
    echo -e "Usage:\n\n$0 [clean|minimum]\n"
    exit -1
fi

if [[ "$1" == 'clean' ]]; then
    clear
    echo -e "\nDeleting file '$CBA_STATE_FILE' to restart the build checks.\n"
    rm -f $CBA_STATE_FILE
    exit -1
fi

if [[ "$1" == 'minimum' ]]; then
    CBA_DO_MIN_CHK=1
fi

##### Execute all build checks
SECONDS=0
execCheckX86
duration_x86=$SECONDS

SECONDS=0
execCheckAVR
duration_AVR=$SECONDS

SECONDS=0
execCheckNodeMCU
duration_NodeMCU=$SECONDS

##### Clean all
make -k allclean > /dev/null

##### Check if any build check is actually done
if [[ $CBA_CHECKCOUNT -eq 0 ]]; then
    if [[ CBA_DO_MIN_CHK -eq 1 ]]; then
        MIN_TEXT='minimum '
    fi
    clear
    echo -e "\nIt appears that all ${MIN_TEXT}build checks have been completed.\n"
    echo -e "Please delete the file '$CBA_STATE_FILE' (or execute '$0 clean') to restart the build checks.\n"
    exit -1
else
    echo
    echo
    TZ=UTC0 printf 'Total time for x86        check build = %(%H:%M:%S)T\n' $duration_x86
    TZ=UTC0 printf 'Total time for AVR & AVRX check build = %(%H:%M:%S)T\n' $duration_AVR
    TZ=UTC0 printf 'Total time for NodeMCU    check build = %(%H:%M:%S)T\n' $duration_NodeMCU
    echo
fi
