module Language.Fortran.Util.SecondParameter(SecondParameter(..)) where
import GHC.Generics
class SecondParameter a e | a -> e where
  getSecondParameter :: a -> e
  setSecondParameter :: e -> a -> a
  default getSecondParameter :: (Generic a, GSecondParameter (Rep a) e) => a -> e
  getSecondParameter a = getSecondParameter' . from $ a
  default setSecondParameter :: (Generic a, GSecondParameter (Rep a) e) => e -> a -> a
  setSecondParameter e a = to . setSecondParameter' e . from $ a
class GSecondParameter f e where
  getSecondParameter' :: f a -> e
  setSecondParameter' :: e -> f a -> f a
instance GSecondParameter (K1 i a) e where
  getSecondParameter' _ = undefined
  setSecondParameter' _ = undefined
instance GSecondParameter a e => GSecondParameter (M1 i c a) e where
  getSecondParameter' (M1 x) = getSecondParameter' x
  setSecondParameter' e (M1 x) = M1 $ setSecondParameter' e x
instance (GSecondParameter a e, GSecondParameter b e) => GSecondParameter (a :+: b) e where
  getSecondParameter' (L1 a) = getSecondParameter' a
  getSecondParameter' (R1 a) = getSecondParameter' a
  setSecondParameter' e (L1 a) = L1 $ setSecondParameter' e a
  setSecondParameter' e (R1 a) = R1 $ setSecondParameter' e a
instance (ParameterLeaf a, GSecondParameter a e, GSecondParameter' b e) => GSecondParameter (a :*: b) e where
  getSecondParameter' (a :*: b) = 
    if isLeaf a 
    then getSecondParameter'' b
    else getSecondParameter' a
  setSecondParameter' e (a :*: b) = 
    if isLeaf a 
    then a :*: setSecondParameter'' e b
    else setSecondParameter' e a :*: b
class GSecondParameter' f e where
  getSecondParameter'' :: f a -> e
  setSecondParameter'' :: e -> f a -> f a
instance GSecondParameter' a e => GSecondParameter' (M1 i c a) e where
  getSecondParameter'' (M1 a) = getSecondParameter'' a
  setSecondParameter'' e (M1 a) = M1 $ setSecondParameter'' e a
instance GSecondParameter' a e => GSecondParameter' (a :*: b) e where
  getSecondParameter'' (a :*: _) = getSecondParameter'' a
  setSecondParameter'' e (a :*: b) = setSecondParameter'' e a :*: b
instance  GSecondParameter' (K1 i e) e where
  getSecondParameter'' (K1 a) = a
  setSecondParameter'' e (K1 a) = K1 e
instance  GSecondParameter' (K1 i a) e where
  getSecondParameter'' _ = undefined
  setSecondParameter'' _ _  = undefined
class ParameterLeaf f where
  isLeaf :: f a -> Bool
instance ParameterLeaf (M1 i c a) where
  isLeaf _ = True
instance ParameterLeaf (a :*: b) where
  isLeaf _ = False