use serde::{de::Deserializer, ser::Serializer};
use specs::{
    error::NoError,
    saveload::{ConvertSaveload, DeserializeComponents, Marker, SerializeComponents, SimpleMarker},
    Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage,
};

use crate::components::{Fleet, FleetOwned, Planet, Player, PlayerOwned, Position, Ship, Velocity};

/* PersistWorld */

pub struct PersistWorld;
pub struct PersistWorldMarker;

impl Persistence for PersistWorld {
    type Marker = SimpleMarker<PersistWorldMarker>;
    type Components = (
        Position,
        Velocity,
        Planet,
        Ship,
        Player,
        PlayerOwned,
        Fleet,
        FleetOwned,
    );
}

/* Persistence */

pub trait Persistence {
    type Marker: Marker;
    type Components: PersistenceComponents<Self::Marker>;

    fn setup(world: &mut World)
    where
        <Self::Marker as Component>::Storage: Default,
        <Self::Marker as Marker>::Allocator: Default,
    {
        world.register::<Self::Marker>();
        world.insert(<Self::Marker as Marker>::Allocator::default());
    }

    fn serialize<S>(&self, world: &World, serializer: S) -> Result<(), S::Error>
    where
        S: Serializer,
    {
        Self::Components::serialize(world, serializer)
    }

    fn deserialize<'de, D>(&self, world: &World, deserializer: D) -> Result<(), D::Error>
    where
        D: Deserializer<'de>,
    {
        Self::Components::deserialize(world, deserializer)
    }
}

/* PersistenceComponents */

pub trait PersistenceComponents<M>
where
    M: Marker,
{
    fn serialize<S>(world: &World, serializer: S) -> Result<(), S::Error>
    where
        S: Serializer;

    fn deserialize<'de, D>(world: &World, deserializer: D) -> Result<(), D::Error>
    where
        D: Deserializer<'de>;
}

macro_rules! define_persistence_components {
    ($($T:ident),*) => {
        #[allow(non_snake_case)]
        impl<M $(,$T)+> PersistenceComponents<M> for ($($T,)+)
        where
            M: Marker,
            M::Allocator: Default,
            $($T: Component + ConvertSaveload<M, Error = NoError>,)+
        {
            fn serialize<S>(world: &World, serializer: S) -> Result<(), S::Error>
            where
                S: Serializer,
            {
                let (
                    entities,
                    mut marker,
                    mut allocator,
                    $($T,)+
                ) = world.system_data::<(
                    Entities,
                    WriteStorage<M>,
                    Write<M::Allocator>,
                    $(ReadStorage<$T>,
                )+)>();

                SerializeComponents::<NoError, M>::serialize_recursive
                    (&($($T,)+),
                    &entities,
                    &mut marker,
                    &mut allocator,
                    serializer,
                )?;

                Ok(())
            }

            fn deserialize<'de, D>(world: &World, deserializer: D) -> Result<(), D::Error>
            where
                D: Deserializer<'de>,
            {
                let (
                    entities,
                    mut marker,
                    mut allocator,
                    $($T,)+
                ) = world.system_data::<(
                    Entities,
                    WriteStorage<M>,
                    Write<M::Allocator>,
                    $(WriteStorage<$T>,)+
                )>();

                DeserializeComponents::<NoError, M>::deserialize(
                    &mut ($($T,)+),
                    &entities,
                    &mut marker,
                    &mut allocator,
                    deserializer,
                )
            }
        }
    }
}

define_persistence_components!(T1);
define_persistence_components!(T1, T2);
define_persistence_components!(T1, T2, T3);
define_persistence_components!(T1, T2, T3, T4);
define_persistence_components!(T1, T2, T3, T4, T5);
define_persistence_components!(T1, T2, T3, T4, T5, T6);
define_persistence_components!(T1, T2, T3, T4, T5, T6, T7);
define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8);
define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
define_persistence_components!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
