use crate::fm_demod::Sample;

pub enum SyncState {
    Active,
    InPulse { count: usize },
}

pub enum SyncEvent {
    HSync,
    VSync,
    VideoSample(Sample),
}

pub struct SyncSeparator<I> {
    pub inner: I,
    pub state: SyncState,
    // Threshold for considering a pulse
    pub sync_threshold: f32,
    pub hsync_min: usize,
    pub hsync_max: usize,
    pub vsync_min: usize,
    pub samples_consumed: usize,
}

impl<I> SyncSeparator<I> {
    pub fn new(inner: I, sample_rate: f32, sync_threshold: f32) -> Self {
        // µs -> samples
        let us = |micros: f32| (micros * sample_rate / 1_000_000.0) as usize;

        Self {
            inner,
            state: SyncState::Active,
            sync_threshold,
            hsync_min: us(2.0),
            hsync_max: us(10.0),
            vsync_min: us(25.0),
            samples_consumed: 0,
        }
    }

    pub fn process_sample(&mut self, sample: Sample) -> Option<SyncEvent> {
        self.samples_consumed += 1;
        match self.state {
            SyncState::Active => {
                if sample < self.sync_threshold {
                    self.state = SyncState::InPulse { count: 1 };
                    None
                } else {
                    Some(SyncEvent::VideoSample(sample))
                }
            }
            SyncState::InPulse { ref mut count } => {
                if sample < self.sync_threshold {
                    *count += 1;
                    None
                } else {
                    let pulse_len = *count;
                    self.state = SyncState::Active;
                    if pulse_len >= self.vsync_min {
                        Some(SyncEvent::VSync)
                    } else if pulse_len >= self.hsync_min && pulse_len <= self.hsync_max {
                        Some(SyncEvent::HSync)
                    } else {
                        Some(SyncEvent::VideoSample(sample))
                    }
                }
            }
        }
    }
}

impl<I, E> Iterator for SyncSeparator<I>
where
    I: Iterator<Item = Result<Sample, E>>,
{
    type Item = Result<SyncEvent, E>;
    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.inner.next()? {
                Ok(sample) => {
                    if let Some(event) = self.process_sample(sample) {
                        return Some(Ok(event));
                    }
                }
                Err(e) => return Some(Err(e)),
            }
        }
    }
}
