#!/bin/bash
set -o pipefail

# TechNexion Camera Driver Modules Installation.
# This file is rendered from configs/driver-cicd.json.

APP_PRODUCT='JCO-6000-ORN-A'
APP_VERSION='JP62'
APP_DESCRIPTION='JCO-6000-ORN-A JP62'
EXTLINUX_CONF="/boot/extlinux/extlinux.conf"
BOOT_DTB_DIR="/boot/dtb"
REF_DTB_FILE='kernel_tegra234-p3737-0000+p3701-0004-nv.dtb'
MODULE_INSTALL_BASE='updates/drivers/media/i2c'
MAXIM_GMSL_INSTALL_DIR="${MODULE_INSTALL_BASE}/technexion/maxim-gmsl"
TEVS_INSTALL_DIR="${MODULE_INSTALL_BASE}/technexion/tevs"
VLS3_INSTALL_DIR="${MODULE_INSTALL_BASE}/technexion/vls3"
LEGACY_SERVICE_FILE="/etc/systemd/system/tn_cam.service"
SUPPORTS_PLATFORM=0
SUPPORTS_PHY=0
VALIDATES_L4T=0
LEGACY_MODULE_DIRS=(
  'kernel/drivers/media/i2c'
  'updates/drivers/media/i2c'
)
SUPPORTED_MODULES=(
  'vls-gm2'
)
MODULE_ALIASES=(
  '1|vls-gm2'
  'vls_gm2|vls-gm2'
)
DEFAULT_LABELS=(
  'vls-gm2|vls-gm2'
)
PHY_DEFAULT_LABELS=(
)
SCRIPT_PLATFORMS=(
)
PLATFORM_ALIASES=(
)
PLATFORM_MODULES=(
)
SUPPORTED_L4T_VERSIONS=(
  '36.4.3'
)
MODULE_FILES=(
  'technexion/tevs/tevs.ko|updates/drivers/media/i2c/technexion/tevs'
  'technexion/maxim-gmsl/max9296a.ko|updates/drivers/media/i2c/technexion/maxim-gmsl'
  'technexion/maxim-gmsl/max96717.ko|updates/drivers/media/i2c/technexion/maxim-gmsl'
)
BOOT_FILES=(
  'dtbs/tegra234-p3737-camera-jco-6000-orn-a-vls-gm2-overlay.dtbo|/boot/tegra234-p3737-camera-jco-6000-orn-a-vls-gm2-overlay.dtbo'
  'dtbs/tegra234-p3737-camera-jco-6000-orn-a-vls-gm2-fsync-overlay.dtbo|/boot/tegra234-p3737-camera-jco-6000-orn-a-vls-gm2-fsync-overlay.dtbo'
)
EXTLINUX_ENTRIES=(
  '|vls-gm2|vls-gm2|auto|OVERLAYS|/boot/tegra234-p3737-camera-jco-6000-orn-a-vls-gm2-overlay.dtbo|'
  '|vls-gm2|vls-gm2-fsync|auto|OVERLAYS|/boot/tegra234-p3737-camera-jco-6000-orn-a-vls-gm2-fsync-overlay.dtbo|'
)

ARG_PLATFORM=""
ARG_MODULE=""
ARG_AUTO_YES=0
ARG_REBOOT=""
ARG_PHY_MODE=single
INTERACTIVE=0
TS=""
KERNEL_VER=""
L4T_VER=""

print_usage() {
  local module_values
  module_values=$(IFS='|'; echo "${SUPPORTED_MODULES[*]}")
  if [ "$SUPPORTS_PLATFORM" = "1" ]; then
    local platform_values
    platform_values=$(IFS='|'; echo "${SCRIPT_PLATFORMS[*]}")
    cat <<USAGE
Usage: ./tn_install.sh [--platform ${platform_values}] [--module ${module_values}] [--reboot yes|no] [--yes]

Installs ${APP_DESCRIPTION} camera driver artifacts.
Without arguments, this script runs an interactive install flow.
With arguments, it runs non-interactively for APP-driven installation.
USAGE
  else
    cat <<USAGE
Usage: ./tn_install.sh [--module ${module_values}] [--reboot yes|no] [--yes]

Installs ${APP_DESCRIPTION} camera driver artifacts.
Without arguments, this script runs an interactive install flow.
With arguments, it runs non-interactively for APP-driven installation.
USAGE
  fi
}

