{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}

{-|
  The Cursor is an easy way to reference something in code and query its properties and relationships
  This is the primary way of traversing and querying code
-}
module Clang.Cursor
( isNullCursor
, nullCursor
, getHash
, getKind
, getLinkage
, getAvailability
, getLanguage
, getTranslationUnit
, getSemanticParent
, getLexicalParent
, getOverriddenCursors
, getIncludedFile
, getLocation
, getSpellingLocation
, getExtent
, getType
, getResultType
, getDeclObjCTypeEncoding
, getSpelling
, getSpellingNameRange
, getDisplayName
, getReferenced
, getDefinition
, getCanonicalCursor
, getObjCSelectorIndex
, getReceiverType
, getObjCPropertyAttributes
, getObjCDeclQualifiers
, isObjCOptional
, isVariadic
, getTemplateKind
, getTemplateForSpecialization
, getReferenceNameRange
, getTypeDeclaration
, getNumArguments
, getArgument
, getUSR

-- attribute function
, getIBOutletCollectionType

, isDefinition
, isDeclaration
, isReference
, isExpression
, isStatement
, isAttribute
, isInvalid
, isTranslationUnit
, isPreprocessing
, isUnexposed
, isBitField
, isVirtualBase
, isPureVirtualCppMethod
, isStaticCppMethod
, isVirtualCppMethod
, isDynamicCall
, getCommentRange
, getRawCommentText
, getBriefCommentText
, getParsedComment
, getModule
, getCXXAccessSpecifier
, getOverloadedDecls

-- CursorSet functions
, createSet
, setContains
, setInsert

-- CursorKind functions
, getCursorKindSpelling

-- Platform availability
, getCursorPlatformAvailability

, getCompletionString
) where

import Control.Applicative
import Control.Monad.IO.Class
import GHC.Word

import Clang.Internal.BitFlags
import Clang.Internal.Comment
import qualified Clang.Internal.FFI as FFI
import Clang.Internal.Monad

isNullCursor :: FFI.Cursor s -> Bool
isNullCursor = FFI.cursor_isNull
{-# INLINE isNullCursor #-}

nullCursor :: ClangBase m => ClangT s m (FFI.Cursor s)
nullCursor = FFI.getNullCursor
{-# INLINE nullCursor #-}

getHash :: ClangBase m => FFI.Cursor s' -> ClangT s m GHC.Word.Word32
getHash c = liftIO $ FFI.hashCursor c

getKind :: FFI.Cursor s -> FFI.CursorKind
getKind = FFI.getCursorKind
{-# INLINE getKind #-}

getLinkage :: ClangBase m => FFI.Cursor s' -> ClangT s m FFI.LinkageKind
getLinkage c = liftIO $ FFI.getCursorLinkage c

getAvailability  :: ClangBase m => FFI.Cursor s' -> ClangT s m FFI.AvailabilityKind
getAvailability c = liftIO $ FFI.getCursorAvailability c

getLanguage :: ClangBase m => FFI.Cursor s' -> ClangT s m FFI.LanguageKind
getLanguage c = liftIO $ FFI.getCursorLanguage c

getTranslationUnit :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.TranslationUnit s)
getTranslationUnit = FFI.cursor_getTranslationUnit

getSemanticParent :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Cursor s)
getSemanticParent c = liftIO $ FFI.getCursorSemanticParent mkProxy c

getLexicalParent :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Cursor s)
getLexicalParent c = liftIO $ FFI.getCursorLexicalParent mkProxy c

getOverriddenCursors :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.CursorList s)
getOverriddenCursors = FFI.getOverriddenCursors

getIncludedFile :: ClangBase m => FFI.Cursor s' -> ClangT s m (Maybe (FFI.File s))
getIncludedFile c = liftIO $ FFI.getIncludedFile mkProxy c

getLocation :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.SourceLocation s)
getLocation c = liftIO $ FFI.getCursorLocation mkProxy c

getSpellingLocation :: ClangBase m => FFI.Cursor s'
                    -> ClangT s m (Maybe (FFI.File s), Int, Int, Int)
getSpellingLocation l = liftIO $ FFI.getCursorSpellingLocation mkProxy l

getExtent :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.SourceRange s)
getExtent c = liftIO $ FFI.getCursorExtent mkProxy c

getType :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Type s)
getType c = liftIO $ FFI.getCursorType mkProxy c

