#!/bin/bash
set -e

###########################################################################################################
## Created by MCUdude for compiling Optiboot flash from source                                           ##
## https://github.com/MCUdude/optiboot_flash                                                             ##
##                                                                                                       ##
## Execute ./makeall to build all variants of this bootloader.                                           ##
## Run $ chmod +x if this file isn't executable.                                                         ##
##                                                                                                       ##
## This is the file contains all all make procedures for all microcontrollers. You can modify the        ##
## mcu_and_params array if you want to add, remove or edit any of the settings. You can also modify      ##
## the clock_and_baud array if you need other F_CPUs or baudrates.                                       ##
##                                                                                                       ##
## Note that all microcontrollers with 64kB flash or more is built with bootloader EEPROM upload and     ##
## flash page copy functionality by default. Use SUPPORT_EEPROM=0 and COPY_FLASH_PAGES=0 (or just        ##
## remove them completely) if you want to disable these features.                                        ##
## Note that you can add EEPROM upload and flash page copy functionality to any target by adding         ##
## SUPPORT_EEPROM=1 and COPY_FLASH_PAGES=1 or simply, BIGBOOT=1.                                         ##
##                                                                                                       ##
## The table below shows the available precompiled bootloaders for the corresponding clock frequencies   ##
## and baud rates.                                                                                       ##
##                                                                                                       ##
## |             | 1000000 | 500000 | 460800 | 250000 | 230400 | 115200 | 57600 | 38400 | 19200 | 9600 | ##
## |-------------|---------|--------|--------|--------|--------|--------|-------|-------|-------|------| ##
## | 24 MHz      |  X      |  X     |        |  X     |  X     |  X     |  X    |  X    |  X    |      | ##
## | 22.1184 MHz |         |        |  X     |        |  X     |  X     |  X    |  X    |  X    |      | ##
## | 20 MHz      |         |  X     |        |  X     |        |  X     |       |       |  X    |      | ##
## | 18.4320 MHz |         |        |  X     |        |  X     |  X     |  X    |  X    |  X    |  X   | ##
## | 16 MHz      |  X      |  X     |        |  X     |        |  X     |       |  X    |  X    |  X   | ##
## | 14.7456 MHz |         |        |  X     |        |  X     |  X     |  X    |  X    |  X    |  X   | ## 
## | 12 MHz      |         |  X     |        |  X     |        |        |  X    |       |  X    |  X   | ##
## | 11.0592 MHz |         |        |  X     |        |  X     |  X     |  X    |  X    |  X    |  X   | ## 
## | 8 MHz       |  X      |  X     |        |  X     |        |  X     |  X    |  X    |  X    |  X   | ##
## | 7.3728 MHz  |         |        |  X     |        |  X     |  X     |  X    |  X    |  X    |  X   | ## 
## | 4 MHz       |         |  X     |        |  X     |        |        |       |       |  X    |  X   | ##
## | 3.6864 MHz  |         |        |  X     |        |  X     |  X     |  X    |  X    |  X    |  X   | ## 
## | 2 MHz       |         |        |        |  X     |        |        |       |       |  X    |  X   | ##
## | 1.8432 MHz  |         |        |        |        |  X     |  X     |  X    |  X    |  X    |  X   | ## 
## | 1 MHz       |         |        |        |        |        |        |       |       |       |  X   | ## 
##                                                                                                       ##
###########################################################################################################