die() {
  printf 'ERROR: %s\n' "$*" >&2
  exit 1
}

require_cmd() {
  command -v "$1" >/dev/null 2>&1 || die "missing required command: $1"
}

require_root() {
  [ "$(id -u)" -eq 0 ] || die "run this script as root through the APP runner, pkexec, or sudo"
}

require_arg_value() {
  case "${2:-}" in
    ""|--*)
      print_usage >&2
      die "missing value for $1"
      ;;
  esac
}

key_for_value() {
  printf '%s' "$1" | tr '[:upper:] ' '[:lower:]_' | sed 's/-/_/g'
}

normalize_yes_no() {
  case "$1" in
    y|Y|yes|YES|true|TRUE|1)
      printf 'y\n'
      ;;
    n|N|no|NO|false|FALSE|0)
      printf 'n\n'
      ;;
    *)
      die "cannot support this yes/no option: $1"
      ;;
  esac
}

normalize_phy_mode() {
  case "$1" in
    ""|single|SINGLE|Single|s|S|1)
      printf 'single\n'
      ;;
    dual|DUAL|Dual|d|D|2)
      printf 'dual\n'
      ;;
    *)
      die "cannot support this PHY mode: $1"
      ;;
  esac
}

require_file_nonempty() {
  local path=$1

  [ -f "$path" ] || die "missing file: $path"
  [ -s "$path" ] || die "zero-byte file: $path"
}

backup_if_exists() {
  local path=$1

  if [ -e "$path" ]; then
    cp -a "$path" "$path.bak-$TS"
  fi
}

install_file_atomic() {
  local source=$1
  local target=$2
  local mode=${3:-644}
  local target_dir=${target%/*}
  local target_tmp="$target_dir/.${target##*/}.tmp-$$"

  require_file_nonempty "$source"
  mkdir -p "$target_dir"
  rm -f "$target_tmp"
  install -m "$mode" "$source" "$target_tmp"
  require_file_nonempty "$target_tmp"
  sync -f "$target_tmp" 2>/dev/null || sync
  mv "$target_tmp" "$target"
  require_file_nonempty "$target"
  sync -f "$target" 2>/dev/null || sync
}

normalize_platform() {
  local value=$1
  local key
  local row
  local alias
  local canonical

  key=$(key_for_value "$value")
  for platform in "${SCRIPT_PLATFORMS[@]}"; do
    if [ "$key" = "$(key_for_value "$platform")" ]; then
      printf '%s\n' "$platform"
      return
    fi
  done

  for row in "${PLATFORM_ALIASES[@]}"; do
    IFS='|' read -r alias canonical <<< "$row"
    if [ "$key" = "$(key_for_value "$alias")" ]; then
      printf '%s\n' "$canonical"
      return
    fi
  done

  die "unsupported platform for ${APP_PRODUCT} ${APP_VERSION}: $value"
}

normalize_module() {
  local value=$1
  local key
  local row
  local alias
  local canonical
  local module

  key=$(key_for_value "$value")
  for module in "${SUPPORTED_MODULES[@]}"; do
    if [ "$key" = "$(key_for_value "$module")" ]; then
      printf '%s\n' "$module"
      return
    fi
  done

  for row in "${MODULE_ALIASES[@]}"; do
    IFS='|' read -r alias canonical <<< "$row"
    if [ "$key" = "$(key_for_value "$alias")" ]; then
      printf '%s\n' "$canonical"
      return
    fi
  done

  die "unsupported module for ${APP_PRODUCT} ${APP_VERSION}: $value"
}

