//! XML serialization helpers.
//!
//! Contains utilities for Space Engineers XML serialization patterns.

use std::io::{Read, Seek, Write};

use deku::prelude::*;
use deku::bitvec::*;
use deku::bitvec::{AsBits, BitVec, Lsb0};
use deku::ctx::Order;

use flate2::bufread::GzDecoder;
use flate2::write::GzEncoder;
use flate2::Compression;
use quick_xml::de::from_reader;
use quick_xml::se::to_string;

use serde::Serialize;
use serde::de::DeserializeOwned;

use crate::Varint;

/// Deserializes a `Vec<T>` from XML, gracefully handling self-closing elements
/// like `<Members />`.
///
/// Space Engineers serializes empty collections as self-closing XML elements.
/// `quick_xml` treats these as an element with empty text content and tries to
/// deserialize one `T` from it, which fails when `T` has required fields.
///
/// This function uses `deserialize_any` which in quick_xml's `MapValueDeserializer`
/// peeks the next event and dispatches:
/// - `Text` (self-closing) → `deserialize_str` → `visit_str` → empty Vec
/// - `Start` (populated)   → `deserialize_map` → `visit_map` → collect items
pub mod xml_vec {
    use serde::de::{self, Visitor};
    use std::fmt;
    use std::marker::PhantomData;

    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
    where
        T: serde::Deserialize<'de>,
        D: serde::Deserializer<'de>,
    {
        struct VecVisitor<U>(PhantomData<U>);

        impl<'de2, U: serde::Deserialize<'de2>> Visitor<'de2> for VecVisitor<U> {
            type Value = Vec<U>;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("a sequence of elements or an empty/self-closing element")
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
            where
                A: de::MapAccess<'de2>,
            {
                let mut items = Vec::new();
                while let Some(_key) = map.next_key::<de::IgnoredAny>()? {
                    items.push(map.next_value()?);
                }
                Ok(items)
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: de::SeqAccess<'de2>,
            {
                let mut items = Vec::new();
                while let Some(item) = seq.next_element()? {
                    items.push(item);
                }
                Ok(items)
            }

            fn visit_str<E: de::Error>(self, _: &str) -> Result<Self::Value, E> {
                Ok(Vec::new())
            }

            fn visit_string<E: de::Error>(self, _: String) -> Result<Self::Value, E> {
                Ok(Vec::new())
            }

            fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
                Ok(Vec::new())
            }

            fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
                Ok(Vec::new())
            }
        }

        deserializer.deserialize_any(VecVisitor(PhantomData))
    }
}