declare -a mcu_and_params=(
"atmega8"     "LED=B5 LED_START_FLASHES=2 UART=0"

"atmega16"    "LED=B0 LED_START_FLASHES=2 UART=0"
"atmega16"    "LED=B7 LED_START_FLASHES=2 UART=0"

"atmega32"    "LED=B0 LED_START_FLASHES=2 UART=0"
"atmega32"    "LED=B7 LED_START_FLASHES=2 UART=0"

"at90can32"   "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"at90can32"   "LED=B5 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"

"atmega64"    "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega64"    "LED=B5 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"

"at90can64"   "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"at90can64"   "LED=B5 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"

"atmega88"    "LED=B5 LED_START_FLASHES=2 UART=0"
"atmega88p"   "LED=B5 LED_START_FLASHES=2 UART=0"
"atmega88pb"  "LED=B5 LED_START_FLASHES=2 UART=0"

"atmega128"   "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega128"   "LED=B5 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"

"at90can128"  "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"at90can128"  "LED=B5 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"

"atmega162"   "LED=B0 LED_START_FLASHES=0 UART=0"
"atmega162"   "LED=B0 LED_START_FLASHES=0 UART=1"

"atmega164a"  "LED=B0 LED_START_FLASHES=2 UART=0"
"atmega164a"  "LED=B0 LED_START_FLASHES=2 UART=1"
"atmega164a"  "LED=B7 LED_START_FLASHES=2 UART=0"
"atmega164a"  "LED=B7 LED_START_FLASHES=2 UART=1"
"atmega164p"  "LED=B0 LED_START_FLASHES=2 UART=0"
"atmega164p"  "LED=B0 LED_START_FLASHES=2 UART=1"
"atmega164p"  "LED=B7 LED_START_FLASHES=2 UART=0"
"atmega164p"  "LED=B7 LED_START_FLASHES=2 UART=1"

"atmega168"   "LED=B5 LED_START_FLASHES=2 UART=0"
"atmega168p"  "LED=B5 LED_START_FLASHES=2 UART=0"
"atmega168pb" "LED=B5 LED_START_FLASHES=2 UART=0"

"atmega169"   "LED=B5 LED_START_FLASHES=2 UART=0"
"atmega169p"  "LED=B5 LED_START_FLASHES=2 UART=0"

"atmega324a"  "LED=B0 LED_START_FLASHES=2 UART=0"
"atmega324a"  "LED=B0 LED_START_FLASHES=2 UART=1"
"atmega324a"  "LED=B7 LED_START_FLASHES=2 UART=0"
"atmega324a"  "LED=B7 LED_START_FLASHES=2 UART=1"
"atmega324p"  "LED=B0 LED_START_FLASHES=2 UART=0"
"atmega324p"  "LED=B0 LED_START_FLASHES=2 UART=1"
"atmega324p"  "LED=B7 LED_START_FLASHES=2 UART=0"
"atmega324p"  "LED=B7 LED_START_FLASHES=2 UART=1"
"atmega324pa" "LED=B0 LED_START_FLASHES=2 UART=0"
"atmega324pa" "LED=B0 LED_START_FLASHES=2 UART=1"
"atmega324pa" "LED=B7 LED_START_FLASHES=2 UART=0"
"atmega324pa" "LED=B7 LED_START_FLASHES=2 UART=1"
"atmega324pb" "LED=B0 LED_START_FLASHES=2 UART=0"
"atmega324pb" "LED=B0 LED_START_FLASHES=2 UART=1"
"atmega324pb" "LED=B0 LED_START_FLASHES=2 UART=2"
"atmega324pb" "LED=B7 LED_START_FLASHES=2 UART=0"
"atmega324pb" "LED=B7 LED_START_FLASHES=2 UART=1"
"atmega324pb" "LED=B7 LED_START_FLASHES=2 UART=2"

"atmega325"   "LED=B5 LED_START_FLASHES=2 UART=0"

"atmega328"   "LED=B5 LED_START_FLASHES=2 UART=0"
"atmega328p"  "LED=B5 LED_START_FLASHES=2 UART=0"
"atmega328pb" "LED=B5 LED_START_FLASHES=2 UART=0"
"atmega328pb" "LED=B5 LED_START_FLASHES=2 UART=1"

"atmega329"   "LED=B5 LED_START_FLASHES=2 UART=0"
"atmega329p"  "LED=B5 LED_START_FLASHES=2 UART=0"

"atmega640"   "LED=B7 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega640"   "LED=B7 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"
"atmega640"   "LED=B7 LED_START_FLASHES=2 UART=2 SUPPORT_EEPROM=1"
"atmega640"   "LED=B7 LED_START_FLASHES=2 UART=3 SUPPORT_EEPROM=1"

"atmega644a"  "LED=B0 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega644a"  "LED=B0 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"
"atmega644a"  "LED=B7 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"
"atmega644a"  "LED=B7 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega644p"  "LED=B0 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega644p"  "LED=B0 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"
"atmega644p"  "LED=B7 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega644p"  "LED=B7 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"

"atmega645"   "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"

"atmega649"   "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega649p"  "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"

"atmega1280"  "LED=B7 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega1280"  "LED=B7 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"
"atmega1280"  "LED=B7 LED_START_FLASHES=2 UART=2 SUPPORT_EEPROM=1"
"atmega1280"  "LED=B7 LED_START_FLASHES=2 UART=3 SUPPORT_EEPROM=1"

"atmega1281"  "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega1281"  "LED=B5 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"

"atmega1284"  "LED=B0 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega1284"  "LED=B0 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"
"atmega1284"  "LED=B7 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega1284"  "LED=B7 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"

"atmega1284p" "LED=B0 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega1284p" "LED=B0 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"
"atmega1284p" "LED=B7 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega1284p" "LED=B7 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"

"atmega2560"  "LED=B7 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega2560"  "LED=B7 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"
"atmega2560"  "LED=B7 LED_START_FLASHES=2 UART=2 SUPPORT_EEPROM=1"
"atmega2560"  "LED=B7 LED_START_FLASHES=2 UART=3 SUPPORT_EEPROM=1"

"atmega2561"  "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega2561"  "LED=B5 LED_START_FLASHES=2 UART=1 SUPPORT_EEPROM=1"

"atmega3250"  "LED=B5 LED_START_FLASHES=2 UART=0"

"atmega3290"  "LED=B5 LED_START_FLASHES=2 UART=0"
"atmega3290p" "LED=B5 LED_START_FLASHES=2 UART=0"

"atmega6450"  "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"

"atmega6490"  "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"
"atmega6490p" "LED=B5 LED_START_FLASHES=2 UART=0 SUPPORT_EEPROM=1"

"atmega8515"  "LED=B0 LED_START_FLASHES=2 UART=0"

"atmega8535"  "LED=B0 LED_START_FLASHES=2 UART=0"
"atmega8535"  "LED=B7 LED_START_FLASHES=2 UART=0"

)