module_allowed_for_platform() {
  local platform=$1
  local module=$2
  local row
  local entry_platform
  local entry_module

  if [ "$SUPPORTS_PLATFORM" != "1" ]; then
    return 0
  fi

  for row in "${PLATFORM_MODULES[@]}"; do
    IFS='|' read -r entry_platform entry_module <<< "$row"
    if [ "$entry_platform" = "$platform" ] && [ "$entry_module" = "$module" ]; then
      return 0
    fi
  done

  return 1
}

allowed_modules_for_current_platform() {
  local module
  local allowed=()

  for module in "${SUPPORTED_MODULES[@]}"; do
    if module_allowed_for_platform "$ARG_PLATFORM" "$module"; then
      allowed+=("$module")
    fi
  done

  printf '%s\n' "${allowed[@]}"
}

parse_args() {
  if [ "$#" -eq 0 ]; then
    INTERACTIVE=1
    return
  fi

  while [ "$#" -gt 0 ]; do
    case "$1" in
      --platform)
        if [ "$SUPPORTS_PLATFORM" != "1" ]; then
          print_usage >&2
          die "$1 is not accepted by this package installer"
        fi
        require_arg_value "$1" "${2:-}"
        ARG_PLATFORM=$(normalize_platform "$2")
        shift 2
        ;;
      --module)
        require_arg_value "$1" "${2:-}"
        ARG_MODULE=$(normalize_module "$2")
        shift 2
        ;;
      --reboot)
        require_arg_value "$1" "${2:-}"
        ARG_REBOOT=$(normalize_yes_no "$2")
        shift 2
        ;;
      --yes|-y)
        ARG_AUTO_YES=1
        shift
        ;;
      --help|-h)
        print_usage
        exit 0
        ;;
      --label|--phy)
        print_usage >&2
        die "$1 is not accepted by this package installer"
        ;;
      *)
        print_usage >&2
        die "unknown argument: $1"
        ;;
    esac
  done
}

prompt_backup_continue() {
  local choice

  cat <<WARNING
****** TechNexion Camera Driver Installation ******
This installation is easy to install TechNexion Camera Drivers for Nvidia
Jetson Development Kits. Before start to install camera driver,
You should BACKUP your image to avoid any file you lost while installing process.
WARNING
  read -r -p "Do you want to continue?[Y/n]" choice
  choice=${choice:-Y}
  case "$choice" in
    y|Y|yes|YES)
      return 0
      ;;
    *)
      echo "Installation aborted."
      exit 0
      ;;
  esac
}

select_platform_interactive() {
  local choice
  local index

  if [ "$SUPPORTS_PLATFORM" != "1" ]; then
    ARG_PLATFORM=""
    return
  fi

  if [ "${#SCRIPT_PLATFORMS[@]}" -eq 1 ]; then
    ARG_PLATFORM=$(normalize_platform "${SCRIPT_PLATFORMS[0]}")
    return
  fi

  echo "Target platform:"
  index=1
  for platform in "${SCRIPT_PLATFORMS[@]}"; do
    printf '  [%d] %s\n' "$index" "$platform"
    index=$((index + 1))
  done
  read -r -p "Select platform [1-${#SCRIPT_PLATFORMS[@]}] (default: 1): " choice
  choice=${choice:-1}
  case "$choice" in
    *[!0-9]*)
      ARG_PLATFORM=$(normalize_platform "$choice")
      ;;
    *)
      [ "$choice" -ge 1 ] && [ "$choice" -le "${#SCRIPT_PLATFORMS[@]}" ] ||
        die "platform selection is out of range: $choice"
      ARG_PLATFORM=$(normalize_platform "${SCRIPT_PLATFORMS[$((choice - 1))]}")
      ;;
  esac
}