/// Generates a module containing `serialize` and `deserialize` functions that
/// handle C#'s `[XmlArrayItem("name")]` pattern for `Vec<T>` fields.
///
/// In C# Space Engineers types, a field like:
/// ```csharp
/// [XmlArrayItem("Warning")]
/// public List<string> SuppressedWarnings = new List<string>();
/// ```
/// serializes as:
/// ```xml
/// <SuppressedWarnings>
///   <Warning>value1</Warning>
///   <Warning>value2</Warning>
/// </SuppressedWarnings>
/// ```
///
/// Usage (generated by codegen):
/// ```ignore
/// crate::compat::define_xml_array_item!(Warning);
///
/// #[serde(serialize_with = "Warning::serialize",
///         deserialize_with = "Warning::deserialize")]
/// pub my_field: Vec<String>,
/// ```
///
/// The field type remains `Vec<T>` — no wrapper types, no proto changes.
#[macro_export]
macro_rules! define_xml_array_item {
    ($name:ident) => {
        #[allow(non_snake_case)]
        pub mod $name {
            pub fn serialize<T, S>(vec: &Vec<T>, serializer: S) -> Result<S::Ok, S::Error>
            where
                T: ::serde::Serialize,
                S: ::serde::Serializer,
            {
                use ::serde::ser::SerializeStruct;
                let mut state = serializer.serialize_struct("wrapper", 1)?;
                state.serialize_field(stringify!($name), vec)?;
                state.end()
            }

            pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
            where
                T: ::serde::Deserialize<'de>,
                D: ::serde::Deserializer<'de>,
            {
                use ::serde::de::{self, MapAccess, Visitor};
                use ::std::marker::PhantomData;

                struct ArrayVisitor<U>(PhantomData<U>);

                impl<'de2, U: ::serde::Deserialize<'de2>> Visitor<'de2> for ArrayVisitor<U> {
                    type Value = Vec<U>;

                    fn expecting(
                        &self,
                        formatter: &mut ::std::fmt::Formatter,
                    ) -> ::std::fmt::Result {
                        write!(formatter, "a sequence of <{}> elements", stringify!($name))
                    }

                    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
                    where
                        A: MapAccess<'de2>,
                    {
                        let mut items = Vec::new();
                        while let Some(key) = map.next_key::<String>()? {
                            if key == stringify!($name) {
                                items.push(map.next_value()?);
                            } else {
                                map.next_value::<de::IgnoredAny>()?;
                            }
                        }
                        Ok(items)
                    }

                    fn visit_str<E: de::Error>(self, _: &str) -> Result<Self::Value, E> {
                        Ok(Vec::new())
                    }

                    fn visit_string<E: de::Error>(self, _: String) -> Result<Self::Value, E> {
                        Ok(Vec::new())
                    }

                    fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
                        Ok(Vec::new())
                    }
                }

                deserializer.deserialize_map(ArrayVisitor(PhantomData))
            }
        }
    };
}

#[derive(Clone, Debug, PartialEq)]
#[derive(Default)]
pub struct PacketCompressedXmlObject<T>(pub T);

impl<'a, T> PacketCompressedXmlObject<T> {
    pub fn as_mut(&self) -> &T { &self.0 }
}

impl<'a, T> DekuReader<'a, ()> for PacketCompressedXmlObject<T>
where
    T: DeserializeOwned {
    fn from_reader_with_ctx<R: Read + Seek>(reader: &mut Reader<R>, _ctx: ()) -> Result<Self, DekuError>
    where
        Self: Sized
    {
        let length = Varint::<u32>::from_reader_with_ctx(reader, ())?.0 as usize;

        let mut buffer = vec![0u8; length];
        for slot in buffer.iter_mut() {
            *slot = reader.read_bits(8, Order::Lsb0)?.unwrap().load_le();
        }

        // TODO: This is horrifically inefficient right now.
        let mut output = vec![];
        let result = GzDecoder::new(buffer.as_slice()).read_to_end(&mut output);
        match result {
            Ok(_) => {
                let obj = from_reader(output.as_slice());
                match obj {
                    Ok(obj) => Ok(PacketCompressedXmlObject(obj)),
                    Err(e) => Err(DekuError::Parse(e.to_string().into()))
                }
            },
            Err(e) => {
                println!("{:?}", e);
                Err(DekuError::Io(e.kind()))}
        }
    }
}

impl<T> DekuWriter<()> for PacketCompressedXmlObject<T>
where
    T: Serialize {
    fn to_writer<W: Write + Seek>(&self, writer: &mut Writer<W>, _: ()) -> Result<(), DekuError> {
        let data_str = to_string(&self.0).unwrap();
        // print the string for debugging
        println!("{:?}", data_str);
        let data = data_str.into_bytes();

        let mut output = vec![];
        let result = GzEncoder::new(&mut output, Compression::fast()).write_all(&*data);

        match result {
            Ok(_) => {
                let compressed_size = output.len();
                Varint::from(compressed_size as u32).to_writer(writer, ())?;
                // TODO: This is horrifically inefficient right now.
                let output_data = BitVec::from_iter(output.as_bits::<Lsb0>().iter().rev());
                writer.write_bits_order(&*output_data, Order::Lsb0)?;
                Ok(())
            },
            Err(e) => Err(DekuError::Io(e.kind()))
        }
    }
}