declare -a clock_and_baud=(
"24000000L" "1000000"
"24000000L" "500000"
"24000000L" "250000"
"24000000L" "230400"
"24000000L" "115200"
"24000000L" "57600"
"24000000L" "38400"
"24000000L" "19200"
"22118400L" "460800"
"22118400L" "230400"
"22118400L" "115200"
"22118400L" "57600"
"22118400L" "38400"
"22118400L" "19200"
"20000000L" "500000"
"20000000L" "250000"
"20000000L" "115200"
"20000000L" "19200"
"18432000L" "460800"
"18432000L" "230400"
"18432000L" "115200"
"18432000L" "57600"
"18432000L" "38400"
"18432000L" "19200"
"18432000L" "9600"
"16000000L" "1000000"
"16000000L" "500000"
"16000000L" "250000"
"16000000L" "115200"
"16000000L" "38400"
"16000000L" "19200"
"16000000L" "9600"
"14745600L" "460800"
"14745600L" "230400"
"14745600L" "115200"
"14745600L" "57600"
"14745600L" "38400"
"14745600L" "19200"
"14745600L" "9600"
"12000000L" "500000"
"12000000L" "250000"
"12000000L" "115200"
"12000000L" "57600"
"12000000L" "19200"
"12000000L" "9600"
"11059200L" "460800"
"11059200L" "230400"
"11059200L" "115200"
"11059200L" "57600"
"11059200L" "38400"
"11059200L" "19200"
"11059200L" "9600"
"8000000L" "1000000" 
"8000000L" "500000" 
"8000000L" "250000" 
"8000000L" "115200" 
"8000000L" "57600"
"8000000L" "38400"
"8000000L" "19200"
"8000000L" "9600"
"7372800L" "460800"
"7372800L" "230400"
"7372800L" "115200"
"7372800L" "57600"
"7372800L" "38400"
"7372800L" "19200"
"7372800L" "9600"
"4000000L" "500000"
"4000000L" "250000"
"4000000L" "19200"
"4000000L" "9600"
"3686400L" "460800"
"3686400L" "230400"
"3686400L" "115200"
"3686400L" "57600"
"3686400L" "38400"
"3686400L" "19200"
"3686400L" "9600"
"2000000L" "250000"
"2000000L" "19200"
"2000000L" "9600"
"1843200L" "230400"
"1843200L" "115200"
"1843200L" "57600"
"1843200L" "38400"
"1843200L" "19200"
"1843200L" "9600"
"1000000L" "9600"
)