select_module_interactive() {
  local choice
  local index
  local allowed=()
  local module

  while IFS= read -r module; do
    [ -n "$module" ] && allowed+=("$module")
  done < <(allowed_modules_for_current_platform)

  [ "${#allowed[@]}" -gt 0 ] || die "no modules are configured for platform: ${ARG_PLATFORM:-default}"

  if [ "${#allowed[@]}" -eq 1 ]; then
    ARG_MODULE=$(normalize_module "${allowed[0]}")
    return
  fi

  echo "Camera module:"
  index=1
  for module in "${allowed[@]}"; do
    printf '  [%d] %s\n' "$index" "$module"
    index=$((index + 1))
  done
  read -r -p "Select module [1-${#allowed[@]}] (default: 1): " choice
  choice=${choice:-1}
  case "$choice" in
    *[!0-9]*)
      ARG_MODULE=$(normalize_module "$choice")
      ;;
    *)
      [ "$choice" -ge 1 ] && [ "$choice" -le "${#allowed[@]}" ] ||
        die "module selection is out of range: $choice"
      ARG_MODULE=$(normalize_module "${allowed[$((choice - 1))]}")
      ;;
  esac
}

prepare_install_choices() {
  local choice
  local allowed=()
  local module

  if [ "$SUPPORTS_PLATFORM" = "1" ]; then
    if [ -z "$ARG_PLATFORM" ]; then
      if [ "$INTERACTIVE" = "1" ]; then
        select_platform_interactive
      else
        ARG_PLATFORM=$(normalize_platform "${SCRIPT_PLATFORMS[0]}")
      fi
    fi
  fi

  while IFS= read -r module; do
    [ -n "$module" ] && allowed+=("$module")
  done < <(allowed_modules_for_current_platform)

  if [ -z "$ARG_MODULE" ]; then
    if [ "$INTERACTIVE" = "1" ]; then
      select_module_interactive
    elif [ "$ARG_AUTO_YES" = "1" ] && [ "${#allowed[@]}" -eq 1 ]; then
      ARG_MODULE=$(normalize_module "${allowed[0]}")
    else
      print_usage >&2
      die "--module is required for non-interactive installation"
    fi
  fi

  if ! module_allowed_for_platform "$ARG_PLATFORM" "$ARG_MODULE"; then
    die "module ${ARG_MODULE} is not supported on platform ${ARG_PLATFORM}"
  fi

  if [ "$SUPPORTS_PHY" = "1" ]; then
    if [ "$INTERACTIVE" = "1" ]; then
      read -r -p "Camera PHY mode [single/dual] (default: single): " choice
      ARG_PHY_MODE=$(normalize_phy_mode "${choice:-single}")
    else
      ARG_PHY_MODE="single"
    fi
  fi

  if [ "$INTERACTIVE" = "1" ]; then
    read -r -p "Install ${APP_DESCRIPTION} for ${ARG_MODULE}? [Y/n] " choice
    choice=${choice:-y}
    choice=$(normalize_yes_no "$choice")
    if [ "$choice" != "y" ]; then
      echo "Installation aborted."
      exit 0
    fi
    read -r -p "Reboot after installation? [y/N] " choice
    ARG_REBOOT=$(normalize_yes_no "${choice:-n}")
  elif [ -z "$ARG_REBOOT" ]; then
    ARG_REBOOT="n"
  fi
}

default_label_for_module() {
  local module=$1
  local row
  local row_module
  local row_phy
  local row_label

  if [ "$SUPPORTS_PHY" = "1" ]; then
    for row in "${PHY_DEFAULT_LABELS[@]}"; do
      IFS='|' read -r row_module row_phy row_label <<< "$row"
      if [ "$row_module" = "$module" ] && [ "$row_phy" = "$ARG_PHY_MODE" ]; then
        printf '%s\n' "$row_label"
        return
      fi
    done
  fi

  for row in "${DEFAULT_LABELS[@]}"; do
    IFS='|' read -r row_module row_label <<< "$row"
    if [ "$row_module" = "$module" ]; then
      printf '%s\n' "$row_label"
      return
    fi
  done

  die "missing default extlinux label for module: $module"
}

