use super::Serializer;
use crate::buf_ext::BufMutExt;
use crate::tags::Tag;
use crate::{AsUnknownFields, Serialize, SerializeError, UnknownFieldsRef, ValueKind};
use bytes::BytesMut;

#[derive(Debug)]
pub struct Struct1Serializer<'a> {
    buf: &'a mut BytesMut,
    num_fields: u32,
    depth: u8,
}

impl<'a> Struct1Serializer<'a> {
    pub(super) fn new(
        buf: &'a mut BytesMut,
        num_fields: usize,
        depth: u8,
    ) -> Result<Self, SerializeError> {
        if num_fields <= u32::MAX as usize {
            buf.put_discriminant_u8(ValueKind::Struct1);
            buf.put_varint_u32_le(num_fields as u32);

            Ok(Self {
                buf,
                num_fields: num_fields as u32,
                depth,
            })
        } else {
            Err(SerializeError::Overflow)
        }
    }

    pub(super) fn with_unknown_fields<T>(
        buf: &'a mut BytesMut,
        num_fields: usize,
        unknown_fields: T,
        depth: u8,
    ) -> Result<Self, SerializeError>
    where
        T: AsUnknownFields,
        T::FieldsIter: ExactSizeIterator,
    {
        let unknown_fields = unknown_fields.fields();
        let mut this = Self::new(buf, num_fields + unknown_fields.len(), depth)?;
        this.serialize_unknown_fields(UnknownFieldsRef(unknown_fields))?;
        Ok(this)
    }

    pub fn remaining_fields(&self) -> usize {
        self.num_fields as usize
    }

    pub fn requires_additional_fields(&self) -> bool {
        self.num_fields > 0
    }

    pub fn serialize<T: Tag>(
        &mut self,
        id: impl Into<u32>,
        value: impl Serialize<T>,
    ) -> Result<&mut Self, SerializeError> {
        if self.num_fields > 0 {
            self.num_fields -= 1;

            self.buf.put_varint_u32_le(id.into());

            let serializer = Serializer::new(self.buf, self.depth)?;
            serializer.serialize(value)?;

            Ok(self)
        } else {
            Err(SerializeError::TooManyElements)
        }
    }

    pub fn serialize_if_some<T: Tag>(
        &mut self,
        id: impl Into<u32>,
        value: impl Serialize<T>,
    ) -> Result<&mut Self, SerializeError> {
        if value.serializes_as_some() {
            self.serialize(id, value)
        } else {
            Ok(self)
        }
    }

    pub fn serialize_unknown_fields(
        &mut self,
        unknown_fields: impl AsUnknownFields,
    ) -> Result<&mut Self, SerializeError> {
        for (id, value) in unknown_fields.fields() {
            if self.num_fields == 0 {
                return Err(SerializeError::TooManyElements);
            }

            self.num_fields -= 1;
            self.buf.put_varint_u32_le(id);

            let serializer = Serializer::new(self.buf, self.depth)?;
            serializer.serialize(value)?;
        }

        Ok(self)
    }

    pub fn finish(self) -> Result<(), SerializeError> {
        if self.num_fields == 0 {
            Ok(())
        } else {
            Err(SerializeError::TooFewElements)
        }
    }
}

#[derive(Debug)]
pub struct Struct2Serializer<'a> {
    buf: &'a mut BytesMut,
    depth: u8,
}

impl<'a> Struct2Serializer<'a> {
    pub(super) fn new(buf: &'a mut BytesMut, depth: u8) -> Result<Self, SerializeError> {
        buf.put_discriminant_u8(ValueKind::Struct2);
        Ok(Self { buf, depth })
    }

    pub(super) fn with_unknown_fields(
        buf: &'a mut BytesMut,
        unknown_fields: impl AsUnknownFields,
        depth: u8,
    ) -> Result<Self, SerializeError> {
        let mut this = Self::new(buf, depth)?;
        this.serialize_unknown_fields(unknown_fields)?;
        Ok(this)
    }

    pub fn serialize<T: Tag>(
        &mut self,
        id: impl Into<u32>,
        value: impl Serialize<T>,
    ) -> Result<&mut Self, SerializeError> {
        self.buf.put_discriminant_u8(ValueKind::Some);
        self.buf.put_varint_u32_le(id.into());

        let serializer = Serializer::new(self.buf, self.depth)?;
        serializer.serialize(value)?;

        Ok(self)
    }

    pub fn serialize_if_some<T: Tag>(
        &mut self,
        id: impl Into<u32>,
        value: impl Serialize<T>,
    ) -> Result<&mut Self, SerializeError> {
        if value.serializes_as_some() {
            self.serialize(id, value)
        } else {
            Ok(self)
        }
    }

    pub fn serialize_unknown_fields(
        &mut self,
        unknown_fields: impl AsUnknownFields,
    ) -> Result<&mut Self, SerializeError> {
        for (id, value) in unknown_fields.fields() {
            self.serialize(id, value)?;
        }

        Ok(self)
    }

    pub fn finish(self) -> Result<(), SerializeError> {
        self.buf.put_discriminant_u8(ValueKind::None);
        Ok(())
    }
}