getResultType :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Type s)
getResultType c = liftIO $ FFI.getCursorResultType mkProxy c

getDeclObjCTypeEncoding :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.ClangString s)
getDeclObjCTypeEncoding = FFI.getDeclObjCTypeEncoding

getSpelling :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.ClangString s)
getSpelling = FFI.getCursorSpelling

getSpellingNameRange :: ClangBase m => FFI.Cursor s' -> Int -> ClangT s m (FFI.SourceRange s)
getSpellingNameRange c idx = liftIO $ FFI.cursor_getSpellingNameRange mkProxy c idx

getDisplayName :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.ClangString s)
getDisplayName = FFI.getCursorDisplayName

getReferenced :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Cursor s)
getReferenced c = liftIO $ FFI.getCursorReferenced mkProxy c

getDefinition :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Cursor s)
getDefinition c = liftIO $ FFI.getCursorDefinition mkProxy c

getCanonicalCursor :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Cursor s)
getCanonicalCursor c = liftIO $ FFI.getCanonicalCursor mkProxy c

getObjCSelectorIndex :: ClangBase m => FFI.Cursor s' -> ClangT s m Int
getObjCSelectorIndex c = liftIO $ FFI.cursor_getObjCSelectorIndex c

getReceiverType :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Type s)
getReceiverType c = liftIO $ FFI.cursor_getReceiverType mkProxy c

getObjCPropertyAttributes :: ClangBase m => FFI.Cursor s'
                          -> ClangT s m [FFI.ObjCPropertyAttrKind]
getObjCPropertyAttributes c = unFlags <$> liftIO (FFI.cursor_getObjCPropertyAttributes c)

getObjCDeclQualifiers :: ClangBase m => FFI.Cursor s' -> ClangT s m [FFI.ObjCDeclQualifierKind]
getObjCDeclQualifiers c = unFlags <$> liftIO (FFI.cursor_getObjCDeclQualifiers c)

isObjCOptional :: ClangBase m => FFI.Cursor s' -> ClangT s m Bool
isObjCOptional c = liftIO $ FFI.cursor_isObjCOptional c

isVariadic :: ClangBase m => FFI.Cursor s' -> ClangT s m Bool
isVariadic c = liftIO $ FFI.cursor_isVariadic c

getTemplateKind :: ClangBase m => FFI.Cursor s' -> ClangT s m FFI.CursorKind
getTemplateKind c = liftIO $ FFI.getTemplateCursorKind c

getTemplateForSpecialization :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Cursor s)
getTemplateForSpecialization c = liftIO $ FFI.getSpecializedCursorTemplate mkProxy c

getReferenceNameRange :: ClangBase m => FFI.Cursor s' -> [FFI.NameRefFlags] -> Int
                      -> ClangT s m (FFI.SourceRange s)
getReferenceNameRange c fs p = liftIO $ FFI.getCursorReferenceNameRange mkProxy c (orFlags fs) p

getTypeDeclaration :: ClangBase m => FFI.Type s' -> ClangT s m (FFI.Cursor s)
getTypeDeclaration t = liftIO $ FFI.getTypeDeclaration mkProxy t

getNumArguments :: ClangBase m => FFI.Cursor s' -> ClangT s m Int
getNumArguments c = liftIO $ FFI.cursor_getNumArguments c

getArgument :: ClangBase m => FFI.Cursor s' -> Int -> ClangT s m (FFI.Cursor s)
getArgument c i = liftIO $ FFI.cursor_getArgument mkProxy c i

getUSR :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.ClangString s)
getUSR = FFI.getCursorUSR

-- attribute function

getIBOutletCollectionType :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Type s)
getIBOutletCollectionType c = liftIO $ FFI.getIBOutletCollectionType mkProxy c

isDefinition :: ClangBase m => FFI.Cursor s' -> ClangT s m Bool
isDefinition c = liftIO $ FFI.isCursorDefinition c

