use obipipeline::{WorkerPool, make_pipeline};
use std::io::Error;

// ── Itérateur source ──────────────────────────────────────────────────────────

struct Compteur {
    count: u64,
    stop: u64,
}

impl Compteur {
    fn new(start: u64, stop: u64) -> Self {
        Self { count: start, stop }
    }
}

impl Iterator for Compteur {
    type Item = u64;

    fn next(&mut self) -> Option<Self::Item> {
        if self.count > self.stop {
            return None;
        }
        let result = self.count;
        self.count += 1;
        Some(result)
    }
}

// ── Fonctions de transformation ───────────────────────────────────────────────

fn to_f64(x: u64) -> f64 {
    x as f64
}

fn step_1(number: f64) -> String {
    format!("{}", number)
}

fn step_2(text: String) -> String {
    text.chars().rev().collect()
}

fn step_3(text: String) -> u64 {
    let mut hash: u64 = 5381;
    for byte in text.bytes() {
        hash = hash.wrapping_mul(33).wrapping_add(byte as u64);
    }
    hash
}

fn step_4(hash: u64) -> Result<(), Error> {
    println!("{}", hash);
    Ok(())
}

// ── Type de données du pipeline ───────────────────────────────────────────────

enum PipelineData {
    Unsigned(u64),
    Number(f64),
    Text(String),
}

// ── Main ──────────────────────────────────────────────────────────────────────

fn main() {
    let pipeline = make_pipeline! {
        PipelineData,
        source  Compteur::new(1000, 1010) => Unsigned,
        | to_f64: Unsigned => Number,
        | step_1: Number   => Text,
        | step_2: Text     => Text,
        | step_3: Text     => Unsigned,
        sink?   step_4     @ Unsigned,
    };

    WorkerPool::new(pipeline, 4, 64).run();
}
