{-# language FlexibleContexts #-}
{-# language RankNTypes #-}
{-# language StandaloneDeriving #-}
{-# language TemplateHaskell #-}
{-# language UndecidableInstances #-}
module Rock.Traces where

import Control.Monad.IO.Class
import Data.Constraint.Extras
import Data.Dependent.HashMap(DHashMap)
import qualified Data.Dependent.HashMap as DHashMap
import Data.Dependent.Sum
import Data.Functor.Classes
import Data.GADT.Compare
import Data.GADT.Show
import Data.Hashable
import Data.Some
import Text.Show.Deriving

data ValueDeps f dep a = ValueDeps
  { value :: !a
  , dependencies :: !(DHashMap f dep)
  }

return []

deriving instance (Show a, GShow f, Has' Show f dep) => Show (ValueDeps f dep a)

instance (GShow f, Has' Show f dep) => Show1 (ValueDeps f dep) where
  liftShowsPrec = $(makeLiftShowsPrec ''ValueDeps)

type Traces f dep = DHashMap f (ValueDeps f dep)

verifyDependencies
  :: (MonadIO m, GEq f, Has' Eq f dep)
  => (forall a'. f a' -> m a')
  -> (forall a'. f a' -> a' -> m (dep a'))
  -> ValueDeps f dep a
  -> m (Maybe a)
verifyDependencies fetch createDependencyRecord (ValueDeps value_ deps) = do
  upToDate <- allM (DHashMap.toList deps) $ \(depKey :=> dep) -> do
    depValue <- fetch depKey
    newDep <- createDependencyRecord depKey depValue
    return $ eqTagged depKey depKey dep newDep
  return $ if upToDate
    then Just value_
    else Nothing
  where
    allM :: Monad m => [a] -> (a -> m Bool) -> m Bool
    allM [] _ = return True
    allM (x:xs) p = do
      b <- p x
      if b then
        allM xs p
      else
        return False

record
  :: (GEq f, Hashable (Some f))
  => f a
  -> a
  -> DHashMap f g
  -> Traces f g
  -> Traces f g
record k v deps
  = DHashMap.insert k
  $ ValueDeps v deps