{ lib
, stdenv
, stdenvNoCC
, symlinkJoin
, fetchurl
, fetchpatch
, fetchFromGitHub
, writeShellScript
, writeShellScriptBin
, makeDesktopItem
, fetchzip
, rustPlatform
, alsaLib
, libpulseaudio
, ffmpeg
, libclang
, xorg
, wayland
, glfw
, libva
, mesa
, libdrm
, xz
, zlib
, ocl-icd
, opencv
, qtbase
, qtsvg
, qtwayland
, qtvirtualkeyboard
, qtdeclarative
, qmake
, clang
, imagemagick
, wrapQtAppsHook
, autoPatchelfHook
, pkg-config }: let
  name = "gyroflow";
  version = "1.5.4";

  src-unpatched = fetchFromGitHub {
    name = "${name}-src-unpatched";
    owner = name;
    repo = name;
    rev = "v${version}";
    hash = "sha256-Pdqe8T03KpO3ApOjm/z7gd5waQOvEZBHcvdrLyot4O8=";
  };

  mdk-sdk = stdenvNoCC.mkDerivation rec {
    pname = "mdk-sdk";
    version = "20240110111804";
    src = fetchzip {
      name = "mdk-sdk";
      # unfortunately the only way to pin the version
      url = "https://web.archive.org/web/${version}if_/https://master.dl.sourceforge.net/project/mdk-sdk/mdk-sdk-linux.tar.xz?viasf=1";
      hash = "sha256-Jtdp0YKVOK5+0xBigezB2Oatsr00gAgJuCZlbzpZPHk=";
    };

    buildInputs = [
      stdenv.cc.cc.lib
      alsaLib libpulseaudio
      glfw libdrm mesa
      xz wayland xorg.libX11
    ];

    nativeBuildInputs = [ autoPatchelfHook ];

    installPhase = ''
      cp -r . "$out"
    '';
  };

  gyroflow-unwrapped = rustPlatform.buildRustPackage rec {
    pname = "gyroflow-unwrapped";
    inherit version;
    cargoHash = "sha256-KkGXKoVRZZ7HUTtWYBerrN36a7RqsHjYQb+bwG1JagY=";
    cargoLock = {
      outputHashes = {
        "ahrs-0.6.0" = "sha256-CxWyX8t+BjqIyNj1p1LdkCmNrtJkudmKgZPv0MVcghY=";
        "akaze-0.7.0" = "sha256-KkGXKoVRZZ7HUTtWYBerrN36a7RqsHjYQb+bwG1JagY=";
        "d3d12-0.7.0" = "sha256-0b/LozKVPm+UQTO066s5uoebRxEWoC0V8T7a6e0z9YE=";
        "fc-blackbox-0.2.0" = "sha256-gL8m9DpHJPVD8vvrmuYv+biJT4PA5LmtohJwFVO+khU=";
        "glow-0.13.0" = "sha256-vhPWzsm7NZx9JiRZcVoUslTGySQbASRh/wNlo1nK5jg=";
        "keep-awake-0.1.0" = "sha256-EoXhK4/Aij70f73+5NBUoCXqZISG1+n2eVavNqe8mq4=";
        "nshare-0.9.0" = "sha256-PAV41mMLDmhkAz4+qyf+MZnYTAdMwjk83+f+RdaJji8=";
        "qmetaobject-0.2.10" = "sha256-ldmpbOYoCOaAoipfcCSwuV+fzF9gg1PTbRz2Jm4zJvA=";
        "qml-video-rs-0.1.0" = "sha256-yvA+Je50rF6SgTxDQuDPgyM2MgtD4AAV9uGnAj/ecf4=";
        "rs-sync-0.1.0" = "sha256-sfym7zv5SUitopqNJ6uFP6AMzAGf4Y7U0dzXAKlvuGA=";
        "simplelog-0.12.0" = "sha256-NvmtLbzahSw1WMS3LY+jWiX4SxfSRwidTMvICGcmDO4=";
        "system_shutdown-4.0.1" = "sha256-arJWmEjDdaig/oAfwSolVmk9s1UovrQ5LNUgTpUvoOQ=";
        "telemetry-parser-0.2.8" = "sha256-J4eHCojoGKKbqVKHkyQjxXbfrYsyYn7DR7ZdogbOQK4=";
      };

      lockFile = "${src}/Cargo.lock";
    };

    # breaks some build.rs
    dontCargoCheck = true;

    src = stdenvNoCC.mkDerivation {
      name = "${name}-src";
      inherit version;
      phases = [ "unpackPhase" "patchPhase" "installPhase" ];
      installPhase = ''
        cp -r . "$out"
      '';
      patches = [
        (fetchpatch {
          name = "fix-ffmpeg.patch";
          url = "https://github.com/gyroflow/gyroflow/compare/v1.5.4...cdca1ee.patch";
          hash = "sha256-kEjKlFSa02sa2NvFyoFl1EWL4TuLeFsmYOY0/dYykrg=";
        })
        ./crash-dump.patch
        ./mdk-log.patch
      ];
      src = src-unpatched;
    };

    buildInputs = [
      alsaLib
      ffmpeg
      libclang
      ocl-icd
      opencv
      zlib.static
      qtdeclarative
      wrapQtAppsHook
    ];

    nativeBuildInputs = [ pkg-config qmake clang ] ++ [
      # build.rs abuses qt path variables to get path to qmlcachegen
      # it fails and falls back to calling qmlcachegen directly which doesn't work
      # since it's in libexec, this derivation provides a wrapper to make it work
      (writeShellScriptBin "qmlcachegen" ''
        exec "${qtdeclarative}/libexec/qmlcachegen" "$@"
      '')
    ];

    # build error debugging
    RUST_BACKTRACE="full";
    # build script cannot find libclang
    LIBCLANG_PATH="${libclang.lib}/lib";
    # proprietary mdk-sdk library has to be patched
    MDK_SDK="${mdk-sdk}/";
    # compiler cannot find these for some reason
    CPLUS_INCLUDE_PATH=lib.concatImapStrings (i: path:
    (if i != 1 then ":" else "") +
    "${qtdeclarative}/include/${path}") [
      "QtQuick"
      "QtQuick/${qtdeclarative.version}"
      "QtQuick/${qtdeclarative.version}/QtQuick"
      "QtQml"
      "QtQml/${qtdeclarative.version}"
      "QtQml/${qtdeclarative.version}/QtQml"
      "QtQuickControls2"
      "QtQuickControls2/${qtdeclarative.version}"
      "QtQuickControls2/${qtdeclarative.version}/QtQuickControls2"
    ];
    # build.rs relies on these variables to point to deps
    # instead of discovering them properly
    OPENCV_LINK_PATHS="${opencv}";
    OPENCV_LINK_LIBS=lib.concatImapStrings (i: opt:
    (if i != 1 then "," else "") + opt) [
      "opencv_core"
      "opencv_calib3d"
      "opencv_features2d"
      "opencv_imgproc"
      "opencv_video"
      "opencv_flann"
      "opencv_dnn"
      "opencv_imgcodecs"
      "opencv_objdetect"
      "opencv_stitching"
      "png"
    ];
    FFMPEG_DIR="${ffmpeg.dev}";

    installPhase = ''
      mkdir -p "$out"
      cp -f "target/x86_64-unknown-linux-gnu/release/gyroflow" "$out/"
      strip "$out/gyroflow"
      cp -rf "${lens-profiles}" "$out/camera_presets"
    '';
  };

  lens-profiles = fetchFromGitHub {
    name = "gyroflow-lens-profiles";
    owner = name;
    repo = "lens_profiles";
    rev = "3e72169ae6b8601260497d7216d5fcbbc8b67194";
    hash = "sha256-18KtunSxTsJhBge+uOGBcNZRG3W26M/Osyxllu+N0UI=";
  };

  gyroflow-qt-runtime = symlinkJoin {
    name = "gyroflow-qt-runtime";
    paths = [
      qtbase
      qtsvg
      qtwayland
      qtdeclarative
      qtvirtualkeyboard
    ];
  };

  desktopItem = makeDesktopItem {
    inherit name;
    exec = name;
    icon = name;
    comment = ''
      Advanced gyro-based video stabilization tool for cinematography, drone videography and much more!
    '';
    desktopName = "Gyroflow";
    genericName = "Video stabilization tool";
  };
