module Clingo.AST
(
    parseProgram,
    fromPureAST,
    toPureAST,

    T.Location (..),
    Sign (..),
    T.Signature,
    T.Symbol,
    UnaryOperation (..),
    UnaryOperator (..),
    BinaryOperation (..),
    BinaryOperator (..),
    Interval (..),
    Function (..),
    Pool (..),
    Term (..),
    CspProductTerm (..),
    CspSumTerm (..),
    CspGuard (..),
    ComparisonOperator (..),
    CspLiteral (..),
    Identifier (..),
    Comparison (..),
    Literal (..),
    AggregateGuard (..),
    ConditionalLiteral (..),
    Aggregate (..),
    BodyAggregateElement (..),
    BodyAggregate (..),
    AggregateFunction (..),
    HeadAggregateElement (..),
    HeadAggregate (..),
    Disjunction (..),
    DisjointElement (..),
    Disjoint (..),
    TheoryTermArray (..),
    TheoryFunction (..),
    TheoryUnparsedTermElement (..),
    TheoryUnparsedTerm (..),
    TheoryTerm (..),
    TheoryAtomElement (..),
    TheoryGuard (..),
    TheoryAtom (..),
    HeadLiteral (..),
    BodyLiteral (..),
    TheoryOperatorDefinition (..),
    TheoryOperatorType (..),
    TheoryTermDefinition (..),
    TheoryGuardDefinition (..),
    TheoryAtomDefinition (..),
    TheoryAtomDefinitionType (..),
    TheoryDefinition (..),
    Rule (..),
    Definition (..),
    ShowSignature (..),
    ShowTerm (..),
    Minimize (..),
    Script (..),
    ScriptType (..),
    Program (..),
    External (..),
    Edge (..),
    Heuristic (..),
    Project (..),
    Statement (..)
)
where

import Control.Monad.IO.Class

import Data.Bifunctor
import Data.Bitraversable
import Data.Text (Text, unpack)
import Data.IORef
import Numeric.Natural

import Foreign hiding (Pool)
import Foreign.C

import Clingo.Symbol
import Clingo.Internal.AST
import Clingo.Internal.Utils
import qualified Clingo.Internal.Types as T
import qualified Clingo.Raw as Raw

-- | Parse a logic program into a list of statements.
parseProgram :: Text                                    -- ^ Program
             -> Maybe (ClingoWarning -> Text -> IO ())  -- ^ Logger Callback
             -> Natural                                 -- ^ Logger Call Limit
             -> T.Clingo s [Statement (T.Symbol s) (T.Signature s)]
parseProgram prog logger limit = do
    ref <- liftIO (newIORef [])
    marshall0 $
        withCString (unpack prog) $ \p -> do
            logCB <- maybe (pure nullFunPtr) T.wrapCBLogger logger
            astCB <- wrapCBAst (\s -> modifyIORef ref (s :))
            Raw.parseProgram p astCB nullPtr 
                               logCB nullPtr (fromIntegral limit)
    liftIO (reverse <$> readIORef ref)

-- | An AST can be constructed in a pure environment using 'PureSymbol' and
-- 'PureSignature' and then registered with the solver when required. Creation
-- calls for the same symbol in multiple places will be repeated, i.e. no symbol
-- table is being created internally by this function!
fromPureAST :: (Monad (m s), MonadSymbol m)
            => [Statement PureSymbol PureSignature]
            -> m s [Statement (T.Symbol s) (T.Signature s)]
fromPureAST = traverse (bitraverse unpureSymbol unpureSignature)

toPureAST :: [Statement (T.Symbol s) (T.Signature s)]
          -> [Statement PureSymbol PureSignature]
toPureAST = fmap (bimap toPureSymbol toPureSignature)

wrapCBAst :: MonadIO m
          => (Statement (T.Symbol s) (T.Signature s) -> IO ())
          -> m (FunPtr (Ptr Raw.AstStatement -> Ptr () -> IO Raw.CBool))
wrapCBAst f = liftIO $ Raw.mkCallbackAst go
    where go :: Ptr Raw.AstStatement -> Ptr () -> IO Raw.CBool
          go stmt _ = reraiseIO $ f =<< fromRawStatement =<< peek stmt