#!/usr/bin/env bash
#
# encode-targetsize-43-crop.sh - Batch encode videos to NTSC 4:3 with hardware accel (multi-OS) 
#                                Targeting iGPUs (Apple Silicon, Intel HD, Intel Arc)
#                                Uses VideoToolbox on macOS, VAAPI on Linux, and QuickSync on Windows.
# 
set -euo pipefail

# --- Default Settings ---
VIDEO_BITRATE="1500k"
AUDIO_BITRATE="128k"
TARGET_SIZE=""
KEEP_LOGS=0
VIDEO_BITRATE_MANUALLY_SET=0

# --- Global Counters ---
encoded_count=0
skipped_count=0
failed_count=0

# --- Functions ---

usage() {
    echo "Usage: $0 [options] <input_directory> <output_directory>"
    echo "Batch encodes video files to NTSC 4:3 MP4."
    echo
    echo "Options:"
    echo "  --target-size <size>      Set a target file size (e.g., 250MB, 700MiB). Overrides --video-bitrate."
    echo "  --video-bitrate <rate>    Set video bitrate (e.g., 2000k). Default: $VIDEO_BITRATE"
    echo "  --audio-bitrate <rate>    Set audio bitrate (e.g., 192k). Default: $AUDIO_BITRATE"
    echo "  --keep-logs               Do not delete log files on successful encodes."
    echo "  -h, --help                Show this help message."
    exit 1
}

check_deps() {
    for cmd in ffmpeg ffprobe awk tee bc; do
        if ! command -v "$cmd" &> /dev/null; then
            echo "❌ Error: Required command '$cmd' is not installed." >&2
            exit 1
        fi
    done
}

# Validate bitrate format (must be ###k)
validate_bitrate() {
    local rate="$1"
    if [[ ! "$rate" =~ ^[0-9]+k$ ]]; then
        echo "❌ Error: Bitrate must be specified in kilobits with a trailing 'k' (e.g., 128k, 2000k)." >&2
        exit 1
    fi
}

# Converts a size string (e.g., 700MB, 700MiB) to total kilobits.
# Requires explicit MB or MiB suffix.
parse_size_to_kbits() {
    local size_str="$1"
    echo "$size_str" | awk '
        /MiB$/ {
            sub(/MiB$/, "", $1)
            val=$1 * 1024 * 1024     # bytes
            print int((val * 8) / 1000)  # kilobits
            next
        }
        /MB$/ {
            sub(/MB$/, "", $1)
            val=$1 * 1024 * 1024     # bytes (treat MB as MiB for consistency)
            print int((val * 8) / 1000)  # kilobits
            next
        }
        {
            print "Error: Target size must end with MB or MiB (case-sensitive)." > "/dev/stderr"
            exit 1
        }
    '
}

process_file() {
    local in_file="$1"
    local rel_path="${in_file#$INPUT_DIR/}"
    local out_file="$OUTPUT_DIR/${rel_path%.*}.ntsc43.mp4"
    local log_file="$OUTPUT_DIR/${rel_path%.*}.ntsc43.log"

    mkdir -p "$(dirname "$out_file")"
    echo "▶ Encoding: $rel_path"

    local current_video_bitrate="$VIDEO_BITRATE"

    # If target size is set, calculate the required video bitrate
    if [[ -n "$TARGET_SIZE" ]]; then
        local duration
        duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$in_file")
        if [[ -z "$duration" || $(echo "$duration <= 0" | bc -l) -eq 1 ]]; then
            echo "❌ FAILED: Could not determine duration of $rel_path. Cannot calculate target bitrate."
            failed_count=$((failed_count + 1))
            return
        fi

        local target_kbits audio_kbits
        target_kbits=$(parse_size_to_kbits "$TARGET_SIZE")
        audio_kbits=$(echo "$AUDIO_BITRATE" | tr -d 'k')

        # video_bitrate = (total_kbits / duration_sec) - audio_bitrate
        local calculated_vbitrate
        calculated_vbitrate=$(echo "scale=2; ($target_kbits / $duration) - $audio_kbits" | bc)

        if [[ "${calculated_vbitrate%.*}" -lt 100 ]]; then
            echo "⚠️ Warning: Calculated bitrate ($calculated_vbitrate) is very low for $rel_path. Clamping to 100k."
            calculated_vbitrate=100
        fi

        current_video_bitrate="${calculated_vbitrate}k"
        echo "    Target Size: $TARGET_SIZE -> Calculated Video Bitrate: $current_video_bitrate"
    fi

    # Skip logic AFTER validation
    if [[ -f "$out_file" ]]; then
        echo "⏭ Skipping (already exists): $rel_path"
        skipped_count=$((skipped_count + 1))
        return
    fi

    # Get dimensions and determine if we need to crop in one go
    local is_widescreen
    is_widescreen=$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height \
        -of csv=p=0:s=, "$in_file" | awk -F, '{w=$1; h=$2; print (w/h > 1.34) ? 1 : 0}')

    local vfilt
    if [[ "$is_widescreen" -eq 1 ]]; then
        vfilt="crop=in_h*4/3:in_h"
    else
        vfilt="scale=in_w:in_h"
    fi

    (set -o pipefail && ffmpeg -y -nostdin \
        $HWACCEL \
        -i "$in_file" \
        -vf "$vfilt,fps=30000/1001,format=yuv420p,scale=720:480,setsar=8/9" \
        -c:v "$VIDEO_CODEC" -b:v "$current_video_bitrate" \
        -c:a aac -b:a "$AUDIO_BITRATE" -ac 2 -ar 48000 \
        -af "loudnorm=I=-16:TP=-1.5:LRA=11" \
        "$out_file" 2>&1 | tee "$log_file")

    local ffmpeg_exit_code=$?
    if [ $ffmpeg_exit_code -ne 0 ]; then
        echo "❌ FAILED: $rel_path (ffmpeg exited with code $ffmpeg_exit_code). See log for details: $log_file"
        failed_count=$((failed_count + 1))
        rm -f "$out_file"
        return
    fi

    echo "✅ Finished: $rel_path"
    encoded_count=$((encoded_count + 1))

    if [[ "$KEEP_LOGS" -eq 0 ]]; then
        rm -f "$log_file"
    fi
}

