#!/usr/bin/env bash

# Automated setup script for Zephyr SDK distribution bundle archive
# for POSIX-compliant host operating systems (Linux and macOS)

# Utility functions
pushd()
{
  command pushd "$@" > /dev/null
}

popd()
{
  command popd "$@" > /dev/null
}

ask_yn()
{
  local reply

  while true; do
    read -r -p "$1 [y/n]? " reply

    case "${reply}" in
      Y* | y*)  return 0 ;;
      N* | n*)  return 1 ;;
      *)        echo "Invalid choice '${reply}'" ;;
    esac
  done
}

assert_rc()
{
  if [[ $? != 0 ]]; then
    echo "$1" 1>&2
    exit $2
  fi
}

# Check if command is available
check_command()
{
  which $1 &> /dev/null
  if [ $? != 0 ]; then
    echo "Zephyr SDK setup requires '$1' to be installed and available in the PATH."
    echo "Please install '$1 and run this script again."
    exit $2
  fi
}

# Check if the current installation is a full SDK with all GNU toolchains
check_full_gnu_sdk()
{
  for toolchain in ${gnu_toolchains[@]}; do
    if [ ! -d "gnu/${toolchain}" ]; then
      return 1
    fi
  done

  return 0
}

# Display script usage
usage()
{
  echo "Usage: $(basename $0) [-t <gnu_toolchain>] [-h] [-c]"
  echo
  echo "  -t <gnu_toolchain>   Install specified GNU toolchain"
  echo "     all               Install all GNU toolchains"
  echo "  -l                   Install LLVM toolchain"
  echo "  -h                   Install host tools"
  echo "  -o                   Create symbolic links matching the old SDK layout for old Zephyr (< 4.3) bisectability"
  echo "  -c                   Register Zephyr SDK CMake package"
  echo
  echo "Supported GNU Toolchains:"
  echo
  for toolchain in ${gnu_toolchains[@]}; do
    echo "  ${toolchain}"
  done
  echo
  echo "If no arguments are specified, the setup script runs in the interactive"
  echo "mode asking for user inputs."
}

# Ask for user inputs (interactive mode)
user_prompt()
{
  echo "** NOTE **"
  echo "You only need to run this script once after extracting the Zephyr SDK"
  echo "distribution bundle archive."
  echo

  # GNU Toolchains
  ask_yn "Install GNU toolchain"
  if [ $? == 0 ]; then
    do_gnu_toolchain="y"
    check_full_gnu_sdk
    if [ $? != 0 ]; then
      ask_yn "Install GNU toolchains for all targets"
      if [ $? == 0 ]; then
        inst_gnu_toolchains=(${gnu_toolchains[*]})
      else
        for toolchain in ${gnu_toolchains[@]}; do
          if [ ! -d "gnu/${toolchain}" ]; then
            ask_yn "Install '${toolchain}' GNU toolchain" && inst_gnu_toolchains+=("${toolchain}")
          fi
        done
      fi
    fi
  fi

  # LLVM Toolchain
  ask_yn "Install LLVM toolchain" && do_llvm_toolchain="y"

  # Host Tools
  ask_yn "Install host tools" && do_hosttools="y"

  # Environment Configurations
  ask_yn "Register Zephyr SDK CMake package" && do_cmake_pkg="y"

  ask_yn "Create symbolic links for old Zephyr bisectability" && do_old_zephyr="y"

  echo
}

# Entry point
pushd "$(dirname "${BASH_SOURCE[0]}")"

# Initialise GNU toolchain list
gnu_toolchains=$(<sdk_gnu_toolchains)
gnu_toolchains=("${gnu_toolchains[@]//$'\n'/ }")

# Initialise list of GNU toolchains to install
inst_gnu_toolchains=()

# Parse arguments
if [ $# == "0" ]; then
  interactive="y"
else
  while [ "$1" != "" ]; do
    case $1 in
      -t)
        shift
        if [[ "$1" = "all" ]]; then
          inst_gnu_toolchains=(${gnu_toolchains[*]})
        else
          if [[ " ${gnu_toolchains[*]} " =~ " $1 " ]]; then
            inst_gnu_toolchains+=("$1")
          else
            echo "ERROR: Unknown GNU toolchain '$1'"
            exit 2
          fi
        fi
        do_gnu_toolchain="y"
        ;;
      -l)
        do_llvm_toolchain="y"
        ;;
      -h)
        do_hosttools="y"
        ;;
      -c)
        do_cmake_pkg="y"
        ;;
      -o)
	do_old_zephyr="y"
	;;
      '-?')
        usage
        exit 0
        ;;
      *)
        echo "Invalid argument '$1'"
        echo
        usage
        exit 1
        ;;
    esac
    shift
  done
fi

# Read bundle version from file
version=$(<sdk_version)

