#!/bin/bash

###############################
# VARS
###############################

# shellcheck source=/.github/workflows/mock/VERSION
source "/etc/VERSION"
dsm_version="$productversion $buildnumber-$smallfixnumber"
repo_base_url="https://dsm.ainas.cc:88/Script/VideoStation-FFMPEG-Patcher-3.0"
action="patch"
branch="main"
ffmpegversion=""
wrappers=(
  "ffmpeg"
  "gst-launch-1.0"
  "gst-inspect-1.0"
)

vs_base_path=/var/packages/VideoStation
vs_path="$vs_base_path/target"
libsynovte_path="$vs_path/lib/libsynovte.so"
cp_base_path=/var/packages/CodecPack
cp_path="$cp_base_path/target"
cp_bin_path="$cp_path/bin"
cp_to_patch=(
  "ffmpeg41:ffmpeg"
  "ffmpeg27:ffmpeg"
  "ffmpeg33:ffmpeg"
  "gst-launch-1.0:gst-launch-1.0"
  "gst-inspect-1.0:gst-inspect-1.0"
)

gstreamer_plugins=(
  "libgstdtsdec"
  "libgstlibav"
)
gstreamer_libs=(
  "libavcodec-ffmpeg.so.56"
  "libavformat-ffmpeg.so.56"
  "libavutil-ffmpeg.so.54"
  "libbluray.so.1"
  "libdca.so.0"
  "libgme.so.0"
  "libgnutls-deb0.so.28"
  "libgsm.so.1"
  "libhogweed.so.4"
  "libmodplug.so.1"
  "libnettle.so.6"
  "libnuma.so.1"
  "libopenjpeg.so.5"
  "libopenjpeg_JPWL.so.5"
  "libopus.so.0"
  "liborc-0.4.so.0"
  "libp11-kit.so.0"
  "libpng12.so.0"
  "librtmp.so.1"
  "libschroedinger-1.0.so.0"
  "libshine.so.3"
  "libsoxr.so.0"
  "libspeex.so.1"
  "libssh-gcrypt.so.4"
  "libssh-gcrypt_threads.so.4"
  "libswresample-ffmpeg.so.1"
  "libtasn1.so.6"
  "libtheora.so.0"
  "libtheoradec.so.1"
  "libtheoraenc.so.1"
  "libtwolame.so.0"
  "libva.so.1"
  "libvpx.so.2"
  "libvpx.so.2.0"
  "libwavpack.so.1"
  "libwebp.so.5"
  "libx264.so.146"
  "libx265.so.59"
  "libxvidcore.so.4"
  "libzvbi.so.0"
  "libzvbi-chains.so.0"
  "dri/dummy_drv_video.so"
  "x264-10bit/libx264.so.146"
  "x265-10bit/libx265.so.59"
)

###############################
# UTILS
###############################

log() {
  printf "\e[0;37m[%s] \e[0m[%s] %b" "$(date '+%Y-%m-%d %H:%M:%S')" "$1" "$2$3"
}
info() {
  log "INFO" "\e[0m" "$1\n"
}
error() {
  log "ERROR" "\e[0;31m" "$1\n"
}
success() {
  log "SUCCESS" "\e[0;32m" "$1\n"
}

welcome_motd() {
  info "ffmpeg-patcher"

  download "motd" "$repo_base_url/$branch/motd.txt" /tmp/tmp.wget
  log "Message of the day" "\033[1;33m" "\n\n$(cat /tmp/tmp.wget)\n\n"

  sleep 3
}

root_check() {
  if [[ "$EUID" -ne 0 ]]; then
    error "This tool needs root access (please run 'sudo -i' before proceeding)."
    exit 1
  fi
}

check_dependencies() {
  missingDeps=0

  for dependency in "${dependencies[@]}"; do
    if [[ ! -d "/var/packages/$dependency" ]]; then
      error "Missing $dependency package, please install it and re-run the patcher."
      missingDeps=1
    fi
  done

  if [[ $missingDeps -eq 1 ]]; then
    exit 1
  fi
}

restart_packages() {
  if [[ -d $cp_bin_path ]]; then
    if [[ -d "$cp_base_path/etc/gstreamer-1.0" ]]; then
      info "Clearing CodecPack gstreamer cache..."
      rm -f "$cp_base_path/etc/gstreamer-1.0/registry.*.bin"
    fi

    info "Restarting CodecPack..."
    synopkg restart CodecPack
  fi

  if [[ -d "$vs_base_path/etc/gstreamer-1.0" ]]; then
    info "Clearing VideoStation gstreamer cache..."
    rm -f "$vs_base_path/etc/gstreamer-1.0/registry.*.bin"
  fi

  info "Restarting VideoStation..."
  synopkg restart VideoStation
}