validate_l4t_version() {
  local supported

  if [ "$VALIDATES_L4T" != "1" ]; then
    return
  fi

  L4T_VER=$(dpkg-query -W -f='${Version}' nvidia-l4t-core 2>/dev/null | cut -d- -f1)
  [ -n "$L4T_VER" ] || die "cannot detect NVIDIA L4T version from nvidia-l4t-core"

  for supported in "${SUPPORTED_L4T_VERSIONS[@]}"; do
    case "$L4T_VER" in
      "$supported"|"$supported".*)
        return
        ;;
    esac
  done

  die "cannot support this L4T version: $L4T_VER"
}

validate_package_files() {
  local row
  local source
  local ignored

  for row in "${MODULE_FILES[@]}"; do
    IFS='|' read -r source ignored <<< "$row"
    require_file_nonempty "$source"
  done

  for row in "${BOOT_FILES[@]}"; do
    IFS='|' read -r source ignored <<< "$row"
    require_file_nonempty "$source"
  done
}

resolve_boot_path() {
  local boot_path=$1

  boot_path=${boot_path%\"}
  boot_path=${boot_path#\"}

  case "$boot_path" in
    /*)
      printf '%s\n' "$boot_path"
      ;;
    *)
      printf '/boot/%s\n' "${boot_path#./}"
      ;;
  esac
}

label_exists() {
  local label=$1
  local conf=${2:-$EXTLINUX_CONF}

  awk -v label="$label" '
    toupper($1) == "LABEL" && $2 == label { found = 1 }
    END { exit found ? 0 : 1 }
  ' "$conf"
}

get_default_label() {
  local conf=${1:-$EXTLINUX_CONF}

  awk '
    /^[[:space:]]*(#|$)/ { next }
    toupper($1) == "DEFAULT" { print $2; found = 1; exit }
    END { exit found ? 0 : 1 }
  ' "$conf" || die "cannot find DEFAULT label in $conf"
}

get_label_fdt() {
  local label=$1
  local conf=${2:-$EXTLINUX_CONF}

  awk -v label="$label" '
    toupper($1) == "LABEL" {
      if (capture) {
        exit
      }
      capture = ($2 == label)
    }
    capture && toupper($1) == "FDT" {
      print $2
      found = 1
      exit
    }
    END { exit found ? 0 : 1 }
  ' "$conf" || true
}

find_base_label() {
  local default_label

  if label_exists primary; then
    printf 'primary\n'
    return
  fi

  default_label=$(get_default_label || true)
  if [ -n "$default_label" ] && [[ "$default_label" != tn-* ]] && label_exists "$default_label"; then
    printf '%s\n' "$default_label"
    return
  fi

  awk '
    toupper($1) == "LABEL" && $2 !~ /^tn-/ {
      print $2
      found = 1
      exit
    }
    END { exit found ? 0 : 1 }
  ' "$EXTLINUX_CONF" || die "cannot find a non-TN extlinux label to clone"
}

first_boot_dtb() {
  [ -d "$BOOT_DTB_DIR" ] || die "missing boot DTB directory: $BOOT_DTB_DIR"
  find "$BOOT_DTB_DIR" -maxdepth 1 \( -type f -o -type l \) -printf '%f\n' | sort | sed -n '1p'
}

resolve_auto_fdt() {
  local base_label=$1
  local label_fdt
  local resolved
  local first_dtb

  if [ -n "$REF_DTB_FILE" ] && [ -s "$BOOT_DTB_DIR/$REF_DTB_FILE" ]; then
    printf '%s/%s\n' "$BOOT_DTB_DIR" "$REF_DTB_FILE"
    return
  fi

  label_fdt=$(get_label_fdt "$base_label")
  if [ -n "$label_fdt" ]; then
    resolved=$(resolve_boot_path "$label_fdt")
    if [ -s "$resolved" ]; then
      printf '%s\n' "$label_fdt"
      return
    fi
  fi

  first_dtb=$(first_boot_dtb)
  [ -n "$first_dtb" ] || die "no DTB files found in $BOOT_DTB_DIR"
  require_file_nonempty "$BOOT_DTB_DIR/$first_dtb"
  printf '%s/%s\n' "$BOOT_DTB_DIR" "$first_dtb"
}

resolve_entry_fdt() {
  local requested=$1
  local base_label=$2
  local resolved

  case "$requested" in
    ""|auto)
      resolve_auto_fdt "$base_label"
      ;;
    /*)
      resolved=$(resolve_boot_path "$requested")
      require_file_nonempty "$resolved"
      printf '%s\n' "$requested"
      ;;
    *)
      resolved="$BOOT_DTB_DIR/$requested"
      require_file_nonempty "$resolved"
      printf '%s\n' "$resolved"
      ;;
  esac
}

remove_legacy_tncam_service() {
  if [ -f "$LEGACY_SERVICE_FILE" ]; then
    echo "Remove legacy TN-CAM service..."
    if command -v systemctl >/dev/null 2>&1; then
      systemctl stop tn_cam.service 2>/dev/null || true
      systemctl disable tn_cam.service 2>/dev/null || true
    fi
    rm -f "$LEGACY_SERVICE_FILE"
    if command -v systemctl >/dev/null 2>&1; then
      systemctl daemon-reload
    fi
  fi
}

remove_legacy_tncam_modules() {
  local row
  local source
  local ignored
  local module_name
  local legacy_dir
  local target
  local seen=" "

  for row in "${MODULE_FILES[@]}"; do
    IFS='|' read -r source ignored <<< "$row"
    module_name=${source##*/}
    case "$seen" in
      *" $module_name "*)
        continue
        ;;
    esac
    seen="$seen$module_name "
    for legacy_dir in "${LEGACY_MODULE_DIRS[@]}"; do
      target="/lib/modules/$KERNEL_VER/$legacy_dir/$module_name"
      if [ -f "$target" ]; then
        echo "Remove legacy TN-CAM module: $target"
        rm -f "$target"
      fi
    done
  done
}

