use anyhow::{anyhow, Result};
use psimple::Simple;
use pulse::context::{Context, FlagSet as ContextFlagSet};
use pulse::mainloop::standard::{IterateResult, Mainloop};
use pulse::sample::{Format, Spec};
use pulse::stream::Direction;
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;

const BUF_SIZE: usize = 1024;
const HOP_SIZE: usize = 512;
const SAMPLE_RATE: u32 = 44100;

fn get_pulse_default_sink() -> Result<String> {
    let mainloop = Rc::new(RefCell::new(
        Mainloop::new().ok_or(anyhow!("Failed to create mainloop"))?,
    ));
    let ctx = Rc::new(RefCell::new(
        Context::new(mainloop.borrow().deref(), "gib_default_sink")
            .ok_or(anyhow!("Failed to create context"))?,
    ));

    ctx.borrow_mut()
        .connect(None, ContextFlagSet::NOFLAGS, None)?;

    // Wait for context to be ready
    loop {
        match mainloop.borrow_mut().iterate(false) {
            IterateResult::Quit(_) | IterateResult::Err(_) => {
                return Err(anyhow!("Iterate state was not success"));
            }
            IterateResult::Success(_) => {}
        }

        match ctx.borrow().get_state() {
            pulse::context::State::Ready => {
                break;
            }
            pulse::context::State::Failed | pulse::context::State::Terminated => {
                return Err(anyhow!("Context was in failed/terminated state"));
            }
            _ => {}
        }
    }

    let result = Rc::new(RefCell::new(None));
    let cb_result_ref = result.clone();

    ctx.borrow().introspect().get_server_info(move |info| {
        *cb_result_ref.borrow_mut() = if let Some(sink_name) = info.default_sink_name.clone() {
            Some(Ok(sink_name.to_string()))
        } else {
            Some(Err(()))
        }
    });

    while result.borrow().is_none() {
        match mainloop.borrow_mut().iterate(false) {
            IterateResult::Quit(_) | IterateResult::Err(_) => {
                return Err(anyhow!("Iterate state was not success"));
            }
            IterateResult::Success(_) => {}
        }
    }

    let result = result.borrow().clone();

    result
        .unwrap()
        .map_err(|_| anyhow!("Default sink name was empty"))
}

fn main() -> Result<()> {
    println!("AYAYA");
    let mut default_sink_name = get_pulse_default_sink()?;
    default_sink_name.push_str(".monitor");
    println!(
        "gotted pulse default sink (hopefully): {}",
        default_sink_name
    );

    let spec = Spec {
        format: Format::F32le,
        channels: 1,
        rate: 44100,
    };
    assert!(spec.is_valid());

    let s = Simple::new(
        None,
        "AAAAAAAA",
        Direction::Record,
        Some(&default_sink_name),
        "BBBBBBBB",
        &spec,
        None,
        None,
    )?;

    let mut tempo = aubio::Tempo::new(aubio::OnsetMode::SpecFlux, BUF_SIZE, HOP_SIZE, SAMPLE_RATE)?;
    
    let mut onset = aubio::Onset::new(aubio::OnsetMode::Energy, BUF_SIZE, HOP_SIZE, SAMPLE_RATE)?;

    let mut data = [0u8; 4 * BUF_SIZE];
    let mut float_data = [0f32; BUF_SIZE];

    loop {
        s.read(&mut data)?;

        for (i, f) in float_data.iter_mut().enumerate() {
            let mut float_bytes = [0u8; 4];
            float_bytes.copy_from_slice(&data[4 * i..4 * i + 4]);
            *f = f32::from_le_bytes(float_bytes);
        }

        let r = tempo.do_result(&float_data)?;

        if r > 0f32 {
            println!(
                "{}ms, {}s, frame {}, {} bpm, confidence {}",
                tempo.get_last_ms(),
                tempo.get_last_s(),
                tempo.get_last(),
                tempo.get_bpm(),
                tempo.get_confidence()
            );
        }
        
        /*

        let r = onset.do_result(&float_data)?;

        if r > 0f32 {
            println!("onset i guess: {}", onset.get_last());
        } else {
            println!("nope")
        }

        */
    }
}