in stdenvNoCC.mkDerivation {
  pname = name;
  inherit version;
  phases = [ "unpackPhase" "patchPhase" "installPhase" ];
  src = src-unpatched;

  nativeBuildInputs = [ imagemagick ];

  installPhase = ''
    mkdir -p "$out/bin"

    convert resources/icon.png -resize 128x128 icon-128.png
    install -m 444 -D icon-128.png $out/share/icons/hicolor/128x128/apps/${name}.png
    cp -r ${desktopItem}/share/applications $out/share/

    # mdk-sdk reads program name as part of its license check
    # therefore a custom wrapper that preserves the gyroflow name is required
    cp ${writeShellScript "gyroflow-wrapper" ''
      export QT_PLUGIN_PATH=${gyroflow-qt-runtime}/lib/qt-6/plugins
      export QML2_IMPORT_PATH=${qtdeclarative}/lib/qt-6/qml

      # does not find drivers without this
      export LD_LIBRARY_PATH="/run/opengl-driver/lib:/run/opengl-driver/lib/dri:/run/opengl-driver/lib/vdpau:${libva.out}/lib"

      # our build crashes on exit so this silences the excessive crash dumps
      cd /var/empty

      exec -a "$0" "${gyroflow-unwrapped}/gyroflow" "$@"
    ''} $out/bin/gyroflow
  '';
}
