{-# LANGUAGE GADTs #-}

-- | Keeps track of the graph of calculation dependencies.
module Byg.Types.Dependency
  ( Action(..)
  , F(..)
  , Dependency(..)
  , makeDependency
  , actionSourceType
  , actionTargetType
  , actionReads
  , actionWrites
  , actionWritesAny
  ) where

import Byg.Types.Token (Token(..))
import Byg.Types.Functions (IsFunctionIO(..))

import Type.Reflection (Typeable, TypeRep, typeRep)

data Action a b where
  Function :: (Typeable a, Typeable b, Show b) => F a b -> Action a b
  FunctionIO :: IsFunctionIO f a b => f -> Action a b
  Inject :: (Typeable a, Show a) => a -> Action () a
  FilterComp :: (Typeable a, Show a) => Action ([a], [Bool]) [a]
  UntupleFst :: (Typeable a, Show a, Typeable b, Show b) => Action (a, b) a
  UntupleSnd :: (Typeable a, Show a, Typeable b, Show b) => Action (a, b) b
  UnzipFst :: (Typeable a, Show a, Typeable b, Show b) => Action [(a, b)] [a]
  UnzipSnd :: (Typeable a, Show a, Typeable b, Show b) => Action [(a, b)] [b]
  MapComp :: (Typeable a, Show a, Typeable b, Show b) => [Dependency] -> Token a -> Token b -> Action [a] [b]

deriving instance Show (Action a b)

newtype F a b = F (a -> b)

instance Show (F a b) where
  show = const "<function>"

data Dependency where
  Dependency :: TypeRep a -> Token a -> Action a b -> TypeRep b -> Token b -> Dependency
deriving instance Show Dependency

makeDependency :: (Typeable a, Show a, Typeable b, Show b) => Token a -> Action a b -> Token b -> Dependency
makeDependency a action b = Dependency typeRep a action typeRep b

actionSourceType :: Typeable a => Action a b -> TypeRep a
actionSourceType _ = typeRep

actionTargetType :: Typeable b => Action a b -> TypeRep b
actionTargetType _ = typeRep

actionReads :: Action a b -> a -> [FilePath]
actionReads = \case
  FunctionIO f -> functionIOReads f
  _ -> const []

actionWrites :: Action a b -> Token a -> Token [FilePath]
actionWrites = \case
  FunctionIO f -> functionIOWrites f
  _ -> const (ListToken [])

actionWritesAny :: Action a b -> Bool
actionWritesAny = \case
  FunctionIO f -> functionIOWritesAny f
  MapComp subDeps _ _ -> any dependencyWritesAny subDeps
  _ -> False
  where dependencyWritesAny :: Dependency -> Bool
        dependencyWritesAny (Dependency _ _ action _ _) = actionWritesAny action
