//! This module writes and reads roots and weights of gauss-quad into files, using a dedicated file format.
//! The format works as follows: One u64 is written, indicating the n of the roots and weights that follow
//! (thereby hinting at the number of floats that are following before the next u64, which is 2*n). This is repeated for
//! arbitrary n.

#![allow(dead_code)]

use std::{
    fs::File,
    io::{self, BufReader, BufWriter, Read, Seek, SeekFrom, Write},
};

use gauss_quad::GaussLegendre;

/// Writes roots and weights of gauss-quad into a file at `filepath`. params contains roots and weights for multiple n.
pub fn serialize(filepath: &str, params: Vec<(Vec<f64>, Vec<f64>)>) -> io::Result<()> {
    let file = File::create(filepath)?;
    let mut file = BufWriter::new(file);
    for (roots, weights) in params.iter() {
        file.write_all(&u64::try_from(roots.len()).unwrap().to_le_bytes())?;
        for root in roots {
            file.write_all(&root.to_bits().to_le_bytes())?;
        }
        for weight in weights {
            file.write_all(&weight.to_bits().to_le_bytes())?;
        }
    }
    Ok(())
}

pub fn deserialize<R: Read + Seek>(
    reader: &mut R,
    filter: impl Fn(u64) -> bool,
) -> io::Result<Vec<(Vec<f64>, Vec<f64>)>> {
    let mut buf = [0u8; 8];
    let mut values = Vec::new();

    'outer: loop {
        let res = reader.read(&mut buf)?;
        if res < 8 {
            break;
        }
        let n = u64::from_le_bytes(buf);

        // skips stored floats for n that should not be stored
        if !filter(n) {
            reader
                .seek(SeekFrom::Current(i64::try_from(2 * n * 8).unwrap()))
                .unwrap();
            continue;
        }

        // Stores roots and weights into values Vec

        let mut roots = Vec::new();
        for _ in 0..n {
            let res = reader.read(&mut buf)?;
            if res < 8 {
                break 'outer;
            }
            roots.push(f64::from_bits(u64::from_le_bytes(buf)));
        }

        let mut weights = Vec::new();
        for _ in 0..n {
            let res = reader.read(&mut buf)?;
            if res < 8 {
                break 'outer;
            }
            weights.push(f64::from_bits(u64::from_le_bytes(buf)));
        }
        values.push((roots, weights));
    }
    Ok(values)
}

pub fn fill_with_gauss_quad(n_values: Vec<u64>) -> std::io::Result<()> {
    let mut params = Vec::new();
    for i in n_values {
        let gq = GaussLegendre::init(usize::try_from(i).unwrap());
        params.push((gq.nodes, gq.weights));
    }
    serialize("./gauss_quad_lut.morello", params).unwrap();
    let file = File::open("./gauss_quad_lut.morello")?;
    let mut file = BufReader::new(file);
    let res = deserialize(&mut file, |_| true);
    println!("{:?}", res);
    Ok(())
}
