pub mod dft;
pub mod mixed_radix;
pub mod rader;
pub mod rader2;
pub mod radix2;
pub mod windows;

use std::{iter::Map, process::Output};

use crate::{
    complex::Complex32,
    fft::{dft::NaiveDFT, mixed_radix::MixedRadixFFT, rader::RaderFFT, radix2::Radix2FFT},
};

#[derive(Copy, Clone)]
pub enum FFTDirection {
    Forward,
    Inverse,
}

impl FFTDirection {
    fn sign(&self) -> f32 {
        match self {
            FFTDirection::Forward => 1.,
            FFTDirection::Inverse => -1.,
        }
    }
}

pub trait DFT {
    fn create(size: usize, direction: FFTDirection) -> Self
    where
        Self: Sized;

    fn execute(&mut self, input: &[Complex32], output: &mut [Complex32], window: fn(f32) -> f32);
}

pub trait DFTWindow {
    fn eval(t: f32) -> f32;
}

pub fn create_fft(size: usize, direction: FFTDirection) -> Box<dyn DFT> {
    if size <= 16 {
        //println!("Naive {size}");
        return Box::new(NaiveDFT::create(size, direction));
    }
    if size.count_ones() == 1 {
        //println!("Radix 2 {size}");
        return Box::new(Radix2FFT::create(size, direction));
    }

    if is_prime(size) {
        //println!("Prime rader {size}");
        return Box::new(RaderFFT::create(size, direction));
    }

    //println!("Mixed radix {size}");
    Box::new(MixedRadixFFT::create(size, direction))
}

// Utilities
pub fn prime_factors(n: usize) -> Vec<usize> {
    let mut factors = vec![];

    let mut num = n;
    // Divide num successively
    while num != 1 {
        // Try divisors from 2 up to n (included)
        for i in 2..n + 1 {
            // if i divides num, it is a prime factor (if it wasn't, then i would have prime
            // factors that would divide into num before i)
            if num % i == 0 {
                factors.push(i);
                num /= i;
                break;
            }
        }
    }

    // If n = 1 then it does not have any prime factors
    // The prime factor decomposition theorem states that any integer
    // greater than TWO has a unique decomposition

    factors
}

pub fn is_prime(n: usize) -> bool {
    prime_factors(n).len() == 1
}
