#!/bin/zsh

set -e
set -x

setopt nullglob

export SCRIPT_PATH=${0:a:h}
source ${SCRIPT_PATH}/config.sh
test -f ${SCRIPT_PATH}/config.local.sh && source ${SCRIPT_PATH}/config.local.sh
export PATH=${TARGET}/bin:$PATH
export DYLD_LIBRARY_PATH=${TARGET}/lib:${DYLD_LIBRARY_PATH}
source ${SCRIPT_PATH}/specs.sh

RAKE=./drake

function fail {
  echo $@
  exit 1
}

function run_curl {
  curl --retry 3 --retry-delay 5 --retry-all-errors --connect-timeout 30 --max-time 600 "$@"
}

function verify_checksum {
  local file=$1
  local expected_checksum=$2
  local allow_failure=$3
  local actual_checksum

  actual_checksum=$(openssl dgst -sha256 < ${file} | sed -e 's/.* //')

  if [[ ${actual_checksum} != ${expected_checksum} ]]; then
    if [[ ${allow_failure:-0} == 1 ]]; then
      return 1
    fi

    fail "File checksum failed: ${file} SHA256 expected ${expected_checksum} actual ${actual_checksum}"
  fi
}

function retrieve_file {
  local spec_name="spec_$1"
  local -a spec=(${(P)spec_name})
  local file=${spec[1]}
  local url=${spec[2]}
  local expected_checksum=${spec[3]}

  file=${SRCDIR}/${file}

  if [[ -f ${file} ]]; then
    if verify_checksum ${file} ${expected_checksum} 1; then
      return
    fi

    echo "Warning: file ${file} exists but has the wrong checksum; retrieving anew"
    command rm ${file}
  fi

  if [[ ! -f ${file} ]]; then
    run_curl -L ${url} > ${file}
  fi

  verify_checksum ${file} ${expected_checksum}
}

function build_tarball {
  local package=${PWD:t}
  if [[ -n $SHARED ]] package="${package}-shared"
  $DEBUG ${SCRIPT_PATH}/myinstall.sh build package $package $@
}