clean() {
  info "Cleaning orphan files..."

  rm -f /tmp/tmp.wget
  rm -f /tmp/ffmpeg.log
  rm -f /tmp/ffmpeg*.stderr
  rm -f /tmp/ffmpeg*.stderr.prev
  rm -f /tmp/gstreamer.log
  rm -f /tmp/gst*.stderr
  rm -f /tmp/gst*.stderr.prev
}

download() {
  log "INFO" "\e[0m" "Downloading $1... "

  wget -q -O - "$2" > /tmp/temp.wget
  downloadStatus=$?

  if [[ $downloadStatus == 0 ]]; then
    mv -f /tmp/temp.wget "$3"
    printf "\e[0;32mDone\n"
  else
    printf "\e[0;31mError\n"
    error "An error occurred while downloading $2. Rolling back changes..."
    unpatch

    error "An error occurred while downloading $2, every changes were rolled back."
    error "Please check your internet connection / GithubStatus. If you think this is an error, please file an issue to the repository."
    exit 1
  fi
}

################################
# PATCH PROCEDURES
################################

patch() {
  check_dependencies

  info "====== Patching procedure (branch: $branch) ======"

  if [[ -f "$vs_path/lib/libsynovte.so.orig" ]]; then
    error "You're trying to patch over an already patched VideoStation, if that's really what you want to do, please unpatch before patching again."
    exit 1
  fi

  for filename in "${wrappers[@]}"; do
    if [[ -f "$vs_path/bin/$filename" ]]; then
      info "Saving current $filename as $filename.orig"
      mv -n "$vs_path/bin/$filename" "$vs_path/bin/$filename.orig"

      download "$filename.sh" "$repo_base_url/$branch/wrappers/$filename.sh" "$vs_path/bin/$filename"
      chown root:VideoStation "$vs_path/bin/$filename"
      chmod 750 "$vs_path/bin/$filename"
      chmod u+s "$vs_path/bin/$filename"

      sed -i -e "s/@package_name@/VideoStation/" "$vs_path/bin/$filename"
      sed -i -e "s/@ffmpeg_version@/ffmpeg$ffmpegversion/" "$vs_path/bin/$filename"
    fi
  done

  if [[ -d $cp_bin_path ]]; then
    for file in "${cp_to_patch[@]}"; do
      filename="${file%%:*}"
      target="${file##*:}"

      if [[ -f "$cp_bin_path/$filename" ]]; then
        info "Patching CodecPack's $filename"

        mv -n "$cp_bin_path/$filename" "$cp_bin_path/$filename.orig"
        download "$filename.sh" "$repo_base_url/$branch/wrappers/$target.sh" "$cp_bin_path/$filename"
        chmod 750 "$cp_bin_path/$filename"
        chmod u+s "$cp_bin_path/$filename"

        sed -i -e "s/@package_name@/CodecPack/" "$cp_bin_path/$filename"
        sed -i -e "s/@ffmpeg_version@/ffmpeg$ffmpegversion/" "$cp_bin_path/$filename"
      fi
    done

    if [[ -d "$cp_path/lib/gstreamer" ]]; then
      gst_lib_path="$cp_path/lib/gstreamer/patch"
      gst_plugin_path="$cp_path/lib/gstreamer/gstreamer-1.0/patch"

      info "Downloading CodecPack's gstreamer plugins..."

      mkdir "$gst_plugin_path"
      for plugin in "${gstreamer_plugins[@]}"; do
        download "Gstreamer plugin: $plugin" "$repo_base_url/$branch/plugins/$plugin.so" "$gst_plugin_path/$plugin.so"
      done

      mkdir "$gst_lib_path"
      mkdir -p "$gst_lib_path/dri"
      mkdir -p "$gst_lib_path/x264-10bit"
      mkdir -p "$gst_lib_path/x265-10bit"

      for lib in "${gstreamer_libs[@]}"; do
        download "Gstreamer library: $lib" "$repo_base_url/$branch/libs/$lib" "$gst_lib_path/$lib"
      done
    fi
  fi

  if [[ -f "$vs_path/bin/gst-launch-1.0" ]]; then
    gst_lib_path="$vs_path/lib/gstreamer/patch"
    gst_plugin_path="$vs_path/lib/gstreamer/gstreamer-1.0/patch"

    info "Downloading gstreamer plugins..."

    mkdir "$gst_plugin_path"
    for plugin in "${gstreamer_plugins[@]}"; do
      download "Gstreamer plugin: $plugin" "$repo_base_url/$branch/plugins/$plugin.so" "$gst_plugin_path/$plugin.so"
    done

    mkdir "$gst_lib_path"
    mkdir -p "$gst_lib_path/dri"
    mkdir -p "$gst_lib_path/x264-10bit"
    mkdir -p "$gst_lib_path/x265-10bit"

    for lib in "${gstreamer_libs[@]}"; do
      download "Gstreamer library: $lib" "$repo_base_url/$branch/libs/$lib" "$gst_lib_path/$lib"
    done

    info "Saving current GSTOmx configuration..."
    mv -n "$vs_path/etc/gstomx.conf" "$vs_path/etc/gstomx.conf.orig"

    info "Injecting GSTOmx configuration..."
    cp -n "$cp_path/etc/gstomx.conf" "$vs_path/etc/gstomx.conf"
  fi

  info "Saving current libsynovte.so as libsynovte.so.orig"
  cp -n "$libsynovte_path" "$libsynovte_path.orig"
  chown VideoStation:VideoStation "$libsynovte_path.orig"

  info "Enabling eac3, dts and truehd"
  sed -i -e 's/eac3/3cae/' -e 's/dts/std/' -e 's/truehd/dheurt/' "$libsynovte_path"

  restart_packages
  clean

  success "Done patching, you can now enjoy your movies ;) (please add a star to the repo if it worked for you)"
}