isDeclaration :: FFI.CursorKind -> Bool
isDeclaration = FFI.isDeclaration
{-# INLINE isDeclaration #-}

isReference :: FFI.CursorKind -> Bool
isReference = FFI.isReference
{-# INLINE isReference #-}

isExpression :: FFI.CursorKind -> Bool
isExpression = FFI.isExpression
{-# INLINE isExpression #-}

isStatement :: FFI.CursorKind -> Bool
isStatement = FFI.isStatement
{-# INLINE isStatement #-}

isAttribute :: FFI.CursorKind -> Bool
isAttribute = FFI.isAttribute
{-# INLINE isAttribute #-}

isInvalid :: FFI.CursorKind -> Bool
isInvalid = FFI.isInvalid
{-# INLINE isInvalid #-}

isTranslationUnit :: FFI.CursorKind -> Bool
isTranslationUnit = FFI.isTranslationUnit
{-# INLINE isTranslationUnit #-}

isPreprocessing :: FFI.CursorKind -> Bool
isPreprocessing = FFI.isPreprocessing
{-# INLINE isPreprocessing #-}

isUnexposed :: FFI.CursorKind -> Bool
isUnexposed = FFI.isUnexposed
{-# INLINE isUnexposed #-}

isBitField :: ClangBase m => FFI.Cursor s' -> ClangT s m Bool
isBitField c = liftIO $ FFI.isBitField c

isVirtualBase :: ClangBase m => FFI.Cursor s' -> ClangT s m Bool
isVirtualBase c = liftIO $ FFI.isVirtualBase c

isPureVirtualCppMethod :: ClangBase m => FFI.Cursor s' -> ClangT s m Bool
isPureVirtualCppMethod c = liftIO $ FFI.cXXMethod_isPureVirtual c

isStaticCppMethod :: ClangBase m => FFI.Cursor s' -> ClangT s m Bool
isStaticCppMethod c = liftIO $ FFI.cXXMethod_isStatic c

isVirtualCppMethod :: ClangBase m => FFI.Cursor s' -> ClangT s m Bool
isVirtualCppMethod c = liftIO $ FFI.cXXMethod_isVirtual c

isDynamicCall :: ClangBase m => FFI.Cursor s' -> ClangT s m Bool
isDynamicCall c = liftIO $ FFI.cursor_isDynamicCall c

getCommentRange :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.SourceRange s)
getCommentRange c = liftIO $ FFI.cursor_getCommentRange mkProxy c

getRawCommentText :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.ClangString s)
getRawCommentText = FFI.cursor_getRawCommentText

getBriefCommentText :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.ClangString s)
getBriefCommentText = FFI.cursor_getBriefCommentText

getParsedComment :: ClangBase m => FFI.Cursor s' -> ClangT s m (Maybe (ParsedComment s))
getParsedComment c = do
  comment <- liftIO $ FFI.cursor_getParsedComment mkProxy c
  parseComment comment

getModule :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.Module s)
getModule c = liftIO $ FFI.cursor_getModule mkProxy c

getCXXAccessSpecifier :: ClangBase m => FFI.Cursor s' -> ClangT s m FFI.CXXAccessSpecifier
getCXXAccessSpecifier c = liftIO $ FFI.getCXXAccessSpecifier c

getOverloadedDecls :: ClangBase m => FFI.Cursor s' -> ClangT s m [FFI.Cursor s]
getOverloadedDecls c = liftIO $ do
  numDecls <- FFI.getNumOverloadedDecls c
  mapM (FFI.getOverloadedDecl mkProxy c) [0..(numDecls - 1)]

-- CursorSet functions

createSet :: ClangBase m => ClangT s m (FFI.CursorSet s)
createSet = FFI.createCXCursorSet

setContains :: ClangBase m => FFI.CursorSet s' -> FFI.Cursor s'' -> ClangT s m Bool
setContains s c = liftIO $ FFI.cXCursorSet_contains s c

setInsert :: ClangBase m => FFI.CursorSet s' -> FFI.Cursor s'' -> ClangT s m Bool
setInsert s c = liftIO $ FFI.cXCursorSet_insert s c


-- CursorKind functions

getCursorKindSpelling :: ClangBase m => FFI.CursorKind -> ClangT s m (FFI.ClangString s)
getCursorKindSpelling = FFI.getCursorKindSpelling

-- Platform availability

getCursorPlatformAvailability :: ClangBase m => FFI.Cursor s'
                              -> ClangT s m (FFI.PlatformAvailabilityInfo s)
getCursorPlatformAvailability = FFI.getCursorPlatformAvailability

-- | Retrieve a completion string for an arbitrary declaration or macro
-- definition cursor.
--
-- Completion strings can be manipulated using the functions in "Clang.Completion".
getCompletionString :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.CompletionString s)
getCompletionString = FFI.getCursorCompletionString