remove_extlinux_label() {
  local label=$1
  local source_conf=$2

  awk -v label="$label" '
    toupper($1) == "LABEL" {
      skip = ($2 == label)
    }
    !skip { print }
  ' "$source_conf"
}

cleanup_tn_extlinux_labels() {
  local tmp_conf

  tmp_conf=$(mktemp)
  awk '
    function flush_blanks() {
      for (i = 1; i <= blank_count; i++) {
        print ""
      }
      blank_count = 0
    }

    /^[[:space:]]*$/ {
      if (skip) {
        next
      }
      blank_count++
      next
    }

    /^[[:space:]]*LABEL[[:space:]]+tn-/ {
      blank_count = 0
      skip = 1
      next
    }

    /^[[:space:]]*LABEL[[:space:]]+/ {
      skip = 0
    }

    {
      if (!skip) {
        flush_blanks()
        print
      }
    }

    END {
      if (!skip) {
        flush_blanks()
      }
    }
  ' "$EXTLINUX_CONF" > "$tmp_conf"

  require_file_nonempty "$tmp_conf"
  install_file_atomic "$tmp_conf" "$EXTLINUX_CONF"
  rm -f "$tmp_conf"
}

write_extlinux_entry_block() {
  local base_label=$1
  local new_label=$2
  local fdt_path=$3
  local overlay_keyword=$4
  local overlay_path=$5
  local tmp_block

  tmp_block=$(mktemp)
  awk \
    -v base="$base_label" \
    -v new="$new_label" \
    -v fdt="$fdt_path" \
    -v overlay_keyword="$overlay_keyword" \
    -v overlay="$overlay_path" '
    function emit_insertions() {
      if (!inserted_fdt) {
        print "      FDT " fdt
        inserted_fdt = 1
      }
      if (overlay != "" && !inserted_overlay) {
        print "      " overlay_keyword " " overlay
        inserted_overlay = 1
      }
    }
    function emit_block(    i, line, trimmed, fields, key) {
      inserted_fdt = 0
      inserted_overlay = 0
      for (i = 1; i <= n; i++) {
        line = lines[i]
        trimmed = line
        sub(/^[ \t]+/, "", trimmed)
        split(trimmed, fields, /[ \t]+/)
        key = toupper(fields[1])

        if (key == "LABEL") {
          sub(/LABEL[ \t]+[^ \t]+/, "LABEL " new, line)
        } else if (key == "MENU" && toupper(fields[2]) == "LABEL") {
          line = "      MENU LABEL " new
        } else if (key == "FDT" || key == "OVERLAY" || key == "OVERLAYS" || key == "FDTOVERLAYS") {
          continue
        } else if (key == "APPEND") {
          emit_insertions()
        }
        print line
      }
      emit_insertions()
    }
    toupper($1) == "LABEL" {
      if (capture) {
        exit
      }
      capture = ($2 == base)
    }
    capture {
      lines[++n] = $0
      line = $0
      sub(/^[ \t]+/, "", line)
      split(line, fields, /[ \t]+/)
      if (toupper(fields[1]) == "APPEND") {
        exit
      }
    }
    END {
      if (n == 0) {
        exit 33
      }
      emit_block()
    }
  ' "$EXTLINUX_CONF" > "$tmp_block" || {
    rm -f "$tmp_block"
    die "cannot clone extlinux label: $base_label"
  }

  printf '%s\n' "$tmp_block"
}