unpatch() {
  info "====== Unpatch procedure ======"

  if [[ -f "$libsynovte_path.orig" ]]; then
    info "Restoring libsynovte.so"
    mv -T -f "$libsynovte_path.orig" "$libsynovte_path"
  else
    info "libsynovte.so was not patched, keeping actual file."
  fi

  find "$vs_path/bin" -type f -name "*.orig" | while read -r filename; do
    info "Restoring VideoStation's $filename"
    mv -T -f "$filename" "${filename::-5}"
  done

  if [[ -d $cp_bin_path ]]; then
    for file in "${cp_to_patch[@]}"; do
      filename="${file%%:*}"
      target="${file##*:}"

      rm -f "$cp_bin_path/$target"

      if [[ -f  "$cp_bin_path/$filename.orig" ]]; then
        info "Restoring CodecPack's $filename"
        mv -T -f "$cp_bin_path/$filename.orig" "$cp_bin_path/$filename"
      fi
    done

    if [[ -d "$cp_path/lib/gstreamer" ]]; then
      info "Removing CodecPack gstreamer's patched libraries and plugins"
      rm -rf "$cp_path/lib/gstreamer/patch"
      rm -rf "$cp_path/lib/gstreamer/gstreamer-1.0/patch"
    fi
  fi

  if [[ -f "$vs_path/bin/gst-launch-1.0" ]]; then
    info "Removing VideoStation gstreamer's patched libraries and plugins"
    rm -rf "$vs_path/lib/gstreamer/patch"
    rm -rf "$vs_path/lib/gstreamer/gstreamer-1.0/patch"

    if [[ -f "$vs_path/etc/gstomx.conf.orig" ]]; then
      info "Restoring GSTOmx configuration..."
      mv -T -f "$vs_path/etc/gstomx.conf.orig" "$vs_path/etc/gstomx.conf"
    else
      info "GSTOmx configuration was not patched, keeping actual file."
    fi
  fi

  restart_packages
  clean

  success "Unpatch complete"
}

################################
# ENTRYPOINT
################################
root_check

while getopts a:b:p:v: flag; do
  case "${flag}" in
    a) action=${OPTARG};;
    b) branch=${OPTARG};;
    p) repo_base_url="${OPTARG}/AlexPresso/VideoStation-FFMPEG-Patcher";;
    v) ffmpegversion="${OPTARG}";;
    *) echo "usage: $0 [-a patch|unpatch] [-b branch] [-p http://proxy] [-v ffmpegVersion]" >&2; exit 1;;
  esac
done

if [[ "$ffmpegversion" == "4" ]]; then
  ffmpegversion=""
fi

dependencies=("VideoStation" "ffmpeg$ffmpegversion")

welcome_motd

info "You're running DSM $dsm_version"
if [[ -d /var/packages/CodecPack/target/pack ]]; then
  cp_path="$cp_base_path/target/pack"
  cp_bin_path="$cp_path/bin"
  info "Tuned script for DSM $dsm_version"
fi

case "$action" in
  unpatch) unpatch;;
  patch) patch;;
esac