function build_package {
  local FUNC_NAME=$1 FILE
  case $1 in
    */*)
      FILE=${1//*\//}
      ;;
    *)
      retrieve_file $1

      local SPEC_NAME="spec_$1"
      local -a SPEC=(${(P)SPEC_NAME})
      FILE=${SPEC[1]}
      ;;
  esac
  shift
  local PACKAGE=${${FILE%.*}%.tar}
  local DIR=${DIR:-$PACKAGE}

  case ${FILE##*.} in
    xz|lz|lzma) COMPRESSION=J ;;
    bz2)        COMPRESSION=j ;;
    gz)         COMPRESSION=z ;;
    tar)        COMPRESSION=  ;;
    *)          echo Unknown compression for ${FILE} ; exit 1 ;;
  esac

  cd $CMPL
  if [[ -z $NO_EXTRACTION ]]; then
    $DEBUG rm -rf ${DIR}
    $DEBUG tar x${COMPRESSION}f ${SRCDIR}/${FILE}
  fi

  $DEBUG cd ${DIR}

  local patch_dir=${SCRIPT_PATH}/${FUNC_NAME}-patches
  if [[ -d ${patch_dir} ]]; then
    for PART in ${patch_dir}/*.patch ; do
      patch -p1 < ${PART}
    done
  fi

  if [[ -n $NO_CONFIGURE ]]; then
    return
  fi

  saved_CFLAGS=${CFLAGS}
  saved_CXXFLAGS=${CXXFLAGS}
  saved_LDFLAGS=${LDFLAGS}

  export CFLAGS="${CFLAGS} -I${TARGET}/include"
  export LDFLAGS="${LDFLAGS} -L${TARGET}/lib"

  if [[ ( -n ${CONFIGURE} ) || ( -x ./configure ) ]]; then
    if [[ -n ${build_package_hook_pre_configure} ]]; then
      ${build_package_hook_pre_configure}
    fi

    $DEBUG ${CONFIGURE:-./configure} $@

    if [[ -z $NO_MAKE ]]; then
      $DEBUG make

      if [[ -n ${build_package_hook_pre_installation} ]]; then
        ${build_package_hook_pre_installation}
      fi

      build_tarball
    fi

  else
    mkdir mtx-build
    cd mtx-build

    $DEBUG cmake .. $@

    if [[ -z $NO_MAKE ]]; then
      $DEBUG make

      if [[ -n ${build_package_hook_pre_installation} ]]; then
        ${build_package_hook_pre_installation}
      fi

      build_tarball command "make DESTDIR=TMPDIR install"
    fi

    cd ..

  fi

  CFLAGS=${saved_CFLAGS}
  CXXFLAGS=${saved_CXXFLAGS}
  LDFLAGS=${saved_LDFLAGS}
}

mkdir -p $CMPL

function build_autoconf {
  build_package autoconf --prefix=${TARGET}
}

function build_automake {
  build_package automake --prefix=${TARGET}
}

function build_pkgconfig {
  build_package pkgconfig \
    --prefix=${TARGET} \
    --with-pc-path=${TARGET}/lib/pkgconfig \
    --with-internal-glib \
    --enable-static \
    --enable-shared=no
}

function build_libiconv {
  build_package libiconv \
    ac_cv_prog_AWK=/usr/bin/awk \
    ac_cv_path_GREP=/usr/bin/grep \
    ac_cv_path_SED=/usr/bin/sed \
    --prefix=${TARGET} \
    --enable-static \
    --docdir=${prefix}/share/doc/${name} \
    --without-libiconv-prefix \
    --without-libintl-prefix \
    --disable-nls \
    --enable-extra-encodings \
    --enable-shared=no
}

function build_cmake {
  build_package cmake --prefix=${TARGET}
}

function build_ogg {
  build_package ogg \
    --prefix=${TARGET} \
    --disable-shared \
    --enable-static
}

function build_vorbis_pre_configure {
  echo fixing libVorbis build

  perl -pi -e 's{-+force_cpusubtype_ALL}{}g' configure.ac configure
}

function build_vorbis {
  build_package_hook_pre_configure=build_vorbis_pre_configure \
  build_package vorbis \
    --prefix=${TARGET} \
    --with-ogg-libraries=${TARGET}/lib \
    --with-ogg-includes=${TARGET}/include/ \
    --enable-static \
    --disable-shared
}

function build_flac {
  build_package flac \
    --prefix=${TARGET} \
    --with-ogg-libraries=${TARGET}/lib \
    --with-ogg-includes=${TARGET}/include/ \
    --with-libiconv-prefix=${TARGET} \
    --enable-static \
    --disable-shared
}

function build_zlib {
  DIR=${${spec_zlib[1]%%.tar*}/zlib-v/zlib-} \
  build_package zlib \
    --prefix=${TARGET} \
    --static
}

function build_gettext {
  build_package gettext \
    --prefix=${TARGET} \
    --disable-csharp \
    --disable-native-java \
    --disable-openmp \
    --without-emacs \
    --without-libexpat-prefix \
    --without-libxml2-prefix \
    --with-included-gettext \
    --with-included-glib \
    --with-included-libcroco \
    --with-included-libunistring \
    --with-included-libxml \
    --enable-static \
    --disable-shared
}

function build_gmp {
  build_package gmp \
    --prefix=${TARGET} \
    --enable-shared=no \
    --enable-static \
    --enable-cxx \
    --with-pic \
    --without-readline
}

function build_boost {
  local -a args properties

  args=(--reconfigure -sICONV_PATH=${TARGET} -j$DRAKETHREADS --prefix=TMPDIR/${TARGET} --libdir=TMPDIR/${TARGET}/lib)
  properties=(toolset=clang link=static variant=release)
  if [[ -n $CXXFLAGS ]] properties+=(cxxflags="${(q)CXXFLAGS}")
  if [[ -n $LDFLAGS  ]] properties+=(linkflags="${(q)LDFLAGS}")

  NO_MAKE=1 CONFIGURE=./bootstrap.sh build_package boost \
    --with-toolset=clang
  build_tarball command "./b2 ${args} ${properties} install"
}

function build_cmark {
  build_package cmark \
    -DCMAKE_INSTALL_PREFIX=${TARGET} \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMARK_TESTS=OFF \
    -DCMARK_STATIC=ON \
    -DCMARK_SHARED=OFF
}

function build_qt {
  local -a args
  args=(
    --prefix=${TARGET}
    -opensource
    -confirm-license
    -release
    -force-bundled-libs
    -no-feature-zstd

    -nomake benchmarks
    -nomake examples
    -nomake tests
    -nomake manual-tests
    -nomake minimal-static-tests

    -skip qt3d,qt5compat,qtactiveqt,qtcanvaspainter,qtcharts,qtcoap,qtconnectivity,qtdatavis3d,qtdeclarative,qtdoc,qthttpserver,qtlanguageserver,qtlottie,qtmqtt,qtnetworkauth,qtopcua,qtpositioning,qtquick3d,qtquick3dphysics,qtquicktimeline,qtremoteobjects,qtscxml,qtsensors,qtserialbus,qtserialport,qtspeech,qtvirtualkeyboard,qtwayland,qtwebchannel,qtwebengine,qtwebsockets,qtwebview,qtgraphs,qtlocation,qtquickeffectmaker

    -no-framework
    -no-avx512

    -no-feature-cups
    -no-feature-printsupport
    -no-feature-dbus
    -no-feature-glib
    -no-feature-openssl
    -no-feature-sql
    -no-feature-sql-db2
    -no-feature-sql-ibase
    -no-feature-sql-mysql
    -no-feature-sql-oci
    -no-feature-sql-odbc
    -no-feature-sql-sqlite

    -no-sbom

    --
    -DCMAKE_OSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET
  )

  local package=qt-everywhere-src-${QTVER}
  local saved_CXXFLAGS=$CXXFLAGS
  export CXXFLAGS="${QT_CXXFLAGS}"
  export QMAKE_CXXFLAGS="${CXXFLAGS}"

  NO_CONFIGURE=1 build_package qt

  cd ${CMPL}/${package}
  $DEBUG ./configure ${args}

  time $DEBUG cmake \
       --build . \
       --parallel $DRAKETHREADS

  # cd ${CMPL}/${package}
  build_tarball command "cmake --install . --prefix TMPDIR${TARGET}"

  CXXFLAGS=$saved_CXXFLAGS
}

function build_docbook_xsl {
  if [[ -d ${DOCBOOK_XSL_ROOT_DIR} ]]; then
    return
  fi

  retrieve_file docbook_xsl

  tar xfC ${SRCDIR}/${spec_docbook_xsl[1]} ${DOCBOOK_XSL_ROOT_DIR:h}
  ln -s ${${spec_docbook_xsl[1]}%%.tar*} ${DOCBOOK_XSL_ROOT_DIR}
}

function build_gpg {
  build_package gpg --prefix=${TARGET}
}

function build_shared_mime_info {
  NO_CONFIGURE=1 build_package shared_mime_info
  command mkdir -p ${TARGET}/share/mime/packages
  command cp data/freedesktop.org.xml.in ${TARGET}/share/mime/packages/freedesktop.org.xml
}

function build_configured_mkvtoolnix {
  if [[ -z ${MTX_VER} ]] fail Variable MTX_VER not set

  cd ${CMPL}/mkvtoolnix-${MTX_VER}

  local dmgbase=${CMPL}/dmg-${MTX_VER}
  local dmgcnt=$dmgbase/${APP_BUNDLE_NAME}/Contents
  local dmgmac=$dmgcnt/MacOS

  local -a args
  args=(
    --prefix=$dmgmac
    --bindir=$dmgmac
    --datarootdir=$dmgmac
    --with-extra-libs=${TARGET}/lib
    --with-extra-includes=${TARGET}/include
    --with-boost-libdir=${TARGET}/lib
    --with-docbook-xsl-root=${DOCBOOK_XSL_ROOT_DIR}
    --disable-debug
  )

  ./configure ${args}

  # Embed the FreeDesktop MIME DB that build_shared_mime_info installs earlier in the
  # default target list; the guard makes its absence a no-op (non-macOS/partial builds).
  local freedesktop_mime=${TARGET}/share/mime/packages/freedesktop.org.xml
  if [[ -f ${freedesktop_mime} ]]; then
    cat > src/mkvtoolnix-gui/qt_resources_macos.qrc <<QRC_EOF
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE RCC>
<RCC version='1.0'>
 <qresource>
  <file alias='/qt-project.org/qmime/packages/freedesktop.org.xml'>${freedesktop_mime}</file>
</qresource>
</RCC>
QRC_EOF
  fi

  grep -q 'BUILD_GUI.*yes' build-config
}

function retrieve_verified_source_tarball {
  if [[ -z ${MTX_VER} ]] fail Variable MTX_VER not set

  local public_key_name=${AUTHOR_PUBLIC_KEY_URL:t}
  local tarball_name=mkvtoolnix-${MTX_VER}.tar.xz
  local signature_name=${tarball_name}.sig

  rm -f ${SRCDIR}/${public_key_name} ${SRCDIR}/${signature_name}

  run_curl -o ${SRCDIR}/${public_key_name} ${AUTHOR_PUBLIC_KEY_URL}
  run_curl -o ${SRCDIR}/${signature_name} ${SOURCES_URL}/${signature_name}

  if [[ ! -f ${SRCDIR}/${tarball_name} ]]; then
    run_curl -o ${SRCDIR}/${tarball_name} ${SOURCES_URL}/${tarball_name}
  fi

  local gpghome=$(mktemp -d)

  gpg --homedir ${gpghome} --import ${SRCDIR}/${public_key_name} 2>&1

  local signature_identity=$( gpg --homedir ${gpghome} \
    --verify ${SRCDIR}/${signature_name} ${SRCDIR}/${tarball_name} 2>&1 \
    | awk -F"<|>" '/Good signature/ { print $2 }' )

  if [[ ${signature_identity} != ${AUTHOR_IDENTITY} ]]; then
    fail Source tarball ${tarball_name} is not signed by ${AUTHOR_IDENTITY}
  fi

  rm -rf ${gpghome}
}

function build_mkvtoolnix {
  if [[ -z ${MTX_VER} ]] fail Variable MTX_VER not set

  retrieve_verified_source_tarball

  NO_MAKE=1 NO_CONFIGURE=1 build_package /mkvtoolnix-${MTX_VER}.tar.xz
  build_configured_mkvtoolnix

  ${RAKE} clean
  ${RAKE} -j ${DRAKETHREADS}
}

function build_dmg {
  if [[ -z ${MTX_VER} ]] fail Variable MTX_VER not set

  if [[ -f ${SCRIPT_PATH}/unlock_keychain.sh ]] ${SCRIPT_PATH}/unlock_keychain.sh

  local dmgbase=${CMPL}/dmg-${MTX_VER}
  local dmgapp=$dmgbase/${APP_BUNDLE_NAME}
  local dmgcnt=$dmgapp/Contents
  local dmgmac=$dmgcnt/MacOS
  local latest_link=${CMPL}/latest

  rm -f ${latest_link}

  if [[ -z $DMG_NO_CD ]] cd ${CMPL}/mkvtoolnix-${MTX_VER}

  rm -rf $dmgbase
  ${RAKE} install prefix=${dmgcnt}
  test -f ${dmgmac}/mkvtoolnix-gui

  strip ${dmgcnt}/MacOS/mkv{merge,info,extract,propedit,toolnix-gui}

  command mv ${dmgmac}/mkvtoolnix ${dmgmac}/data

  cp README.md $dmgbase/README.txt
  cp COPYING $dmgbase/COPYING.txt
  cp NEWS.md $dmgbase/NEWS.txt

  cat > $dmgbase/README.macOS.txt <<EOF
MKVToolNix – macOS specific notes
========================================

Configuration files are stored in ~/.config/bunkus.org and temporary
files are stored in the folder automatically set via TMPDIR.

This build works only with macOS ${MACOSX_DEPLOYMENT_TARGET} or newer. Older
releases that work on older macOS versions can be found at
https://mkvtoolnix.download/downloads.html#macosx-old

If you need the command line tools then copy mkvextract, mkvinfo,
mkvmerge and mkvpropedit from ./${APP_BUNDLE_NAME}/Contents/MacOS/
to /usr/local/bin

EOF

  mkdir -p $dmgcnt/Resources
  cp share/icons/macos/MKVToolNix.icns $dmgcnt/Resources/MKVToolNix.icns

  mkdir -p ${dmgmac}/locale/libqt
  for file in ${TARGET}/translations/qt_*.qm; do
    if [[ ${file} == *qt_help* ]] continue
    lang=${${file:t:r}#qt_}
    lcdir=${dmgmac}/locale/${lang}/LC_MESSAGES
    if [[ ! -d ${lcdir} ]] continue
    baseqm=${TARGET}/translations/qtbase_${lang}.qm
    if [[ -f ${baseqm} ]] file=${baseqm}
    cp -v ${file} ${dmgmac}/locale/libqt/qt_${lang}.qm
  done

  ln -s /Applications ${dmgbase}/

  cat <<EOF > $dmgcnt/PkgInfo
APPL????
EOF

  sed -e "s/::MTX_VER::/${MTX_VER}/" -e "s/::MACOSX_DEPLOYMENT_TARGET::/${MACOSX_DEPLOYMENT_TARGET}/" \
      < ${SCRIPT_PATH}/Info.plist \
      > $dmgcnt/Info.plist

  cat $dmgcnt/Info.plist

  mkdir -p ${dmgmac}/libs
  cp -v -a \
     ${TARGET}/lib/libQt6{Concurrent*.dylib,Core*.dylib,Gui*.dylib,Multimedia*.dylib,Network*.dylib,Svg*.dylib,Widgets*.dylib} \
     ${TARGET}/lib/libboost_system*.dylib \
     ${dmgmac}/libs/

  cd ${TARGET}/plugins

  for dylib in audio/* iconengines/libqsvg*dylib imageformats/libqico*dylib imageformats/libqsvg*dylib imageformats/libqweb*dylib multimedia/lib*dylib platforms/libqcocoa*dylib styles/*dylib tls/*dylib; do
    dstdir=${dmgmac}/${dylib:h:t}
    mkdir -p ${dstdir}
    cp -v ${dylib} ${dstdir}/
  done

  cd -

  ${SCRIPT_PATH}/fix_library_paths.sh ${dmgmac}/**/*.dylib(.) ${dmgmac}/{mkvmerge,mkvinfo,mkvextract,mkvpropedit,mkvtoolnix-gui}

  # Strip the unused ${TARGET}/lib rpath leaked into the binaries (the build
  # host's absolute path; bundled libs load via @executable_path/libs/). Guard
  # with otool so a binary lacking the rpath can't abort the build; runs before
  # codesign since install_name_tool invalidates the signature.
  for FILE (${dmgmac}/{mkvmerge,mkvinfo,mkvextract,mkvpropedit,mkvtoolnix-gui}) {
    if otool -l ${FILE} | grep -qF "path ${TARGET}/lib "; then
      install_name_tool -delete_rpath ${TARGET}/lib ${FILE}
    fi
  }

  # Strip debug symbols from all dylibs and plugins
  strip -x ${dmgmac}/**/*.dylib(.)

  if [[ -n ${SIGNATURE_IDENTITY} ]]; then
    typeset -a non_executables
    for FILE (${dmgcnt}/**/*(.)) {
      if [[ ${FILE} != */MacOS/mkv* ]] non_executables+=(${FILE})
    }

    local harden=""
    if [[ -n ${NOTARY_PROFILE} ]] harden="--options=runtime"

    codesign --force --sign ${SIGNATURE_IDENTITY} ${non_executables}
    codesign --force ${harden} --sign ${SIGNATURE_IDENTITY} ${dmgmac}/mkv*(.)
  fi

  if [[ -n $DMG_NO_DMG ]] return

  if [[ -z ${DMG_REVISION} ]]; then
    fail Variable DMG_REVISION not set
  fi

  local machine=$(uname -m)
  local dmg_label=MKVToolNix-${MTX_VER}-${DMG_REVISION}-${machine}
  local volumename=${dmg_label}
  if [[ $DMG_PRE == 1 ]]; then
    # Ziel: 29.0.0-revision-008-gb71b2b27c-01808
    # describe: release-29.0.0-8-gb71b2b27c
    local build_number_file=$HOME/opt/build-number
    local build_number=$(< $build_number_file)
    let build_number=$build_number+1
    echo $build_number > $build_number_file
    build_number=$(printf '%05d' $build_number)
    local build=$(git describe --tags)
    while [[ $build != *-*-*-* ]]; do
      build=${build}-0
    done
    local num=${${${build#release-}#*-}%-*}
    local hash=${build##*-}
    local revision="revision-$(printf '%03d' ${num})-${hash}-${build_number}"

    volumename=${volumename}-${revision}
  fi

  local dmgname=${CMPL}/${dmg_label}.dmg
  local dmgbuildname=${CMPL}/${volumename}.dmg

  rm -f ${dmgname} ${dmgbuildname}
  hdiutil create -srcfolder ${dmgbase} -volname ${volumename} \
    -fs APFS -format ULMO \
    ${dmgname}

  if [[ -n ${SIGNATURE_IDENTITY} ]] codesign --force -s ${SIGNATURE_IDENTITY} ${dmgname}

  if [[ -n ${NOTARY_PROFILE} ]]; then
    xcrun notarytool submit ${dmgname} --keychain-profile ${NOTARY_PROFILE} --wait
  fi

  if [[ ${dmgname} != ${dmgbuildname} ]] command mv ${dmgname} ${dmgbuildname}

  ln -s ${dmgbuildname} ${latest_link}
}

mkdir -p ${TARGET} ${TARGET}/include ${TARGET}/lib ${SRCDIR} ${DOCBOOK_XSL_ROOT_DIR:h}

if [[ -z $MTX_VER ]]; then
  MTX_VER=$(awk -F, '/AC_INIT/ { gsub("[][]", "", $2); print $2 }' < ${SCRIPT_PATH}/../../configure.ac)
fi

if [[ -z $@ ]]; then
  build_docbook_xsl
  build_autoconf
  build_automake
  build_pkgconfig
  build_libiconv
  build_cmake
  build_ogg
  build_vorbis
  build_flac
  build_zlib
  build_gettext
  build_cmark
  build_gmp
  build_boost
  build_qt
  build_gpg
  build_shared_mime_info
  build_mkvtoolnix

else
  while [[ -n $1 ]]; do
    build_$1
    shift;
  done
fi
