use anyhow::Context as _;
use windows::Win32::System::Com::VARIANT;
mod com;
pub mod error;
mod initialize;
mod variant_ext;
use com::ComObject;
use initialize::Initialize;
use variant_ext::VariantExt;
pub struct CeVIO {
    _init: Initialize,
    talker: ComObject,
    controller: ComObject,
}
fn make_error_message(method_name: &str, fn_name: &str) -> String {
    format!("Failed to call `{method_name}` in fn `{fn_name}`")
}
impl CeVIO {
    pub fn new() -> error::Result<Self> {
        Ok(Self {
            _init: Initialize::new().map_err(error::CeVIOError)?,
            talker: ComObject::new("CeVIO.Talk.RemoteService2.Talker2")
                .map_err(|e| e.into())
                .map_err(error::CeVIOError)?,
            controller: ComObject::new("CeVIO.Talk.RemoteService2.ServiceControl2")
                .map_err(|e| e.into())
                .map_err(error::CeVIOError)?,
        })
    }
    pub fn new_cevio() -> error::Result<Self> {
        Ok(Self {
            _init: Initialize::new().map_err(error::CeVIOError)?,
            talker: ComObject::new("CeVIO.Talk.RemoteService.Talker")
                .map_err(|e| e.into())
                .map_err(error::CeVIOError)?,
            controller: ComObject::new("CeVIO.Talk.RemoteService.ServiceControl")
                .map_err(|e| e.into())
                .map_err(error::CeVIOError)?,
        })
    }
    pub fn new_cevio_ai() -> error::Result<Self> {
        Ok(Self {
            _init: Initialize::new().map_err(error::CeVIOError)?,
            talker: ComObject::new("CeVIO.Talk.RemoteService2.Talker2")
                .map_err(|e| e.into())
                .map_err(error::CeVIOError)?,
            controller: ComObject::new("CeVIO.Talk.RemoteService2.ServiceControl2")
                .map_err(|e| e.into())
                .map_err(error::CeVIOError)?,
        })
    }
    pub fn start_host(&self, no_wait: bool) -> error::Result<i32> {
        self.controller
            .invoke_method("StartHost", vec![VARIANT::from_bool(no_wait)])
            .with_context(|| make_error_message("invoke_method", "start_host"))
            .map_err(error::CeVIOError)?
            .to_i32()
            .with_context(|| make_error_message("to_i32", "start_host"))
            .map_err(error::CeVIOError)
    }
    pub fn close_host(&self, mode: i32) -> error::Result<()> {
        self.controller
            .invoke_method("CloseHost", vec![VARIANT::from_i32(mode)])
            .with_context(|| make_error_message("invoke_method", "close_host"))
            .map_err(error::CeVIOError)?;
        Ok(())
    }
    pub fn get_host_version(&self) -> error::Result<String> {
        self.controller
            .get_property("HostVersion", None)
            .with_context(|| make_error_message("get_property", "get_host_version"))
            .map_err(error::CeVIOError)?
            .to_string()
            .with_context(|| make_error_message("to_string", "get_host_version"))
            .map_err(error::CeVIOError)
    }
    pub fn get_interface_version(&self) -> error::Result<String> {
        self.controller
            .get_property("InterfaceVersion", None)
            .with_context(|| make_error_message("get_property", "get_interface_version"))
            .map_err(error::CeVIOError)?
            .to_string()
            .with_context(|| make_error_message("to_string", "get_interface_version"))
            .map_err(error::CeVIOError)
    }
    pub fn get_is_host_started(&self) -> error::Result<bool> {
        self.controller
            .get_property("InterfaceVersion", None)
            .with_context(|| make_error_message("get_property", "get_is_host_started"))
            .map_err(error::CeVIOError)?
            .to_bool()
            .with_context(|| make_error_message("to_bool", "get_is_host_started"))
            .map_err(error::CeVIOError)
    }
    pub fn get_volume(&self) -> error::Result<i32> {
        self.talker
            .get_property("Volume", None)
            .with_context(|| make_error_message("get_property", "get_volume"))
            .map_err(error::CeVIOError)?
            .to_i32()
            .with_context(|| make_error_message("to_i32", "get_volume"))
            .map_err(error::CeVIOError)
    }
    pub fn set_volume(&self, volume: i32) -> error::Result<()> {
        self.talker
            .set_property("Volume", None, VARIANT::from_i32(volume))
            .with_context(|| make_error_message("set_property", "set_volume"))
            .map_err(error::CeVIOError)
    }
    pub fn get_speed(&self) -> error::Result<i32> {
        self.talker
            .get_property("Speed", None)
            .with_context(|| make_error_message("get_property", "get_speed"))
            .map_err(error::CeVIOError)?
            .to_i32()
            .with_context(|| make_error_message("to_i32", "get_speed"))
            .map_err(error::CeVIOError)
    }
    pub fn set_speed(&self, speed: i32) -> error::Result<()> {
        self.talker
            .set_property("Speed", None, VARIANT::from_i32(speed))
            .with_context(|| make_error_message("set_property", "set_speed"))
            .map_err(error::CeVIOError)
    }
    pub fn get_tone(&self) -> error::Result<i32> {
        self.talker
            .get_property("Tone", None)
            .with_context(|| make_error_message("get_property", "get_tone"))
            .map_err(error::CeVIOError)?
            .to_i32()
            .with_context(|| make_error_message("to_i32", "get_tone"))
            .map_err(error::CeVIOError)
    }
    pub fn set_tone(&self, tone: i32) -> error::Result<()> {
        self.talker
            .set_property("Tone", None, VARIANT::from_i32(tone))
            .with_context(|| make_error_message("set_property", "set_tone"))
            .map_err(error::CeVIOError)
    }
    pub fn get_tone_scale(&self) -> error::Result<i32> {
        self.talker
            .get_property("ToneScale", None)
            .with_context(|| make_error_message("get_property", "get_tone_scale"))
            .map_err(error::CeVIOError)?
            .to_i32()
            .with_context(|| make_error_message("to_i32", "get_tone_scale"))
            .map_err(error::CeVIOError)
    }
    pub fn set_tone_scale(&self, tone_scale: i32) -> error::Result<()> {
        self.talker
            .set_property("ToneScale", None, VARIANT::from_i32(tone_scale))
            .with_context(|| make_error_message("set_property", "set_tone_scale"))
            .map_err(error::CeVIOError)
    }
    pub fn get_alpha(&self) -> error::Result<i32> {
        self.talker
            .get_property("Alpha", None)
            .with_context(|| make_error_message("get_property", "get_alpha"))
            .map_err(error::CeVIOError)?
            .to_i32()
            .with_context(|| make_error_message("to_i32", "get_alpha"))
            .map_err(error::CeVIOError)
    }
    pub fn set_alpha(&self, alpha: i32) -> error::Result<()> {
        self.talker
            .set_property("Alpha", None, VARIANT::from_i32(alpha))
            .with_context(|| make_error_message("set_property", "set_alpha"))
            .map_err(error::CeVIOError)
    }
    pub fn get_cast(&self) -> error::Result<String> {
        self.talker
            .get_property("Cast", None)
            .with_context(|| make_error_message("get_property", "get_cast"))
            .map_err(error::CeVIOError)?
            .to_string()
            .with_context(|| make_error_message("to_string", "get_cast"))
            .map_err(error::CeVIOError)
    }
    pub fn set_cast(&self, cast: &str) -> error::Result<()> {
        self.talker
            .set_property("Cast", None, VARIANT::from_str(cast))
            .with_context(|| make_error_message("set_property", "set_cast"))
            .map_err(error::CeVIOError)
    }
    pub fn get_available_casts(&self) -> error::Result<String> {
        self.talker
            .get_property("AvailableCasts", None)
            .with_context(|| make_error_message("get_property", "get_available_casts"))
            .map_err(error::CeVIOError)?
            .to_string()
            .with_context(|| make_error_message("to_string", "get_available_casts"))
            .map_err(error::CeVIOError)
    }
    pub fn speak(&self, text: &str) -> error::Result<()> {
        self.talker
            .invoke_method("Speak", vec![VARIANT::from_str(text)])
            .with_context(|| make_error_message("invoke_method", "speak"))
            .map_err(error::CeVIOError)?;
        Ok(())
    }
    pub fn get_phonemes(&self, text: &str) -> error::Result<()> {
        self.talker
            .invoke_method("GetPhonemes", vec![VARIANT::from_str(text)])
            .with_context(|| make_error_message("invoke_method", "get_phonemes"))
            .map_err(error::CeVIOError)?;
        Ok(())
    }
    pub fn output_wave_to_file(&self, text: &str, path: &str) -> error::Result<()> {
        self.talker
            .invoke_method(
                "OutputWaveToFile",
                vec![VARIANT::from_str(text), VARIANT::from_str(path)],
            )
            .with_context(|| make_error_message("invoke_method", "speak"))
            .map_err(error::CeVIOError)?;
        Ok(())
    }
}