#!/bin/bash

set -eux

ARCH="$1"
shift

sudo=
if [ "${CI:-false}" = true ]; then
  sudo=sudo
fi

$sudo apt-get update -y
if [ "${CI:-false}" != true ]; then
  $sudo apt-get upgrade -y
fi

build_packages() {
  local pkgs=()
  local checkcacert=false

  if ! dpkg -L ca-certificates >/dev/null 2>/dev/null; then
    checkcacert=true
  fi

  for pkg in "${@}"; do
    if ! dpkg -L "${pkg}" >/dev/null 2>/dev/null; then
      pkgs+=("${pkg}")
    fi
  done
  if [ "${#pkgs[@]}" -gt 0 ]; then
    $sudo apt-get install --assume-yes --no-install-recommends "${pkgs[@]}"
    $sudo apt-mark auto "${pkgs[@]}"
  fi

  if $checkcacert && dpkg -L ca-certificates >/dev/null 2>/dev/null; then
    $sudo update-ca-certificates --fresh
  fi
}

common_install() {
  build_packages wget git file texinfo ca-certificates
  $sudo apt-get install -y build-essential "$@"
}

install_musl() {
  local MUSLDIR
  MUSLDIR="$(mktemp -d)"

  git clone --depth=1 https://github.com/richfelker/musl-cross-make.git "$MUSLDIR"
  cd "$MUSLDIR"
  cat <<EOF >config.mak
TARGET = $1
OUTPUT = /usr
GCC_VER = 11.2.0
BINUTILS_VER = 2.33.1
GCC_CONFIG += --enable-default-pie
EOF
  make -j"$(nproc)" install

  cd
  rm -rf "$MUSLDIR"

  local sysroot="/usr/$1"
  local arch="$2"
  local ld_arch="${arch//_/-}"
  local dst

  mkdir -p "${sysroot}/usr/lib"
  local src="${sysroot}/lib/libc.so"

  for dst in \
    "/lib/ld-musl-${arch}.so" \
    "/lib/ld-musl-${arch}.so.1" \
    "${sysroot}/lib/ld-musl-${arch}.so" \
    "${sysroot}/lib/ld-musl-${arch}.so.1" \
    "${sysroot}/usr/lib/ld.so" \
    "${sysroot}/usr/lib/ld.so.1" \
    "${sysroot}/usr/lib/libc.so" \
    "${sysroot}/usr/lib/libc.so.1" \
    "${sysroot}/lib/ld-linux-${ld_arch}.so" \
    "${sysroot}/lib/ld-linux-${ld_arch}.so.1"; do
    if [ -L "$dst" ] && [ ! -a "$dst" ]; then
      ln -sf "${src}" "${dst}"
    elif [ ! -f "$dst" ]; then
      ln -s "${src}" "${dst}"
    fi
  done

  rm -f "${sysroot}/lib/libstdc++.so"*

  echo 'GROUP ( libstdc++.a AS_NEEDED( -lgcc -lc -lm ) )' >"${sysroot}/lib/libstdc++.so.6.0.29"
  ln -s libstdc++.so.6.0.29 "${sysroot}/lib/libstdc++.so.6"
  ln -s libstdc++.so.6.0.29 "${sysroot}/lib/libstdc++.so"

  echo "${sysroot}/lib" >>"/etc/ld-musl-${arch}.path"
}

