content: {- Copyright 2015-2022 NGLess Authors
   - License: MIT
   -}
  
  {-|
   - # Modules
   -
   - Modules add additional functions, constants, or references to ngless
   -
   - See https://ngless.embl.de/modules.html for user information.
   -
   - The data type 'Module' encapsulates all information about a module.
   -}
  
  module Modules
      ( ArgInformation(..)
      , ArgCheck(..)
      , Function(..)
      , FunctionCheck(..)
      , Reference(..)
      , ExternalReference(..)
      , Module(..)
      , ModInfo(..) -- re-export
      , knownModules
      ) where
  
  import qualified Data.Text as T
  import Data.Aeson
  import Data.Default (Default, def)
  
  import Language
  import NGLess
  
  data Reference = Reference
      { refName :: T.Text
      , refAlias :: Maybe T.Text
      , refVersionedName :: T.Text
      , refUrl :: Maybe FilePath
      , refHasGff :: Bool
      , refHasFunctionalMap :: Bool
      } deriving (Eq, Show)
  
  
  -- | Checks for an argument.
  data ArgCheck =
              ArgCheckSymbol [T.Text] -- ^ for symbols, list allowed values
              | ArgCheckFileReadable -- ^ file should be readable
              | ArgCheckFileWritable -- ^ file should be writeable (i.e., this is an output file)
              | ArgCheckMinVersion (Int, Int) -- ^ first version where this argument can be used
              | ArgDeprecated (Int, Int) T.Text -- ^ deprecated since given version
              deriving (Eq,Show)
  
  -- | Checks for a function
  data FunctionCheck =
              FunctionCheckMinNGLessVersion (Int, Int) -- ^ first version where this function can be used
              | FunctionCheckReturnAssigned -- ^ Function is pure
              | FunctionCheckNGLVersionIncompatibleChange (Int, Int)-- ^ version when behaviour changed
                              T.Text -- ^ reason for change
              deriving (Eq, Show)
  
  -- | Basic information about argument to a function
  data ArgInformation = ArgInformation
      { argName :: !T.Text -- ^ argument name
      , argRequired :: !Bool -- ^ whether it is required
      , argType :: !NGLType -- ^  type
      , argChecks :: [ArgCheck] -- ^ what checks should be performed on the argument
      } deriving (Eq, Show)
  
  
  -- | This represents an ngless function
  data Function = Function
      { funcName :: FuncName -- ^ name of function
      , funcArgType :: Maybe NGLType -- ^ if it takes an unnamed argument, what is its type
      , funcArgChecks :: [ArgCheck] -- ^ checks for first argument
      , funcRetType :: NGLType -- ^ what type it returns
      , funcKwArgs :: [ArgInformation] -- ^ what are the keyword arguments
      , funcAllowsAutoComprehension :: Bool -- ^ if true, then calling this function with [funcArgType] should return [funcRetType]
      , funcChecks :: [FunctionCheck]
      } deriving (Eq, Show)
  
  data ExternalReference = ExternalReference
          { erefName :: T.Text
          , faFile :: FilePath
          , gtfFile :: Maybe FilePath
          , geneMapFile :: Maybe FilePath
          }
          | ExternalPackagedReference
          { refData :: Reference
          }
      deriving (Eq, Show)
  
  instance FromJSON ExternalReference where
      parseJSON = withObject "external reference" $ \o -> do
                      rtype <- o .:? "rtype"
                      case (rtype :: Maybe String) of
                          Just "packaged" -> do
                              name <- o .: "name"
                              vname <- o .: "name-version"
                              path <- o .: "url"
                              hasGtf <- o .: "has-gtf"
                              hasMap <- o .: "has-mapfile"
                              return (ExternalPackagedReference (Reference name Nothing vname (Just path) hasGtf hasMap))
                          _ -> ExternalReference
                                  <$> o .: "name"
                                  <*> o .: "fasta-file"
                                  <*> o .:? "gtf-file"
                                  <*> o .:? "map-file"
  
  
  -- | This represents a module (i.e., something which is imported by ngless)
  data Module = Module
      { modInfo :: !ModInfo -- ^ name & version
      , modPath :: FilePath
      , modCitations :: [T.Text]
      , modConstants :: [(T.Text, NGLessObject)] -- ^ constants defined in this module
      , modReferences :: [ExternalReference]
      , modFunctions :: [Function] -- ^ functions defined by this module
      , modTransform :: [(Int, Expression)] -> NGLessIO [(Int, Expression)] -- ^ this is called before any interpretation. It can also perform validation by throwing exceptions on error
      , runFunction :: T.Text -> NGLessObject -> KwArgsValues -> NGLessIO NGLessObject -- ^ run a function defined by the module. The first argument is the name of the function
      }
  
  instance Show Module where
      show (Module info p _ cs fs _ _ _) = "Module["++show info++" ("++p++"); constants="++show cs++"; functions="++show fs++"]"
  
  instance Default Module where
      def = Module
          { modInfo = ModInfo "builtin.default" "0.0"
          , modPath = "<internal>"
          , modCitations = []
          , modConstants = []
          , modReferences = []
          , modFunctions = []
          , modTransform = return
          , runFunction = \_ _ _ -> return NGOVoid
          }
  
  
  
  knownModules :: [T.Text]
  knownModules =
      ["example-cmd"
  
      ,"gmgc"
  
      ,"igc"
      ,"om-rgc"
      ,"DogGutCatalog"
      ,"MouseGutCatalog"
      ,"PigGutCatalog"
  
      ,"specI"
      ,"motus"
      ]
  
  