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;

/*
    Some manual poking around in PulseAudio to get the name of the default sink
*/

fn poll_mainloop(mainloop: &Rc<RefCell<Mainloop>>) -> Result<()> {
    match mainloop.borrow_mut().iterate(true) {
        IterateResult::Quit(_) | IterateResult::Err(_) => {
            return Err(anyhow!("Iterate state was not success"));
        }
        IterateResult::Success(_) => {
            return Ok(());
        }
    }
}

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 {
        poll_mainloop(&mainloop)?;

        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(ref sink_name) = info.default_sink_name {
            Some(Ok(sink_name.to_string()))
        } else {
            Some(Err(()))
        }
    });

    loop {
        if let Some(result) = result.borrow().deref() {
            return result
                .to_owned()
                .map_err(|_| anyhow!("Default sink name was empty"));
        }

        poll_mainloop(&mainloop)?;
    }
}

/*
    Get a PASimple instance which reads from the default sink monitor
*/

pub fn get_audio_reader(spec: &Spec) -> Result<Simple> {
    let mut default_sink_name = get_pulse_default_sink()?;
    default_sink_name.push_str(".monitor");

    let simple = Simple::new(
        None,
        "piano_thingy",
        Direction::Record,
        Some(&default_sink_name),
        "sample_yoinker",
        &spec,
        None,
        None,
    )?;

    Ok(simple)
}