musl_linker() {
  cat <<EOF
#!/bin/bash

set -x
set -euo pipefail

if [ \$# -eq 0 ] || [ "\$(rustc -Vv | grep '^release:' | cut -d: -f2 | cut -d. -f2)" -ge "$2" ]; then
  exec "${1}gcc" "\$@"
else
  exec "${1}gcc" "\$@" -lgcc -static-libgcc
fi
EOF
}

compiler_rt_root() {
  local tempdir
  tempdir="$(mktemp -d)"
  git clone --depth=1 'https://github.com/llvm/llvm-project.git' "$tempdir"
  mkdir -p /opt
  mv "${tempdir}/compiler-rt" /opt/compiler-rt
  rm -rf "$tempdir"
}

install_android_ndk() {
  local VERSION="r25c"
  local FILENAME="android-ndk-${VERSION}-linux.zip"
  local URL="https://dl.google.com/android/repository/${FILENAME}"
  local ARCH="${1}"
  local TD

  common_install
  build_packages unzip

  TD="$(mktemp -d)"
  __TO_DELETE+=("$TD")

  pushd "$TD"

  wget -t3 "$URL"
  unzip -q "${FILENAME}"
  rm -f "${FILENAME}"

  local libver
  libver="$(head -1 android-ndk-${VERSION}/toolchains/llvm/prebuilt/linux-x86_64/AndroidVersion.txt)"

  pushd "android-ndk-${VERSION}/toolchains/llvm/prebuilt/linux-x86_64"

  case "${ARCH}" in
    x86_64)
      rm -f bin/armv7a-linux-androideabi* \
        bin/i686-linux-android* \
        bin/aarch64-linux-android*
      rm -rf lib64/clang/*/lib/linux/{aarch64,arm,i386}
      ;;
    x86)
      rm -f bin/armv7a-linux-androideabi* \
        bin/x86_64-linux-android* \
        bin/aarch64-linux-android*
      rm -rf lib64/clang/*/lib/linux/{aarch64,arm,x86_64}
      ;;
    arm)
      rm -f bin/i686-linux-androideabi* \
        bin/x86_64-linux-android* \
        bin/aarch64-linux-android*
      rm -rf lib64/clang/*/lib/linux/{aarch64,x86_64,i386}
      ;;
    aarch64)
      rm -f bin/armv7a-linux-androideabi* \
        bin/x86_64-linux-android* \
        bin/i686-linux-android*
      rm -rf lib64/clang/*/lib/linux/{arm,x86_64,i386}
      ;;
  esac

  local target
  case "${ARCH}" in
    x86_64)
      target='x86_64-linux-android'
      ;;
    x86)
      target='i686-linux-android'
      ;;
    arm)
      target='arm-linux-androideabi'
      ;;
    aarch64)
      target='aarch64-linux-android'
      ;;
    *)
      echo "invalid arch"
      return 1
      ;;
  esac

  local lib
  for lib in sysroot/usr/lib/*; do
    if [ "$(basename "${lib}")" != "${target}" ]; then
      rm -rf "${lib}"
    fi
  done

  popd

  $sudo mv "android-ndk-${VERSION}/toolchains/llvm/prebuilt/linux-x86_64" /android-ndk
  $sudo chown -R root:root /android-ndk

  popd
  rm -rf "$TD"

  local tool tool_src tool_dst suffix binpref gnusuff ndk_libdir libname libdir

  for tool in addr2line ar as nm objcopy objdump ranlib readelf readobj size \
    strings strip ld; do
    tool_src="/android-ndk/bin/llvm-${tool}"
    tool_dst="/android-ndk/bin/${target}-${tool}"
    if [[ ! -f "${tool_dst}" ]] && [[ -f "${tool_src}" ]]; then
      $sudo ln -s "${tool_src}" "${tool_dst}"
    elif [[ "${tool}" == ld ]] && [[ ! -f "${tool_dst}" ]]; then
      $sudo ln -s "/android-ndk/bin/${tool}" "${tool_dst}"
    fi
  done

  case "$target" in
    arm-linux-androideabi)
      binpref=armv7a-linux-androideabi
      ;;
    *)
      binpref="$target"
      ;;
  esac

  for suffix in '' ++; do
    if [ -z "$suffix" ]; then
      gnusuff=cc
    else
      gnusuff=++
    fi

    $sudo ln -s "/android-ndk/bin/${binpref}30-clang$suffix" \
      "/android-ndk/bin/${target}-clang$suffix"
    $sudo ln -s "/android-ndk/bin/${binpref}30-clang$suffix" \
      "/android-ndk/bin/${target}-g$gnusuff"
  done

  case "${ARCH}" in
    x86)
      libdir="/android-ndk/lib64/clang/${libver}/lib/linux/i386"
      ;;
    *)
      libdir="/android-ndk/lib64/clang/${libver}/lib/linux/${ARCH}"
      ;;
  esac
  if [ -f "${libdir}/libunwind.a" ]; then
    $sudo ln -s "${libdir}/libunwind.a" "${libdir}/libgcc.a"
  fi

  ndk_libdir="/android-ndk/sysroot/usr/lib/${target}/"

  if [[ -d "${ndk_libdir}" ]] && [[ -d "${ndk_libdir}/30" ]]; then
    for lib in "${ndk_libdir}/30"/*; do
      libname="$(basename "${lib}")"
      if [[ ! -e "${ndk_libdir}/${libname}" ]]; then
        $sudo ln -s "${lib}" "${ndk_libdir}/${libname}"
      fi
    done
  fi
}

setup_loongarch64() {
  cd

  common_install

  # install toolchain
  wget -t3 https://github.com/loongson/build-tools/releases/download/2022.09.06/loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz
  tar xf loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz
  mkdir -p /opt
  mv cross-tools /opt/loongarch64-unknown-linux-gnu
  rm -f loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz

  # install qemu
  wget -t3 https://github.com/loongson/build-tools/releases/download/2022.09.06/qemu-loongarch64
  chmod +x qemu-loongarch64
  mv qemu-loongarch64 /bin/qemu-loongarch64

  for tool in addr2line ar as c++ c++filt cpp elfedit g++ gcc gcc-ar gcc-nm \
    gcc-ranlib gcov gcov-dump gcov-tool gprof ld ld.bfd lto-dump nm objcopy \
    objdump ranlib readelf size strings strip; do
    ln -s "/opt/loongarch64-unknown-linux-gnu/bin/loongarch64-unknown-linux-gnu-$tool" \
      "/bin/loongarch64-linux-gnu-$tool"
  done

  compiler_rt_root
}

setup_x86_64() {
  common_install qemu-user gcc-x86-64-linux-gnu
  install_musl x86_64-linux-musl x86_64
  compiler_rt_root
  install_android_ndk x86_64
}

setup_x86() {
  common_install qemu-user gcc-i686-linux-gnu
  install_musl i686-linux-musl i686
  compiler_rt_root
  install_android_ndk x86
}

setup_arm() {
  common_install qemu-user gcc-arm-linux-gnueabi{,hf}
  install_musl arm-linux-musleabi arm
  install_musl arm-linux-musleabihf armhf
  musl_linker arm-linux-musleabi- 65 >/armv5te-musl.sh
  chmod +x /armv5te-musl.sh
  compiler_rt_root
  install_android_ndk arm
}

setup_aarch64() {
  common_install qemu-user gcc-aarch64-linux-gnu
  install_musl aarch64-linux-musl aarch64
  musl_linker aarch64-linux-musl- 48 >/aarch64-musl.sh
  chmod +x /aarch64-musl.sh
  compiler_rt_root
  install_android_ndk aarch64
}

setup_riscv64() {
  common_install qemu-user gcc-riscv64-linux-gnu
  install_musl riscv64-linux-musl riscv64
  compiler_rt_root
}

setup_powerpc() {
  common_install qemu-user gcc-powerpc-linux-gnu
  install_musl powerpc-linux-musl powerpc
  compiler_rt_root
}

setup_powerpc64() {
  common_install qemu-user gcc-powerpc64-linux-gnu \
    gcc-powerpc64le-linux-gnu
  install_musl powerpc64-linux-musl powerpc64
  install_musl powerpc64le-linux-musl powerpc64le
  compiler_rt_root
}

setup_mips() {
  common_install qemu-user gcc-mips-linux-gnu gcc-mipsel-linux-gnu
  install_musl mips-linux-musl mips
  install_musl mipsel-linux-musl mipsel
  compiler_rt_root
}

setup_mips64() {
  common_install qemu-user gcc-mips64-linux-gnuabi64 \
    gcc-mips64el-linux-gnuabi64
  install_musl mips64-linux-muslabi64 mips64
  install_musl mips64el-linux-muslabi64 mips64el
  musl_linker mips64-linux-muslabi64- 65 >/mips64-musl.sh
  chmod +x /mips64-musl.sh
  musl_linker mips64el-linux-muslabi64- 65 >/mips64el-musl.sh
  chmod +x /mips64el-musl.sh
  compiler_rt_root
}

setup_s390x() {
  common_install qemu-user gcc-s390x-linux-gnu
  install_musl s390x-linux-musl s390x
  compiler_rt_root
}

# TODO: riscv32

"setup_${ARCH}"
