use crate::{DeltaStruct};
use paste::paste;
use std::{
  collections::{HashMap, HashSet},
  hash::Hash,
};

#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};


// Numbers
macro_rules! impl_num {
  ($($tname:ty),*) => {
    $(paste! {
      #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
      #[allow(non_camel_case_types)]
      pub enum [<$tname Delta>] {
        Set($tname),
        Add($tname),
        Sub($tname),
        Mult($tname),
        Div($tname),
        Mod($tname),
      }

      impl DeltaStruct<[<$tname Delta>]> for $tname {
        fn apply(&mut self, delta : &[<$tname Delta>]) {
          match delta {
            [<$tname Delta>]::Set(x) => { *self = *x }
            [<$tname Delta>]::Add(x) => { *self += *x }
            [<$tname Delta>]::Sub(x) => { *self -= *x }
            [<$tname Delta>]::Mult(x) => { *self *= *x }
            [<$tname Delta>]::Div(x) => { *self /= *x }
            [<$tname Delta>]::Mod(x) => { *self %= *x }
          }
        }
      }
    })*
  }
}

impl_num!(i8, i16, i32, i64, u8, u16, u32, u64, usize, isize, f32, f64);

// bool
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[allow(non_camel_case_types)]
pub enum boolDelta {
  Set(bool),
  Toggle,
}

impl DeltaStruct<boolDelta> for bool {
  fn apply(&mut self, delta : &boolDelta) {
    match delta {
      boolDelta::Set(x) => *self = *x,
      boolDelta::Toggle => *self = !*self,
    }
  }
}

// char
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[allow(non_camel_case_types)]
pub enum charDelta {
  Set(char)
}

impl DeltaStruct<charDelta> for char {
  fn apply(&mut self, delta : &charDelta) {
    match delta {
      charDelta::Set(x) => *self = *x,
    }
  }
}

// String
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum StringDelta {
  Set(String),
  Append(String),
}

impl DeltaStruct<StringDelta> for String {
  fn apply(&mut self, delta : &StringDelta) {
    match delta {
      StringDelta::Set(x) => *self = x.clone(),
      StringDelta::Append(x) => *self += x,
    }
  }
}

// Vecs
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum VecDelta<T>
  where T : Clone 
{
  Set(Vec<T>),
  Push(T),
  Pop(),
}

impl<T> DeltaStruct<VecDelta<T>> for Vec<T>
  where T : Clone 
{
  fn apply(&mut self, delta : &VecDelta<T>) {
    match delta {
      VecDelta::Set(x) => *self = x.clone(),
      VecDelta::Push(x) => self.push(x.clone()),
      VecDelta::Pop() => { self.pop(); },
    }
  }
}

// Option
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum OptionDelta<T>
  where T : Clone
{
  Set(T),
  Clear
}

impl<T> DeltaStruct<OptionDelta<T>> for Option<T>
  where T : Clone
{
  fn apply(&mut self, delta : &OptionDelta<T>) {
    match delta {
      OptionDelta::Set(x) => *self = Some(x.clone()),
      OptionDelta::Clear => *self = None,
    }
  }
}

// HashSet
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum HashSetDelta<T> 
  where T : Clone + Eq + Hash
{
  Set(HashSet<T>),
  Insert(T),
  Remove(T),
}

impl<T> DeltaStruct<HashSetDelta<T>> for HashSet<T>
  where T : Clone + Eq + Hash
{
  fn apply(&mut self, delta : &HashSetDelta<T>) {
    match delta {
      HashSetDelta::Set(x) => *self = x.clone(),
      HashSetDelta::Insert(x) => { self.insert(x.clone()); },
      HashSetDelta::Remove(x) => { self.remove(x); }
    }
  }
}

// HashMap
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum HashMapDelta<T, U> 
  where T : Clone + Eq + Hash,
        U : Clone
{
  Set(HashMap<T, U>),
  Insert(T, U),
  Remove(T),
}

impl<T, U> DeltaStruct<HashMapDelta<T, U>> for HashMap<T, U>
  where T : Clone + Eq + Hash,
        U : Clone
{
  fn apply(&mut self, delta : &HashMapDelta<T, U>) {
    match delta {
      HashMapDelta::Set(x) => *self = x.clone(),
      HashMapDelta::Insert(k, v) => { self.insert(k.clone(), v.clone()); },
      HashMapDelta::Remove(k) => { self.remove(k); }
    }
  }
}