# Set this variable to true if you want the script to abort if compilation fails
abort_on_fail=false

# Clear terminal window
clear

# Welcome message
echo -e "\x1B[7m\n Welcome to MCUdude's optiboot_flash build script! \n\x1B[0m\033[1m"

# Should all files and folders be deleted?
while true; do
  read -p "Do you want to delete all previous files and folders (y/n)? " answer;
  case "$answer" in
    [Yy]* ) delete_all_files=true; break;;
    [Nn]* ) delete_all_files=false; break;;
    * ) ;;   
  esac;
done

# Should all existing files be replaced overwritten?
if [ $delete_all_files = false ]
then
  while true; do
    read -p "Do you want to replace/overwrite all existing hex files (y/n)? " answer;
    case "$answer" in
      [Yy]* ) overwrite_files=true; break;;
      [Nn]* ) overwrite_files=false; break;;
      * ) ;;   
    esac;
  done
fi

# Print warning
if [[ ($delete_all_files = true) || ($overwrite_files = true)]]
then
  echo -e "\x1B[0m\n\nYou're about to build a total of $((${#mcu_and_params[@]} * ${#clock_and_baud[@]} / 4)) hex files. This is probably going to take a while.\nAre you sure you want to do this?"
fi

# Press enter to start!
echo -e "\033[1m\n"
read -p "Press enter to run script!"
echo -e "\x1B[0m"

# Delete all files and folders if selected
if [ $delete_all_files = true ]
then
  echo -e "\n\nDeleting all previous generated files and empty folders"
  make clean_all
  find . -iname ".DS_Store" -type f -delete
  #find . -iname "*_build_info.txt" -type f -delete
  find . -iname "[1-9]*" -type d -empty -delete
  #find . -iname "atmega*" -type d -empty -delete
  echo -e "\n\n"
fi

