#!/bin/bash

set -e
#set -x

DISTS_URL="http://archive.ubuntu.com/ubuntu/dists"

OPT_UPSTREAM_BASE_URL="https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git"
OPT_UPSTREAM_BRANCH="master"
OPT_LP_BASE_URL="https://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux-firmware"
OPT_LP_SERIES=
OPT_KVER=$(uname -r)
OPT_MODULES=()
OPT_VERBOSE=0

usage() {
  echo "Usage: $0 [-h] [-u <upsream url>] [-b <upstream branch>] [-l <launchpad url>] [-s <focal|groovy|...>] [-k <kernel ver>] [<modules>...]" 1>&2
}

die() {
  echo "Error: $*" >&2
  exit 1
}

verbose() {
  local level=$1
  shift

  if [[ ${OPT_VERBOSE} -ge ${level} ]]; then
    if [[ ${level} -eq 0 ]]; then
      echo "Log: $*";
    else
      echo "Log: $*" >&2;
    fi
  fi
}

missing() {
  verbose 0 "missing: $1"
}

neither() {
  verbose 1 "neither: $1"
}

found() {
  verbose 2 "found: $1"
}

while getopts ":hu:b:l:s:k:v" opt; do
  case "${opt}" in
  'u')
    OPT_UPSTREAM_BASE_URL=${OPTARG}
    ;;
  'b')
    OPT_UPSTREAM_BRANCH=${OPTARG}
    ;;
  'l')
    OPT_LP_BASE_URL=${OPTARG}
    ;;
  's')
    if ! wget --spider -q "${DISTS_URL}/${OPTARG}/Release"; then
      die "invalid series: ${OPTARG}";
    fi
    OPT_LP_SERIES=${OPTARG}
    ;;
  'k')
    if [[ ! -d "/lib/modules/${OPTARG}" ]]; then
      die "invalid kernel version: ${OPTARG}";
    fi
    OPT_KVER=${OPTARG}
    ;;
  'v')
    OPT_VERBOSE=$((OPT_VERBOSE + 1))
    ;;
  *)
    usage
    [[ "${opt}" = 'h' ]] && exit 0 || exit 1
    ;;
  esac
done
shift $((OPTIND - 1))
OPT_MODULES=( "$@" )

if [[ -z "${OPT_LP_SERIES}" ]]; then
  OPT_LP_SERIES=$(wget -q -O - "${DISTS_URL}/devel/Release" | awk '/^Suite:/ {print $2}')
fi
if [[ ${#OPT_MODULES} -eq 0 ]]; then
  OPT_MODULES=( $(find "/lib/modules/${OPT_KVER}" -type f -name \*.ko -printf "%f\n") )
  OPT_MODULES=( "${OPT_MODULES[@]%.ko}" )
fi

# Build file maps for specified series
fetch_map() {
  local base_url="$1"; shift
  local branch="$1"; shift
  local prefixes=("$@")

  verbose 3 "listing base url '${base_url}', branch '${branch}', prefix: ${prefixes[*]}..."

  local items=( $(for p in "${prefixes[@]}"; do echo "${base_url}/tree/${p}?h=${branch}"; done | \
    wget -q -O - -i - | \
    sed -n "/class='ls-mode'/ { s,^.*<a class='ls-\([^ ']\+\)[^>]\+href='\([^']\+\)'>.*,\1 \2,; p }" | \
    while read -r type href; do
      href=${href#*/tree/}
      href=${href%?h=*}
      echo "${type}:${href}"
    done) )

  prefixes=()
  for item in "${items[@]}"; do
    type=${item%%:*}
    path=${item#*:}
    case "${type}" in
    'dir') prefixes+=("${path}") ;;
    'blob') echo "${path}" ;;
    *)
      die "Unknown type: ${type}"
    esac
  done

  [[ ${#prefixes} -eq 0 ]] || fetch_map "${base_url}" "${branch}" "${prefixes[@]}"
}

upstream_files=( $(fetch_map "${OPT_UPSTREAM_BASE_URL}" "${OPT_UPSTREAM_BRANCH}" "") )
lp_files=( $(fetch_map "${OPT_LP_BASE_URL}" "${OPT_LP_SERIES}" "") )

upstream_has() {
  case " ${upstream_files[@]} " in
  *" $1 "*) return 0;;
  *) return 1;;
  esac
}

lp_has() {
  case " ${lp_files[@]} " in
  *" $1 "*) return 0;;
  *) return 1;;
  esac
}

for mod in "${OPT_MODULES[@]}"; do
  fws=$(modinfo -k "${OPT_KVER}" -F firmware "${mod}")
  [[ -n "$fws" ]] || continue;

  for fw in ${fws}; do
    if lp_has "${fw}"; then
      found "${fw}"
      continue
    fi

    if upstream_has "${fw}"; then
      missing "${fw}"
    elif [[ "${fw}" =~ ^iwlwifi- ]]; then
      max_rev=${fw%.ucode}
      prefix=${max_rev%-*}
      max_rev=${max_rev##*-}
      max_rev=$((max_rev - 1))
      while [[ ${max_rev} -gt 0 ]]; do
        fname="${prefix}-${max_rev}.ucode"
        if lp_has "${fname}"; then
          found "${fname}"
          break;
        elif upstream_has "${fname}"; then
          missing "${fname}"
          break;
        fi

        max_rev=$((max_rev - 1))
      done

      if [[ ${max_rev} -le 0 ]]; then
        neither "${fw}"
      fi
    else
      neither "${fw}"
    fi
  done
done
