#![allow(clippy::derive_partial_eq_without_eq)]
mod connect;
mod data;
mod downstream;
mod e2e;
mod filter;
mod metadata;
mod ping_pong;
mod qos;
mod result;
mod upstream;
pub use connect::*;
pub use data::*;
pub use downstream::*;
pub use e2e::*;
pub use filter::*;
pub use metadata::*;
pub use ping_pong::*;
pub use qos::*;
pub use result::*;
pub use upstream::*;
use std::collections::HashMap;
use crate::error::{Error, Result};
#[derive(Clone, Copy, Eq, PartialEq, Hash, Default, Debug)]
pub struct RequestId(u32);
impl RequestId {
    pub fn new(v: u32) -> Self {
        Self(v)
    }
    pub fn new_as_edge() -> Self {
        Self(0)
    }
    pub fn value(&self) -> u32 {
        self.0
    }
    pub fn set(&mut self, v: u32) {
        self.0 = v
    }
    pub fn increment(&mut self) -> &mut Self {
        self.0 += 2;
        self
    }
}
impl From<u32> for RequestId {
    fn from(v: u32) -> Self {
        Self::new(v)
    }
}
pub use crate::encoding::internal::autogen::DataId;
impl DataId {
    pub fn new<T1: Into<String>, T2: Into<String>>(name: T1, type_: T2) -> Self {
        Self {
            name: name.into(),
            type_: type_.into(),
        }
    }
    pub fn validate(&self) -> Result<()> {
        let list = ["#", "+", ":"];
        for c in list.iter() {
            if self.name.contains(c) || self.type_.contains(c) {
                return Err(Error::invalid_value(format!("cannot use {:?}", list)));
            }
        }
        Ok(())
    }
    pub fn parse_str(data_id: &str) -> Result<Self> {
        let split = data_id.split(':').collect::<Vec<_>>();
        if split.len() != 2 {
            return Err(Error::invalid_value("splitter ':' not found"));
        }
        let id = Self {
            type_: split[0].to_string(),
            name: split[1].to_string(),
        };
        id.validate()?;
        Ok(id)
    }
}
impl std::str::FromStr for DataId {
    type Err = crate::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::parse_str(s)
    }
}
impl std::fmt::Display for DataId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}:{}", self.type_, self.name)
    }
}
pub type DataIdAliasMap = HashMap<u32, DataId>;
#[derive(Clone, PartialEq, Debug)]
pub enum Message {
    ConnectRequest(ConnectRequest),
    ConnectResponse(ConnectResponse),
    Disconnect(Disconnect),
    UpstreamOpenRequest(UpstreamOpenRequest),
    UpstreamOpenResponse(UpstreamOpenResponse),
    UpstreamResumeRequest(UpstreamResumeRequest),
    UpstreamResumeResponse(UpstreamResumeResponse),
    UpstreamCloseRequest(UpstreamCloseRequest),
    UpstreamCloseResponse(UpstreamCloseResponse),
    DownstreamOpenRequest(DownstreamOpenRequest),
    DownstreamOpenResponse(DownstreamOpenResponse),
    DownstreamResumeRequest(DownstreamResumeRequest),
    DownstreamResumeResponse(DownstreamResumeResponse),
    DownstreamCloseRequest(DownstreamCloseRequest),
    DownstreamCloseResponse(DownstreamCloseResponse),
    UpstreamCall(UpstreamCall),
    UpstreamCallAck(UpstreamCallAck),
    DownstreamCall(DownstreamCall),
    Ping(Ping),
    Pong(Pong),
    UpstreamChunk(UpstreamChunk),
    UpstreamChunkAck(UpstreamChunkAck),
    DownstreamChunk(DownstreamChunk),
    DownstreamChunkAck(DownstreamChunkAck),
    DownstreamChunkAckComplete(DownstreamChunkAckComplete),
    UpstreamMetadata(UpstreamMetadata),
    UpstreamMetadataAck(UpstreamMetadataAck),
    DownstreamMetadata(DownstreamMetadata),
    DownstreamMetadataAck(DownstreamMetadataAck),
}
macro_rules! impl_from {
    ($($member:ident,)*) => {
        $(
            impl From<$member> for Message {
                fn from(m: $member) -> Self {
                    Self::$member(m)
                }
            }
            impl TryFrom<Message> for $member {
                type Error = crate::error::Error;
                fn try_from(m: Message) -> core::result::Result<Self, Self::Error> {
                    match m {
                        Message::$member(p) => Ok(p),
                        _ => Err(Error::unexpected("unmatched message")),
                    }
                }
            }
        )*
    }
}
impl_from!(
    ConnectRequest,
    ConnectResponse,
    Disconnect,
    UpstreamOpenRequest,
    UpstreamOpenResponse,
    UpstreamResumeRequest,
    UpstreamResumeResponse,
    UpstreamCloseRequest,
    UpstreamCloseResponse,
    DownstreamOpenRequest,
    DownstreamOpenResponse,
    DownstreamResumeRequest,
    DownstreamResumeResponse,
    DownstreamCloseRequest,
    DownstreamCloseResponse,
    UpstreamCall,
    UpstreamCallAck,
    DownstreamCall,
    Ping,
    Pong,
    UpstreamChunk,
    UpstreamChunkAck,
    DownstreamChunk,
    DownstreamChunkAck,
    DownstreamChunkAckComplete,
    UpstreamMetadata,
    UpstreamMetadataAck,
    DownstreamMetadata,
    DownstreamMetadataAck,
);
macro_rules! impl_error {
    ($($member:ident,)*) => {
        $(
            impl From<$member> for Error {
                fn from(m: $member) -> Error {
                    Error::FailedMessage {
                        code: m.result_code,
                        detail: m.result_string,
                    }
                }
            }
        )*
    }
}
impl_error!(
    ConnectResponse,
    Disconnect,
    UpstreamOpenResponse,
    UpstreamResumeResponse,
    UpstreamCloseResponse,
    UpstreamChunkResult,
    UpstreamMetadataAck,
    DownstreamOpenResponse,
    DownstreamResumeResponse,
    DownstreamCloseResponse,
    DownstreamChunkResult,
    DownstreamMetadataAck,
);
impl Message {
    pub fn request_id(&self) -> Option<RequestId> {
        match &self {
            Self::ConnectRequest(m) => Some(m.request_id),
            Self::ConnectResponse(m) => Some(m.request_id),
            Self::UpstreamOpenRequest(m) => Some(m.request_id),
            Self::UpstreamOpenResponse(m) => Some(m.request_id),
            Self::UpstreamResumeRequest(m) => Some(m.request_id),
            Self::UpstreamResumeResponse(m) => Some(m.request_id),
            Self::UpstreamCloseRequest(m) => Some(m.request_id),
            Self::UpstreamCloseResponse(m) => Some(m.request_id),
            Self::DownstreamOpenRequest(m) => Some(m.request_id),
            Self::DownstreamOpenResponse(m) => Some(m.request_id),
            Self::DownstreamResumeRequest(m) => Some(m.request_id),
            Self::DownstreamResumeResponse(m) => Some(m.request_id),
            Self::DownstreamCloseRequest(m) => Some(m.request_id),
            Self::DownstreamCloseResponse(m) => Some(m.request_id),
            Self::Ping(m) => Some(m.request_id),
            Self::Pong(m) => Some(m.request_id),
            Self::UpstreamMetadata(m) => Some(m.request_id),
            Self::UpstreamMetadataAck(m) => Some(m.request_id),
            Self::DownstreamMetadata(m) => Some(m.request_id),
            Self::DownstreamMetadataAck(m) => Some(m.request_id),
            _ => None,
        }
    }
    pub fn upstream_id_alias(&self) -> Option<u32> {
        match &self {
            Self::UpstreamChunk(m) => Some(m.stream_id_alias),
            Self::UpstreamChunkAck(m) => Some(m.stream_id_alias),
            _ => None,
        }
    }
    pub fn downstream_id_alias(&self) -> Option<u32> {
        match &self {
            Self::DownstreamChunk(m) => Some(m.stream_id_alias),
            Self::DownstreamChunkAck(m) => Some(m.stream_id_alias),
            Self::DownstreamChunkAckComplete(m) => Some(m.stream_id_alias),
            _ => None,
        }
    }
}
#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn request_id_and_stream_id_alias() {
        let requests: Vec<Message> = vec![
            Message::from(ConnectRequest::default()),
            Message::from(ConnectResponse::default()),
            Message::from(UpstreamOpenRequest::default()),
            Message::from(UpstreamOpenResponse::default()),
            Message::from(UpstreamResumeRequest::default()),
            Message::from(UpstreamResumeResponse::default()),
            Message::from(UpstreamCloseRequest::default()),
            Message::from(UpstreamCloseResponse::default()),
            Message::from(DownstreamOpenRequest::default()),
            Message::from(DownstreamOpenResponse::default()),
            Message::from(DownstreamResumeRequest::default()),
            Message::from(DownstreamResumeResponse::default()),
            Message::from(DownstreamCloseRequest::default()),
            Message::from(DownstreamCloseResponse::default()),
            Message::from(Ping::default()),
            Message::from(Pong::default()),
            Message::from(UpstreamMetadata::default()),
            Message::from(UpstreamMetadataAck::default()),
            Message::from(DownstreamMetadata::default()),
            Message::from(DownstreamMetadataAck::default()),
        ];
        requests.into_iter().for_each(|m| {
            m.request_id().expect("exist request id");
            assert!(m.upstream_id_alias().is_none());
            assert!(m.downstream_id_alias().is_none());
        });
        let upstreams: Vec<Message> = vec![
            UpstreamChunk::default().into(),
            UpstreamChunkAck::default().into(),
        ];
        let downstreams: Vec<Message> = vec![
            DownstreamChunk::default().into(),
            DownstreamChunkAck::default().into(),
        ];
        upstreams.into_iter().for_each(|m| {
            assert!(m.upstream_id_alias().is_some());
            assert!(m.downstream_id_alias().is_none());
            assert!(m.request_id().is_none());
        });
        downstreams.into_iter().for_each(|m| {
            assert!(m.upstream_id_alias().is_none());
            assert!(m.downstream_id_alias().is_some());
            assert!(m.request_id().is_none());
        });
    }
    #[test]
    fn data_id_validate() {
        assert!(DataId::new("name", "type").validate().is_ok());
        assert!(DataId::new("na:me", "type").validate().is_err());
        assert!(DataId::new("na+me", "type").validate().is_err());
        assert!(DataId::new("na#me", "type").validate().is_err());
        assert!(DataId::new("name", "ty:pe").validate().is_err());
        assert!(DataId::new("name", "ty+pe").validate().is_err());
        assert!(DataId::new("name", "ty#pe").validate().is_err());
    }
    #[test]
    fn data_id_parse_str() {
        assert_eq!(
            DataId::parse_str("type:name").unwrap(),
            DataId::new("name", "type"),
        );
        assert!(DataId::parse_str("type-name").is_err());
        assert!(DataId::parse_str("ty:pe:name").is_err());
        assert!(DataId::parse_str("ty#pe:name").is_err());
        assert!(DataId::parse_str("type:na+me").is_err());
    }
    #[test]
    fn data_id_format() {
        let id = DataId::parse_str("type:name").unwrap();
        assert_eq!(format!("{}", id), "type:name");
    }
}