//! Stateful Vivox response payloads that preserve the C# inheritance data.

use crate::{Error, VoiceGatewayResponseType, VoiceGatewayVoiceResponseEventArgs};
use std::ops::Deref;

macro_rules! response_payload {
    ($name:ident { $($field:ident : $type:ty),+ $(,)? }) => {
        #[derive(Clone)]
        pub struct $name {
            pub base: VoiceGatewayVoiceResponseEventArgs,
            $(pub $field: $type,)+
        }

        impl Deref for $name {
            type Target = VoiceGatewayVoiceResponseEventArgs;

            fn deref(&self) -> &Self::Target {
                &self.base
            }
        }
    };
}

response_payload!(VoiceGatewayVoiceAccountEventArgs {
    account_handle_value: String,
});

impl VoiceGatewayVoiceAccountEventArgs {
    pub fn new(rcode: i32, scode: i32, text: String, ahandle: String) -> Result<Self, Error> {
        Ok(Self {
            base: VoiceGatewayVoiceResponseEventArgs::new(
                VoiceGatewayResponseType::AccountLogin,
                rcode,
                scode,
                text,
            )?,
            account_handle_value: ahandle,
        })
    }

    #[must_use]
    pub fn account_handle(&self) -> String {
        self.account_handle_value.clone()
    }
}

response_payload!(VoiceGatewayVoiceSessionEventArgs {
    session_handle: String,
});

impl VoiceGatewayVoiceSessionEventArgs {
    pub fn new(rcode: i32, scode: i32, text: String, shandle: String) -> Result<Self, Error> {
        Ok(Self {
            base: VoiceGatewayVoiceResponseEventArgs::new(
                VoiceGatewayResponseType::SessionCreate,
                rcode,
                scode,
                text,
            )?,
            session_handle: shandle,
        })
    }
}

response_payload!(VoiceGatewayVoiceConnectorEventArgs {
    version_value: String,
    handle_value: String,
});

impl VoiceGatewayVoiceConnectorEventArgs {
    pub fn new(
        rcode: i32,
        scode: i32,
        text: String,
        version: String,
        handle: String,
    ) -> Result<Self, Error> {
        Ok(Self {
            base: VoiceGatewayVoiceResponseEventArgs::new(
                VoiceGatewayResponseType::ConnectorCreate,
                rcode,
                scode,
                text,
            )?,
            version_value: version,
            handle_value: handle,
        })
    }

    #[must_use]
    pub fn handle(&self) -> String {
        self.handle_value.clone()
    }

    #[must_use]
    pub fn version(&self) -> String {
        self.version_value.clone()
    }
}

response_payload!(VoiceGatewayVoiceDevicesEventArgs {
    current_device_value: String,
    devices_value: Vec<String>,
});

impl VoiceGatewayVoiceDevicesEventArgs {
    pub fn new(
        type_: VoiceGatewayResponseType,
        rcode: i32,
        scode: i32,
        text: String,
        current: String,
        avail: Vec<String>,
    ) -> Result<Self, Error> {
        Ok(Self {
            base: VoiceGatewayVoiceResponseEventArgs::new(type_, rcode, scode, text)?,
            current_device_value: current,
            devices_value: avail,
        })
    }

    #[must_use]
    pub fn current_device(&self) -> String {
        self.current_device_value.clone()
    }

    #[must_use]
    pub fn devices(&self) -> Vec<String> {
        self.devices_value.clone()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn derived_response_payloads_retain_base_and_specific_data() {
        let connector = VoiceGatewayVoiceConnectorEventArgs::new(
            3,
            4,
            "status".into(),
            "1.2.3".into(),
            "connector".into(),
        )
        .unwrap();
        assert_eq!(connector.type_, VoiceGatewayResponseType::ConnectorCreate);
        assert_eq!(connector.return_code, 3);
        assert_eq!(connector.status_code, 4);
        assert_eq!(connector.message, "status");
        assert_eq!(connector.version(), "1.2.3");
        assert_eq!(connector.handle(), "connector");

        let devices = VoiceGatewayVoiceDevicesEventArgs::new(
            VoiceGatewayResponseType::GetCaptureDevices,
            0,
            0,
            "OK".into(),
            "default".into(),
            vec!["default".into(), "usb".into()],
        )
        .unwrap();
        assert_eq!(devices.current_device(), "default");
        assert_eq!(devices.devices(), ["default", "usb"]);
    }
}