validate_extlinux_label() {
  local label=$1
  local conf=$2

  label_exists "$label" "$conf" || die "generated extlinux config is missing label: $label"
}

install_extlinux_entry() {
  local label=$1
  local requested_fdt=$2
  local overlay_keyword=$3
  local overlay_path=$4
  local base_label
  local fdt_path
  local tmp_block
  local tmp_conf

  base_label=$(find_base_label)
  fdt_path=$(resolve_entry_fdt "$requested_fdt" "$base_label")

  if [ -n "$overlay_path" ]; then
    require_file_nonempty "$(resolve_boot_path "$overlay_path")"
  fi

  tmp_block=$(write_extlinux_entry_block "$base_label" "tn-$label" "$fdt_path" "$overlay_keyword" "$overlay_path")
  tmp_conf=$(mktemp)
  {
    remove_extlinux_label "tn-$label" "$EXTLINUX_CONF"
    printf '\n\n'
    cat "$tmp_block"
  } > "$tmp_conf"

  validate_extlinux_label "tn-$label" "$tmp_conf"
  install_file_atomic "$tmp_conf" "$EXTLINUX_CONF"
  rm -f "$tmp_block" "$tmp_conf"
}

set_default_label() {
  local label=$1
  local tmp_conf

  label="tn-$label"
  label_exists "$label" || die "refusing to set DEFAULT to missing extlinux label: $label"
  tmp_conf=$(mktemp)
  if grep -Eq '^[[:space:]]*DEFAULT[[:space:]]+' "$EXTLINUX_CONF"; then
    sed "s/^[[:space:]]*DEFAULT[[:space:]].*/DEFAULT $label/" "$EXTLINUX_CONF" > "$tmp_conf"
  else
    {
      printf 'DEFAULT %s\n' "$label"
      cat "$EXTLINUX_CONF"
    } > "$tmp_conf"
  fi
  install_file_atomic "$tmp_conf" "$EXTLINUX_CONF"
  rm -f "$tmp_conf"
}

