{-# LANGUAGE DeriveGeneric #-}
module Clingo.Inspection.Symbolic
(
    SymbolicAtoms,
    SymbolicAtom (..),
    AspifLiteral,
    fromSymbolicAtoms,
    fromSymbolicAtomsSig,
    S.symbolicAtomsSignatures
)
where

import Control.DeepSeq
import Control.Monad.IO.Class
import Control.Monad.Catch

import GHC.Generics

import Clingo.Internal.Types
import qualified Clingo.Internal.Inspection.Symbolic as S

import System.IO.Unsafe

data SymbolicAtom s = SymbolicAtom
    { external :: Bool
    , literal  :: AspifLiteral s
    , symbol   :: Symbol s
    , fact     :: Bool
    }
    deriving (Generic)

instance NFData (SymbolicAtom s)

fromSymbolicAtoms :: (MonadIO m, MonadThrow m, NFData a)
                  => SymbolicAtoms s 
                  -> ([SymbolicAtom s] -> a) 
                  -> m a
fromSymbolicAtoms s f = force . f <$> buildSAtoms Nothing s

fromSymbolicAtomsSig :: (MonadIO m, MonadThrow m, NFData a)
                     => SymbolicAtoms s
                     -> Signature s
                     -> ([SymbolicAtom s] -> a)
                     -> m a
fromSymbolicAtomsSig s sig f = force . f <$> buildSAtoms (Just sig) s

getSAtom :: (MonadIO m, MonadThrow m)
         => S.SymbolicAtoms s -> S.SIterator s -> m (SymbolicAtom s)
getSAtom s i = SymbolicAtom
           <$> S.symbolicAtomsIsExternal s i
           <*> S.symbolicAtomsLiteral s i
           <*> S.symbolicAtomsSymbol s i
           <*> S.symbolicAtomsIsFact s i

buildSAtoms :: (MonadIO m, MonadThrow m)
            => Maybe (Signature s) -> S.SymbolicAtoms s -> m [SymbolicAtom s]
buildSAtoms sig s = do
    start <- S.symbolicAtomsBegin s sig
    end   <- S.symbolicAtomsEnd s
    liftIO $ go end start
    where go end i = unsafeInterleaveIO $ do
              abort <- S.symbolicAtomsIteratorEq s end i
              if abort then return []
              else let next = S.symbolicAtomsNext s i
                    in (:) <$> getSAtom s i <*> (go end =<< next)