package main

import (
	"fmt"
	"log"
	"os"

	"valhallir-deconvolver/pkg/convolve"
	"valhallir-deconvolver/pkg/plot"
	"valhallir-deconvolver/pkg/wav"

	"github.com/urfave/cli/v2"
)

func main() {
	app := &cli.App{
		Name:    "valhallir-deconvolver",
		Usage:   "Deconvolve sweep and recorded WAV files to create impulse responses",
		Version: "v1.0.0",
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:     "sweep",
				Usage:    "Path to the sweep WAV file (96kHz 24bit)",
				Required: true,
			},
			&cli.StringFlag{
				Name:     "recorded",
				Usage:    "Path to the recorded WAV file (96kHz 24bit)",
				Required: true,
			},
			&cli.StringFlag{
				Name:     "output",
				Usage:    "Path to the output IR WAV file (96kHz 24bit)",
				Required: true,
			},
			&cli.BoolFlag{
				Name:  "mpt",
				Usage: "Generate minimum phase transform IR in addition to regular IR",
			},
			&cli.IntFlag{
				Name:  "sample-rate",
				Usage: "Output sample rate (44, 48, 88, 96 kHz)",
				Value: 96000,
			},
			&cli.IntFlag{
				Name:  "bit-depth",
				Usage: "Output bit depth (16, 24, 32 bit)",
				Value: 24,
			},
			&cli.Float64Flag{
				Name:  "normalize",
				Usage: "Normalize output to this peak value (0.0-1.0, default 0.95)",
				Value: 0.95,
			},
			&cli.Float64Flag{
				Name:  "trim-threshold",
				Usage: "Silence threshold for trimming (0.0-1.0, default 0.001)",
				Value: 0.001,
			},
			&cli.Float64Flag{
				Name:  "length-ms",
				Usage: "Optional: Output IR length in milliseconds (will trim or zero-pad as needed)",
			},
			&cli.Float64Flag{
				Name:  "fade-ms",
				Usage: "Fade-out duration in milliseconds to apply at the end of the IR (default 5)",
				Value: 5.0,
			},
			&cli.Float64Flag{
				Name:  "highcut",
				Usage: "High-cut filter (low-pass) cutoff frequency in Hz (applied to recorded sweep, optional)",
			},
			&cli.Float64Flag{
				Name:  "lowcut",
				Usage: "Low-cut filter (high-pass) cutoff frequency in Hz (applied to recorded sweep, optional)",
			},
			&cli.IntFlag{
				Name:  "cut-slope",
				Usage: "Cut filter slope in dB/octave (12, 24, 36, 48, ...; default 12)",
				Value: 12,
			},
			&cli.BoolFlag{
				Name:  "plot-ir",
				Usage: "Plot the generated regular IR waveform to ir_plot.png",
			},
		},
		Action: func(c *cli.Context) error {
			// Read sweep WAV file
			sweepData, err := wav.ReadWAVFile(c.String("sweep"))
			if err != nil {
				return err
			}

			// Read recorded WAV file
			recordedData, err := wav.ReadWAVFile(c.String("recorded"))
			if err != nil {
				return err
			}

			log.Printf("Sweep: %d samples, %d channels", len(sweepData.PCMData), sweepData.Channels)
			log.Printf("Recorded: %d samples, %d channels", len(recordedData.PCMData), recordedData.Channels)

			// Optionally filter the recorded sweep
			recordedFiltered := recordedData.PCMData
			recSampleRate := recordedData.SampleRate
			highcutHz := c.Float64("highcut")
			lowcutHz := c.Float64("lowcut")
			cutSlope := c.Int("cut-slope")
			if cutSlope < 12 || cutSlope%12 != 0 {
				return fmt.Errorf("cut-slope must be a positive multiple of 12 (got %d)", cutSlope)
			}
			if lowcutHz > 0 {
				log.Printf("Applying low-cut (high-pass) filter to recorded sweep: %.2f Hz, slope: %d dB/oct", lowcutHz, cutSlope)
				recordedFiltered = convolve.CascadeLowcut(recordedFiltered, recSampleRate, lowcutHz, cutSlope)
			}
			if highcutHz > 0 {
				log.Printf("Applying high-cut (low-pass) filter to recorded sweep: %.2f Hz, slope: %d dB/oct", highcutHz, cutSlope)
				recordedFiltered = convolve.CascadeHighcut(recordedFiltered, recSampleRate, highcutHz, cutSlope)
			}

			log.Println("Performing deconvolution...")
			ir := convolve.Deconvolve(sweepData.PCMData, recordedFiltered)
			log.Printf("Deconvolution result: %d samples", len(ir))

			log.Println("Trimming silence...")
			ir = convolve.TrimSilence(ir, 1e-5)
			log.Printf("After trimming: %d samples", len(ir))

			log.Println("Normalizing...")
			ir = convolve.Normalize(ir, c.Float64("normalize"))
			log.Printf("Final IR: %d samples", len(ir))

			// Validate output format options
			sampleRate := c.Int("sample-rate")
			bitDepth := c.Int("bit-depth")

			// Validate sample rate
			validSampleRates := []int{44100, 48000, 88200, 96000}
			validSampleRate := false
			for _, sr := range validSampleRates {
				if sampleRate == sr {
					validSampleRate = true
					break
				}
			}
			if !validSampleRate {
				return fmt.Errorf("invalid sample rate: %d. Valid options: %v", sampleRate, validSampleRates)
			}

			// Validate bit depth
			validBitDepths := []int{16, 24, 32}
			validBitDepth := false
			for _, bd := range validBitDepths {
				if bitDepth == bd {
					validBitDepth = true
					break
				}
			}
			if !validBitDepth {
				return fmt.Errorf("invalid bit depth: %d. Valid options: %v", bitDepth, validBitDepths)
			}

			// Resample IR to target sample rate if different from input (96kHz)
			targetSampleRate := sampleRate
			if targetSampleRate != 96000 {
				log.Printf("Resampling IR from 96kHz to %dHz...", targetSampleRate)
				ir = convolve.Resample(ir, 96000, targetSampleRate)
				log.Printf("Resampled IR: %d samples", len(ir))
			}

			// Trim or pad IR to requested length if --length-ms is set
			lengthMs := c.Float64("length-ms")
			if lengthMs > 0 {
				targetSamples := int(float64(targetSampleRate) * lengthMs / 1000.0)
				log.Printf("Trimming or padding IR to %d samples (%.2f ms)...", targetSamples, lengthMs)
				ir = convolve.TrimOrPad(ir, targetSamples)
			}

			// Apply fade-out
			fadeMs := c.Float64("fade-ms")
			fadeSamples := int(float64(targetSampleRate) * fadeMs / 1000.0)
			if fadeSamples > 0 {
				log.Printf("Applying linear fade-out: %d samples (%.2f ms)...", fadeSamples, fadeMs)
				ir = convolve.FadeOutLinear(ir, fadeSamples)
			}

			// Write regular IR
			log.Printf("Writing IR to: %s (%dHz, %d-bit WAV)", c.String("output"), sampleRate, bitDepth)
			if err := wav.WriteWAVFileWithOptions(c.String("output"), ir, sampleRate, bitDepth); err != nil {
				return err
			}

			// Plot IR waveform if requested
			if c.Bool("plot-ir") {
				log.Printf("Plotting IR waveform to ir_plot.png...")
				err := plot.PlotIR(ir, sampleRate, c.String("output"))
				if err != nil {
					return fmt.Errorf("failed to plot IR: %v", err)
				}
				log.Printf("IR plot saved as ir_plot.png")
			}

			// Generate MPT IR if requested
			if c.Bool("mpt") {
				log.Println("Generating minimum phase transform...")
				// Use the original 96kHz IR for MPT generation
				originalIR := convolve.Deconvolve(sweepData.PCMData, recordedData.PCMData)
				originalIR = convolve.TrimSilence(originalIR, 1e-5)
				mptIR := convolve.MinimumPhaseTransform(originalIR)
				mptIR = convolve.Normalize(mptIR, c.Float64("normalize"))
				log.Printf("MPT IR: %d samples", len(mptIR))

				// Resample MPT IR to target sample rate if different from input (96kHz)
				if targetSampleRate != 96000 {
					log.Printf("Resampling MPT IR from 96kHz to %dHz...", targetSampleRate)
					mptIR = convolve.Resample(mptIR, 96000, targetSampleRate)
					log.Printf("Resampled MPT IR: %d samples", len(mptIR))
				}

				// Trim or pad MPT IR to requested length if --length-ms is set
				if lengthMs > 0 {
					targetSamples := int(float64(targetSampleRate) * lengthMs / 1000.0)
					log.Printf("Trimming or padding MPT IR to %d samples (%.2f ms)...", targetSamples, lengthMs)
					mptIR = convolve.TrimOrPad(mptIR, targetSamples)
				}

				// Apply fade-out to MPT IR
				if fadeSamples > 0 {
					log.Printf("Applying linear fade-out to MPT IR: %d samples (%.2f ms)...", fadeSamples, fadeMs)
					mptIR = convolve.FadeOutLinear(mptIR, fadeSamples)
				}

				// Generate MPT output filename
				outputPath := c.String("output")
				if len(outputPath) > 4 && outputPath[len(outputPath)-4:] == ".wav" {
					outputPath = outputPath[:len(outputPath)-4]
				}
				mptOutputPath := outputPath + "_mpt.wav"

				log.Printf("Writing MPT IR to: %s (%dHz, %d-bit WAV)", mptOutputPath, sampleRate, bitDepth)
				if err := wav.WriteWAVFileWithOptions(mptOutputPath, mptIR, sampleRate, bitDepth); err != nil {
					return err
				}
				log.Println("Minimum phase transform IR generated successfully!")
			}

			log.Println("Impulse response generated successfully!")
			return nil
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}
