use num_complex::Complex;
use std::error::Error;
use std::fs::File;
use std::io::{BufReader, ErrorKind, Read};

pub type IqSample = Complex<f32>;

// Data chunk
// #[derive(Debug)]
// pub struct IqChunk {
//     pub samples: Vec<IqSample>,
// }
//
// impl IqChunk {
//     pub fn new() -> Self {
//         IqChunk {
//             samples: Vec::<IqSample>::new(),
//         }
//     }
// }

pub struct FileSource {
    // Buffer
    pub reader: BufReader<File>,
    // Hackrf I & Q 8 bits
    pub raw_buffer: Vec<u8>,
    // Size of a sample chunk
    pub chunk_samples_size: usize,
    // raw_buffer position (in samples)
    pub cursor: usize,
    // The length of the file in samples
    pub len: u64,
}

impl FileSource {
    pub fn new(file_path: &str, chunk_samples_size: usize) -> Result<Self, Box<dyn Error>> {
        let file = File::open(file_path)?;
        let len = file.metadata().unwrap().len() / 2;
        // Init buffer with size 16 Mo
        let reader = BufReader::with_capacity(16 * 1024 * 1024, file);

        // 1 sample = 2 bytes (1 byte : I, 1 byte : Q)
        let raw_reader = vec![0; chunk_samples_size * 2];

        Ok(Self {
            reader,
            raw_buffer: raw_reader,
            chunk_samples_size,
            cursor: chunk_samples_size,
            len,
        })
    }
}

impl ExactSizeIterator for FileSource {}

impl Iterator for FileSource {
    type Item = Result<IqSample, Box<dyn Error>>;

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.len as usize, Some(self.len as usize))
    }

    fn next(&mut self) -> Option<Self::Item> {
        if self.cursor >= self.chunk_samples_size {
            // Buffer read chunk
            match self.reader.read_exact(&mut self.raw_buffer) {
                Ok(_) => {}

                // EOF
                Err(e) if e.kind() == ErrorKind::UnexpectedEof => {
                    return None;
                }

                Err(e) => {
                    return Some(Err(e.into()));
                }
            }
            self.cursor = 0;
        }

        // Buffer read sample
        let i = (self.raw_buffer[self.cursor * 2] as i8) as f32 / 128.0;
        let q = (self.raw_buffer[self.cursor * 2 + 1] as i8) as f32 / 128.0;

        self.cursor += 1;

        Some(Ok(IqSample::new(i, q)))
    }
}