# Resolve host type
case ${OSTYPE} in
  linux*)
    host="linux-${HOSTTYPE}"
    ;;
  darwin*)
    # Bash 3.x on AArch64 Darwin sets HOSTTYPE to 'arm64'
    if [ "${HOSTTYPE}" = "arm64" ]; then
      HOSTTYPE="aarch64"
    fi

    host="macos-${HOSTTYPE}"
    ;;
  *)
    echo "ERROR: Unsupported host operating system"
    exit 5
    ;;
esac

# Resolve release download base URI
dl_rel_base="https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${version}"
dl_gnu_toolchain_filename='toolchain_gnu_${host}_${toolchain}.tar.xz'
dl_llvm_toolchain_filename='toolchain_llvm_${host}.tar.xz'

# Print banner
echo "Zephyr SDK ${version} Setup"
echo

# Check dependencies
check_command cmake 90
[[ "${host}" =~ ^linux-.* ]] && check_command wget 91

# Resolve wget binary path
if [[ "${host}" =~ ^macos-.* ]]; then
  # Use wget from the SDK host tools on macOS
  wget=" \
    ${PWD}/hosttools/opt/wget/bin/wget \
     --ca-certificate ${PWD}/hosttools/opt/wget/etc/ssl/cert.pem \
    "
else
  # Use the system wget on Linux
  wget="wget"
fi

# Ask for user inputs if no argument is specified
if [ "${interactive}" = "y" ]; then
  interactive="y"
  user_prompt
fi

# Install GNU toolchains
if [ "${do_gnu_toolchain}" = "y" ]; then
  mkdir -p gnu
  pushd gnu

  for toolchain in ${inst_gnu_toolchains[@]}; do
    eval toolchain_filename="${dl_gnu_toolchain_filename}"
    toolchain_uri="${dl_rel_base}/${toolchain_filename}"

    # Skip if toolchain directory already exists
    if [ -d "${toolchain}" ]; then
      continue
    fi

    echo "Installing '${toolchain}' GNU toolchain ..."

    # Download toolchain archive
    ${wget} -q --show-progress -N -O "${toolchain_filename}" "${toolchain_uri}"
    if [ $? != 0 ]; then
      rm -f "${toolchain_filename}"
      echo "ERROR: GNU toolchain download failed"
      exit 20
    fi

    # Extract archive
    tar xf "${toolchain_filename}"
    assert_rc "ERROR: GNU toolchain archive extraction failed" 21

    # Remove archive
    rm -f "${toolchain_filename}"

    echo
  done
  popd
fi

# Install LLVM toolchain
if [ "${do_llvm_toolchain}" = "y" ] && [ ! -d "llvm" ]; then
  echo "Installing LLVM toolchain ..."

  eval toolchain_filename="${dl_llvm_toolchain_filename}"
  toolchain_uri="${dl_rel_base}/${toolchain_filename}"

  # Download toolchain archive
  ${wget} -q --show-progress -N -O "${toolchain_filename}" "${toolchain_uri}"
  if [ $? != 0 ]; then
    rm -f "${toolchain_filename}"
    echo "ERROR: LLVM toolchain download failed"
    exit 20
  fi

  # Extract archive
  tar xf "${toolchain_filename}"
  assert_rc "ERROR: LLVM toolchain archive extraction failed" 21

  # Remove archive
  rm -f "${toolchain_filename}"

  echo
fi

# Install host tools
if [ "${do_hosttools}" = "y" ]; then
  echo "Installing host tools ..."
  case ${host} in
    linux-*)
      pushd hosttools
      ./zephyr-sdk-${HOSTTYPE}-hosttools-standalone-0.10.sh -y -d . &> /dev/null
      assert_rc "ERROR: Host tools installation failed" 30
      popd
      ;;
    macos-*)
      echo "SKIPPED: macOS host tools are not available yet."
      ;;
  esac
  echo
fi

# Register Zephyr SDK CMake package
if [ "${do_cmake_pkg}" = "y" ]; then
  echo "Registering Zephyr SDK CMake package ..."
  cmake -P cmake/zephyr_sdk_export.cmake
  assert_rc "ERROR: CMake package registration failed" 40
  echo
fi

# Create links for old Zephyr versions
if [ "${do_old_zephyr}" = "y" ]; then
  echo "Creating links for old Zephyr bisectability ..."
  ln -s gnu/* . || assert_rc "ERROR: Creating toolchain links" 50
  pushd cmake/zephyr
  ln -s gnu/* . || assert_rc "ERROR: Creating cmake links" 50
  popd
  ln -s hosttools/* . || assert_rc "ERROR: Creating hosttools links" 50
  echo
fi

echo "All done."
if [ "${interactive}" = "y" ]; then
  read -n 1 -s -r -p "Press any key to exit ..."
  echo
fi

popd