install_modules() {
  local row
  local source
  local target_subdir
  local target

  for row in "${MODULE_FILES[@]}"; do
    IFS='|' read -r source target_subdir <<< "$row"
    target="/lib/modules/$KERNEL_VER/$target_subdir/${source##*/}"
    backup_if_exists "$target"
    install_file_atomic "$source" "$target"
  done

  if ((${#MODULE_FILES[@]} > 0)); then
    depmod -a "$KERNEL_VER"
  fi
}

install_boot_files() {
  local row
  local source
  local target

  for row in "${BOOT_FILES[@]}"; do
    IFS='|' read -r source target <<< "$row"
    backup_if_exists "$target"
    install_file_atomic "$source" "$target"
  done
}

install_extlinux_entries() {
  local row
  local entry_platform
  local entry_module
  local label
  local fdt
  local overlay_keyword
  local overlay
  local phy_mode
  local installed=0

  require_file_nonempty "$EXTLINUX_CONF"
  backup_if_exists "$EXTLINUX_CONF"
  cleanup_tn_extlinux_labels

  for row in "${EXTLINUX_ENTRIES[@]}"; do
    IFS='|' read -r entry_platform entry_module label fdt overlay_keyword overlay phy_mode <<< "$row"
    if [ "$SUPPORTS_PLATFORM" = "1" ] && [ "$entry_platform" != "$ARG_PLATFORM" ]; then
      continue
    fi
    [ "$entry_module" = "$ARG_MODULE" ] || continue
    if [ -n "${phy_mode:-}" ] && [ "$phy_mode" != "$ARG_PHY_MODE" ]; then
      continue
    fi
    install_extlinux_entry "$label" "$fdt" "$overlay_keyword" "$overlay"
    installed=$((installed + 1))
  done

  [ "$installed" -gt 0 ] || die "no extlinux entries configured for module: $ARG_MODULE"
  set_default_label "$(default_label_for_module "$ARG_MODULE")"
}

show_summary() {
  local row
  local source
  local target_subdir
  local target
  local installed_files=()

  for row in "${MODULE_FILES[@]}"; do
    IFS='|' read -r source target_subdir <<< "$row"
    installed_files+=("/lib/modules/$KERNEL_VER/$target_subdir/${source##*/}")
  done

  for row in "${BOOT_FILES[@]}"; do
    IFS='|' read -r source target <<< "$row"
    installed_files+=("$target")
  done

  echo "Installed artifacts:"
  sha256sum "${installed_files[@]}"
  echo "Extlinux camera entries:"
  awk '/^[[:space:]]*DEFAULT/ || /^[[:space:]]*LABEL/ || /FDT/ || /OVERLAYS/ || /FDTOVERLAYS/ { print }' "$EXTLINUX_CONF"
}

main() {
  local script_dir

  parse_args "$@"

  script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
  cd "$script_dir" || die "cannot enter installer directory: $script_dir"

  if [ "$INTERACTIVE" = "1" ]; then
    prompt_backup_continue
  else
    echo "Running non-interactive installation; prompts are skipped."
  fi

  prepare_install_choices

  for cmd in awk cat chmod cp cut date depmod find grep id install mkdir mktemp mv rm sed sha256sum sort sync tr uname; do
    require_cmd "$cmd"
  done
  if [ "$VALIDATES_L4T" = "1" ]; then
    require_cmd dpkg-query
  fi

  validate_package_files
  validate_l4t_version
  require_root

  TS=$(date +%Y%m%d-%H%M%S)
  KERNEL_VER=$(uname -r)

  echo "Installing ${APP_DESCRIPTION}"
  if [ "$SUPPORTS_PLATFORM" = "1" ]; then
    echo "Selected platform: $ARG_PLATFORM"
  fi
  echo "Selected module: $ARG_MODULE"
  if [ "$SUPPORTS_PHY" = "1" ]; then
    echo "Selected PHY mode: $ARG_PHY_MODE"
  fi

  remove_legacy_tncam_modules
  remove_legacy_tncam_service
  install_modules
  install_boot_files
  install_extlinux_entries
  show_summary

  echo "Finish Camera Driver Installation."
  if [ "$ARG_REBOOT" = "y" ]; then
    echo "Rebooting..."
    reboot
  else
    echo "Reboot the device when the user is ready."
  fi
}

main "$@"