# Loop through all MCUs and parameters
for((i=0; i < ${#mcu_and_params[@]}; i+=2));
do
  # Count how many targets we've built for
  TARGET_COUNTER=$(($TARGET_COUNTER + 1))
  
  # Create MCU folder if it doesn't exist already
  mkdir -p bootloaders/${mcu_and_params[$i]}
  
  # Print build header if one or more builds is ran
  build_info_header_is_printed="false"
  
  # Loop through all clock speeds and baud rates
  for((j=0; j < ${#clock_and_baud[@]}; j+=2));
  do
    # Count how many builds that have been ran
    BUILD_COUNTER=$(($BUILD_COUNTER + 1))
    echo -e "\x1B[7m\n\n Build $BUILD_COUNTER of $((${#mcu_and_params[@]} * ${#clock_and_baud[@]} / 4)) "
    echo -e "\x1B[0mTarget $TARGET_COUNTER of $((${#mcu_and_params[@]} / 2)) " 
    echo -e "\x1B[0mSub build $(((j+2) / 2)) of $((${#clock_and_baud[@]} / 2)) \x1B[7m"
    
     # Check if hex file already exists
     if [[ $overwrite_files = false && $delete_all_files = false ]]
     then
       bootloader_file=(bootloaders/${mcu_and_params[$i]}/${clock_and_baud[$j]}/optiboot_flash_${mcu_and_params[$i]}_$(echo ${mcu_and_params[$i+1]} | awk -F'=| ' '{printf("UART" $6)}')_${clock_and_baud[$j+1]}_${clock_and_baud[$j]}_$(echo ${mcu_and_params[$i+1]} | awk -F'=| ' '{printf($2)}')*)
       if [ -e "${bootloader_file}" ];
       then
         file_exists=true
         if [ $build_info_header_is_printed != "true" ]
         then
           build_info_header_is_printed="N/A"
         fi
       else
         file_exists=false
         if [ $build_info_header_is_printed == "N/A" ]
         then
           build_info_header_is_printed="false"
         fi  
       fi
     fi
     
    # Make sure the build info header is printed only once
    if [ $build_info_header_is_printed = "false" ]
    then
      # Create header and build info file once
      echo -e "Optiboot_flash build output for ${mcu_and_params[$i]}"\
      "\nhttps://github.com/MCUdude/optiboot_flash"\
      "\nBuild date and time: $(date +%d.%m.%Y\ %H:%M)"\
      "\nCompiler: $(make gcc_version)\n"\
      "\nF_CPU:\t\tUART NUMBER:\tLED PIN:\tLED FLASHES:\tEEPROM SUPPORT:\tFLASH PG COPY:\tDESIRED BAUD:\tACTUAL BAUD:\tERROR:\tCOMPILED SIZE:\tOUTPUT FILE:"\
      >> bootloaders/${mcu_and_params[$i]}/${mcu_and_params[$i]}_build_info.txt
      build_info_header_is_printed="true"
    fi
     
     
    # Skip, build or overwrite build
    if [[ $file_exists = false || $delete_all_files = true || $overwrite_files = true ]]
    then    
      # Print out current build command
      echo -e " make ${mcu_and_params[$i]} AVR_FREQ=${clock_and_baud[$j]} BAUD_RATE=${clock_and_baud[$j+1]} ${mcu_and_params[$i+1]} \x1B[0m"
    
      # Build hex and pipe output to stdout and temp.txt
      make -s ${mcu_and_params[$i]} AVR_FREQ=${clock_and_baud[$j]} BAUD_RATE=${clock_and_baud[$j+1]} ${mcu_and_params[$i+1]} | tee /dev/tty | tee temp.txt | \
         
      # Fill build info file
      awk -v F_CPU="${clock_and_baud[$j]}" '
      BEGIN { printf(F_CPU "\t") }
      {   
        if ($1 ~ "UART") printf($3 "\t\t")
        if ($4 ~ "LED_PIN") printf($6 "\t\t")
        if ($7 ~ "LED_FLASHES") printf($9 "\t\t")
        if ($10 ~ "SUPPORT_EEPROM" && $12 == "1") printf("Yes\t\t")
        if ($10 ~ "SUPPORT_EEPROM" && $12 != "1") printf("No\t\t")
        if (($12 ~ "COPY_FLASH_PAGES" && $14 == "1" ) || ($13 ~ "COPY_FLASH_PAGES" && $15 == "1")) printf("Yes\t\t")
        if (($12 ~ "COPY_FLASH_PAGES" && $14 != "1" ) || ($13 ~ "COPY_FLASH_PAGES" && $15 != "1")) printf("No\t\t")
        if ($4 ~ "Desired:") printf($5 "\t\t")
        if ($6 ~ "Real:") printf($7 "\t\t")
        if ($11 ~ "Difference:") printf($12 "%%\t")
        if ($1 ~ /^[0-9]+$/) printf($1 " bytes\t")
        if ($1 ~ "Output" && $2 ~ "file" && $3 "name:") 
        {
          sub(".*/", "", $4)
          printf($4 "\n")
        }
      } ' >> bootloaders/${mcu_and_params[$i]}/${mcu_and_params[$i]}_build_info.txt
      
      # Check if hex file exist and status message
      bootloader_file=(bootloaders/${mcu_and_params[$i]}/${clock_and_baud[$j]}/optiboot_flash_${mcu_and_params[$i]}_$(echo ${mcu_and_params[$i+1]} | awk -F'=| ' '{printf("UART" $6)}')_${clock_and_baud[$j+1]}_${clock_and_baud[$j]}*)
      if [ -e "${bootloader_file}" ];
      then
        echo -e "\n\n\x1B[7m COMPILATION SUCCESSFUL! \x1B[0m\n\n\n"
      else
        echo -e "\n\n\x1B[41m COMPILATION FAILED! \x1B[0m\n\n\n"
        if [ $abort_on_fail = true ]
        then
          exit 
        fi
      fi
     
    # Hex file already exist and we're not overwriting     
    else
      echo $bootloader_file
      echo -e "Hex file already exists. Skipping build.."
    fi      
  done
  
  # Add a few newlines at the end of the build info file if something was added
  if [ $build_info_header_is_printed != "N/A" ]
  then
    echo -e "\n" >> bootloaders/${mcu_and_params[$i]}/${mcu_and_params[$i]}_build_info.txt
    fi
done

# Delete temporary shell script files
find . -name "*.tmp.sh" -exec rm {} \;
find . -name "temp.txt" -exec rm {} \;

echo -e "\x1B[7m\n\n Done! \n\x1B[0m"
