module Lang.LamIf.Monads where

import FP
import MAAM
import Lang.LamIf.Semantics
import Lang.LamIf.StateSpace
import Lang.LamIf.Pretty ()

-- Path Sensitive
type PSΣ' val   = (ID :.: ListSet) :.: (,) (𝒮 val  )
newtype PSΣ val   a = PSΣ { unPSΣ :: ListSet (a, 𝒮 val  ) }
  deriving (PartialOrder, Bot, Join, JoinLattice, Pretty)
instance Morphism2 (PSΣ val  ) (PSΣ' val  )  where
  morph2 = Compose . Compose . ID . map swap . unPSΣ
instance Morphism2 (PSΣ' val  ) (PSΣ val  ) where
  morph2 = PSΣ . map swap . unID . unCompose . unCompose
instance Isomorphism2 (PSΣ val  ) (PSΣ' val  ) where
instance (TimeC , TimeC ) => Inject (PSΣ val  ) where
  inj = PSΣ . inj . (,bot)

newtype PSΣ𝒫 val   a = PSΣ𝒫 { unPSΣ𝒫 :: Set (a, 𝒮 val  ) }
  deriving (PartialOrder, Bot, Join, JoinLattice, Pretty, Difference)
instance (Ord val, Ord , Ord , Ord a) => Morphism (PSΣ val   a) (PSΣ𝒫 val   a) where
  morph (PSΣ a𝓈s) = PSΣ𝒫 $ toSet $ toList a𝓈s
instance (Ord val, Ord , Ord , Ord a) => Morphism (PSΣ𝒫 val   a) (PSΣ val   a) where
  morph (PSΣ𝒫 a𝓈s) = PSΣ $ fromList $ toList a𝓈s
instance (Ord val, Ord , Ord , Ord a) => Isomorphism (PSΣ val   a) (PSΣ𝒫 val   a)

newtype PS val   a = FSPS 
  { unPS :: IsoMonadStep (PSΣ val  ) (PSΣ' val  ) 
                 (StateT (𝒮 val  ) (ListSetT ID)) a 
  } deriving 
    ( Unit, Functor, Product, Applicative, Bind, Monad
    , MonadBot, MonadPlus
    , MonadState (𝒮 val  )
    , MonadStep (PSΣ val  )
    )
instance (TimeC , TimeC , ValC   val) => Analysis val   (PS val  )
instance (TimeC , TimeC , ValC   val) => Execution (PSΣ val  ) (PSΣ𝒫 val  ) (PS val  )

-- Flow Sensitive Path Insensitive
type FSΣ' val   = (ID :.: ListSet) :.: (,) (𝒮 val  )

newtype FSΣ val   a = FSΣ { unFSΣ :: ListSet (a, 𝒮 val  ) }
  deriving (PartialOrder, Bot, Join, JoinLattice, Pretty)
instance Morphism2 (FSΣ val  ) (FSΣ' val  )  where
  morph2 = Compose . Compose . ID . map swap . unFSΣ
instance Morphism2 (FSΣ' val  ) (FSΣ val  ) where
  morph2 = FSΣ . map swap . unID . unCompose . unCompose
instance Isomorphism2 (FSΣ val  ) (FSΣ' val  ) where
instance (TimeC , TimeC ) => Inject (FSΣ val  ) where
  inj = FSΣ . inj . (,bot)

newtype FSΣ𝒫 val   a = FSΣ𝒫 { unFSΣ𝒫 :: Map a (Set (𝒮Cxt  ), Store val  ) }
  deriving (PartialOrder, Bot, Join, JoinLattice, Pretty, Difference)
instance (Ord val, Join val, Ord , Ord , Ord a) => Morphism (FSΣ val   a) (FSΣ𝒫 val   a) where
  morph = FSΣ𝒫 . toMapJoin . map (mapSnd $ \ (𝒮 cxt σ) -> (single cxt, σ)) . toList . unFSΣ
instance (Ord val, Ord , Ord , Ord a) => Morphism (FSΣ𝒫 val   a) (FSΣ val   a) where
  morph = FSΣ . fromList . toList . joins . map (\ (a, (cxts, σ)) -> setMapOn cxts $ \ cxt -> (a, 𝒮 cxt σ)) . toList . unFSΣ𝒫
instance (Ord val, Join val, Ord , Ord , Ord a) => Isomorphism (FSΣ val   a) (FSΣ𝒫 val   a)

newtype FS val   a = FS 
  { unFS :: IsoMonadStep (FSΣ val  ) (FSΣ' val  ) 
                 (StateT (𝒮 val  ) (ListSetT ID)) a 
  } deriving 
    ( Unit, Functor, Product, Applicative, Bind, Monad
    , MonadBot, MonadPlus
    , MonadState (𝒮 val  )
    , MonadStep (FSΣ val  )
    )
instance (TimeC , TimeC , ValC   val) => Analysis val   (FS val  )
instance (TimeC , TimeC , ValC   val) => Execution (FSΣ val  ) (FSΣ𝒫 val  ) (FS val  )

-- Path Insensitive
instance Morphism (𝒮 val  ) (𝒮Cxt  , Store val  ) where
  morph (𝒮 cxt σ) = (cxt, σ)
instance Morphism (𝒮Cxt  , Store val  ) (𝒮 val  ) where
  morph (cxt, σ) = 𝒮 cxt σ
instance Isomorphism (𝒮 val  ) (𝒮Cxt  , Store val  )

-- Flow Insensitive Path Insensitive
type FIΣ' val   = ((ID :.: (,) (Store val  )) :.: ListSet) :.: (,) (𝒮Cxt  )

newtype FIΣ val   a = FIΣ { unFIΣ :: (ListSet (a, 𝒮Cxt  ), Store val  ) }
  deriving (PartialOrder, Bot, Join, JoinLattice, Pretty)
instance Morphism2 (FIΣ val  ) (FIΣ' val  ) where
  morph2 = Compose . Compose . Compose . ID . mapSnd (map swap) . swap . unFIΣ
instance Morphism2 (FIΣ' val  ) (FIΣ val  ) where
  morph2 = FIΣ . swap . mapSnd (map swap) . unID . unCompose . unCompose . unCompose
instance Isomorphism2 (FIΣ val  ) (FIΣ' val  ) where

newtype FIΣ𝒫 val   a = FIΣ𝒫 { unFIΣ𝒫 :: (Set (a, 𝒮Cxt  ), Store val  ) }
  deriving (PartialOrder, Bot, Join, JoinLattice, Difference, Pretty)
instance (Ord val, Ord , Ord , Ord a) => Morphism (FIΣ val   a) (FIΣ𝒫 val   a) where
  morph (FIΣ (a𝓈s, σ)) = FIΣ𝒫 (toSet $ toList a𝓈s, σ)
instance (Ord val, Ord , Ord , Ord a) => Morphism (FIΣ𝒫 val   a) (FIΣ val   a) where
  morph (FIΣ𝒫 (a𝓈s, σ)) = FIΣ (fromList $ toList a𝓈s, σ)
instance (Ord val, Ord , Ord , Ord a) => Isomorphism (FIΣ val   a) (FIΣ𝒫 val   a)

instance (TimeC , TimeC ) => Inject (FIΣ val  ) where
  inj = FIΣ . (,bot) . inj . (,bot)

newtype FI val   a = FIPI 
  { unFI :: IsoMonadStep (FIΣ val  ) (FIΣ' val  )
                 (AddStateT (𝒮 val  ) (𝒮Cxt  ) (ListSetT (StateT (Store val  ) ID))) a 
  } deriving 
    ( Unit, Functor, Product, Applicative, Bind, Monad
    , MonadBot, MonadPlus
    , MonadState (𝒮 val  )
    , MonadStep (FIΣ val  )
    )
instance (TimeC , TimeC , ValC   val) => Analysis val   (FI val  )
instance (TimeC , TimeC , ValC   val) => Execution (FIΣ val  ) (FIΣ𝒫 val  ) (FI val  )