use caw_core::*;
use caw_modules::*;
use caw_player::Player;
use rand::Rng;
use std::{
    sync::{
        Arc, RwLock,
        mpsc::{self, Receiver, Sender},
    },
    thread,
};

fn osc(freq: f32) -> Sig<impl SigT<Item = f32>> {
    oscillator(waveform::Saw, freq)
        .reset_offset_01(rand::rng().random::<f32>())
        .build()
}

fn signal(
    thread_index: usize,
    num_threads: usize,
) -> Sig<impl SigT<Item = f32>> {
    let n = 2000;
    let total_oscillators = num_threads * n;
    (0..n)
        .map(move |i| {
            let i = i + (n * thread_index);
            let offset_total = 10.0;
            let offset =
                ((i as f32 / total_oscillators as f32) - 0.5) * offset_total;
            osc(40.0 + offset)
        })
        .sum::<Sig<_>>()
        .map(move |x| (x / (total_oscillators as f32)) * 100.0)
}

struct Query {
    ctx: SigCtx,
}

struct Done;

struct ThreadInfo {
    send_query: Sender<Query>,
    recv_done: Receiver<Done>,
    buf: Arc<RwLock<Vec<f32>>>,
}

fn run_thread(
    index: usize,
    num_threads: usize,
    recv_query: Receiver<Query>,
    send_done: Sender<Done>,
    buf: Arc<RwLock<Vec<f32>>>,
) {
    thread::spawn(move || {
        let mut sig = signal(index, num_threads);
        loop {
            let Query { ctx } = recv_query.recv().unwrap();
            {
                let in_buf = sig.sample(&ctx);
                let mut buf = buf.write().unwrap();
                buf.clear();
                buf.extend(in_buf.iter());
            }
            send_done.send(Done).unwrap();
        }
    });
}

struct MultithreadedSignal {
    thread_info: Vec<ThreadInfo>,
    buf: Vec<f32>,
}

impl MultithreadedSignal {
    fn new(num_threads: usize) -> Self {
        let mut thread_info = Vec::new();
        for i in 0..num_threads {
            let (send_query, recv_query) = mpsc::channel();
            let (send_done, recv_done) = mpsc::channel();
            let buf = Arc::new(RwLock::new(Vec::new()));
            run_thread(i, num_threads, recv_query, send_done, Arc::clone(&buf));
            thread_info.push(ThreadInfo {
                send_query,
                recv_done,
                buf,
            });
        }
        Self {
            thread_info,
            buf: Vec::new(),
        }
    }
}

impl SigT for MultithreadedSignal {
    type Item = f32;

    fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
        for thread_info in &self.thread_info {
            thread_info.send_query.send(Query { ctx: *ctx }).unwrap();
        }
        self.buf.resize(ctx.num_samples, 0.0);
        for thread_info in &self.thread_info {
            let Done = thread_info.recv_done.recv().unwrap();
            let buf = thread_info.buf.read().unwrap();
            for (out, sample) in self.buf.iter_mut().zip(buf.iter()) {
                *out = *sample;
            }
        }
        &self.buf
    }
}

fn main() -> anyhow::Result<()> {
    env_logger::init();
    let player = Player::new()?;
    player.play_signal_sync_mono(
        Sig(MultithreadedSignal::new(20)),
        Default::default(),
    )
}