# --- Main Script ---

# 1. Parse Command-Line Arguments
while [[ $# -gt 0 ]]; do
    key="$1"
    case $key in
        --target-size)
        TARGET_SIZE="$2"
        shift 2
        ;;
        --video-bitrate)
        VIDEO_BITRATE="$2"
        VIDEO_BITRATE_MANUALLY_SET=1
        shift 2
        ;;
        --audio-bitrate)
        AUDIO_BITRATE="$2"
        shift 2
        ;;
        --keep-logs)
        KEEP_LOGS=1
        shift
        ;;
        -h|--help)
        usage
        ;;
        -* )
        echo "Unknown option: $1"
        usage
        ;;
        *)
        break
        ;;
    esac
done

# 2. Validate Dependencies & Positional Arguments
check_deps
[ "$#" -ne 2 ] && usage
INPUT_DIR=$(realpath "$1")
OUTPUT_DIR=$(realpath "$2")

if [ ! -d "$INPUT_DIR" ]; then
    echo "❌ Error: Input directory not found: $1" >&2
    exit 1
fi

# Enforce bitrate formats before processing
validate_bitrate "$AUDIO_BITRATE"
validate_bitrate "$VIDEO_BITRATE"

if [[ -n "$TARGET_SIZE" && "$VIDEO_BITRATE_MANUALLY_SET" -eq 1 ]]; then
    echo "⚠️ Warning: Both --target-size and --video-bitrate were specified."
    echo "   The --target-size option will take precedence and calculate the video bitrate automatically."
fi

mkdir -p "$OUTPUT_DIR"

# 3. Set up OS-specific FFmpeg parameters
OS="$(uname -s)"
case "$OS" in
    Linux*)   VIDEO_CODEC="h264_vaapi"; HWACCEL="-hwaccel vaapi -vaapi_device /dev/dri/renderD128" ;;
    Darwin*)  VIDEO_CODEC="h264_videotoolbox"; HWACCEL="-hwaccel videotoolbox" ;;
    MINGW*|MSYS*|CYGWIN*) VIDEO_CODEC="h264_qsv"; HWACCEL="-hwaccel qsv" ;;
    *) echo "❌ Unsupported OS: $OS"; exit 1 ;;
esac

echo "🚀 Starting batch encode..."
echo "    Input:          $INPUT_DIR"
echo "    Output:         $OUTPUT_DIR"
echo "    OS:             $OS ($VIDEO_CODEC)"
if [[ -n "$TARGET_SIZE" ]]; then
echo "    Target Size:    $TARGET_SIZE (video bitrate will be calculated per file)"
else
echo "    Video Bitrate:  $VIDEO_BITRATE"
fi
echo "    Audio Bitrate:  $AUDIO_BITRATE"
echo "    Keep Logs:      $(if [ $KEEP_LOGS -eq 1 ]; then echo 'Yes'; else echo 'No'; fi)"
echo "--------------------------------------------------"

# 4. Find and process video files sequentially
while IFS= read -r -d '' file; do
    process_file "$file"
    echo "--------------------------------------------------"
done < <(find "$INPUT_DIR" -type f \
    \( -iname '*.mp4' -o -iname '*.mov' -o -iname '*.mkv' -o -iname '*.avi' -o -iname '*.ts' -o -iname '*.m4v' \) \
    -print0)

echo "🎉 All done! Encoded files are in: $OUTPUT_DIR"
echo "    Encoded: $encoded_count"
echo "    Skipped: $skipped_count"
echo "    Failed:  $failed_count"
