#!/usr/bin/env bash
set -euo pipefail

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "$script_dir/common.sh"

usage() {
  cat <<'EOF'
Usage:
  mix-audio.sh [--dry-run] [--mode amix|duck] [--volume N] <video> <music> <output>

Examples:
  mix-audio.sh video.mp4 music.mp3 mixed.mp4
  mix-audio.sh --mode duck --volume 0.14 video.mp4 music.mp3 mixed.mp4

Environment:
  AUDIO_BITRATE   Output audio bitrate. Default: 192k
  SAMPLE_RATE     Output audio sample rate. Default: 48000
  DUCK_THRESHOLD  sidechaincompress threshold. Default: 0.03
  DUCK_RATIO      sidechaincompress ratio. Default: 10
  DUCK_ATTACK     sidechaincompress attack. Default: 20
  DUCK_RELEASE    sidechaincompress release. Default: 300
EOF
}

dry_run=0
mode="amix"
music_volume="0.18"
audio_bitrate="${AUDIO_BITRATE:-192k}"
sample_rate="${SAMPLE_RATE:-48000}"
duck_threshold="${DUCK_THRESHOLD:-0.03}"
duck_ratio="${DUCK_RATIO:-10}"
duck_attack="${DUCK_ATTACK:-20}"
duck_release="${DUCK_RELEASE:-300}"

while [ "$#" -gt 0 ]; do
  case "$1" in
    --dry-run)
      dry_run=1
      shift
      ;;
    --mode)
      shift
      [ "$#" -gt 0 ] || die "missing value for --mode"
      mode="$1"
      shift
      ;;
    --volume)
      shift
      [ "$#" -gt 0 ] || die "missing value for --volume"
      music_volume="$1"
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    --)
      shift
      break
      ;;
    -*)
      die "unknown option: $1"
      ;;
    *)
      break
      ;;
  esac
done

[ "$#" -eq 3 ] || {
  usage
  exit 1
}

case "$mode" in
  amix|duck)
    ;;
  *)
    die "--mode must be amix or duck"
    ;;
esac

video_file="$1"
music_file="$2"
output_file="$3"

[ -f "$video_file" ] || die "video file not found: $video_file"
[ -f "$music_file" ] || die "music file not found: $music_file"

assert_non_negative_number "volume" "$music_volume"
assert_positive_number "SAMPLE_RATE" "$sample_rate"
assert_non_negative_number "DUCK_THRESHOLD" "$duck_threshold"
assert_positive_number "DUCK_RATIO" "$duck_ratio"
assert_positive_number "DUCK_ATTACK" "$duck_attack"
assert_positive_number "DUCK_RELEASE" "$duck_release"

require_command ffmpeg
require_command ffprobe

has_stream "$video_file" v:0 || die "missing video stream: $video_file"
has_stream "$video_file" a:0 || die "missing audio stream in video file: $video_file"
has_stream "$music_file" a:0 || die "missing audio stream: $music_file"

if [ "$mode" = "amix" ]; then
  filter_complex="\
    [0:a:0]aformat=sample_rates=${sample_rate}:channel_layouts=stereo[voice]; \
    [1:a:0]volume=${music_volume},aformat=sample_rates=${sample_rate}:channel_layouts=stereo[music]; \
    [voice][music]amix=inputs=2:duration=first:dropout_transition=2[a]"
else
  filter_complex="\
    [0:a:0]aformat=sample_rates=${sample_rate}:channel_layouts=stereo,asplit=2[voice_mix][voice_side]; \
    [1:a:0]volume=${music_volume},aformat=sample_rates=${sample_rate}:channel_layouts=stereo[music]; \
    [music][voice_side]sidechaincompress=threshold=${duck_threshold}:ratio=${duck_ratio}:attack=${duck_attack}:release=${duck_release}[ducked]; \
    [voice_mix][ducked]amix=inputs=2:duration=first:dropout_transition=2[a]"
fi

if [ "$dry_run" -eq 1 ]; then
  cat <<EOF
mode=$mode
video_file=$video_file
music_file=$music_file
music_volume=$music_volume
sample_rate=$sample_rate
output_file=$output_file
EOF
  exit 0
fi

ensure_parent_dir "$output_file"

faststart_args=()
case "$(normalized_extension "$output_file")" in
  mp4|m4v|mov)
    faststart_args=(-movflags +faststart)
    ;;
esac

ffmpeg \
  -y \
  -i "$video_file" \
  -i "$music_file" \
  -filter_complex "$filter_complex" \
  -map 0:v:0 \
  -map "[a]" \
  -c:v copy \
  -c:a aac \
  -b:a "$audio_bitrate" \
  "${faststart_args[@]}" \
  "$output_file"
