{-# LANGUAGE GADTs, RankNTypes #-}
-- | Define types and functions related to creating a system information database.

-- Changes to System should be reflected in the 'Creating Your Project in
-- Drasil' tutorial found on the wiki:
-- https://github.com/JacquesCarette/Drasil/wiki/Creating-Your-Project-in-Drasil
module Drasil.System.SmithEtAlSRS (
  -- * System
  -- ** Types
  SmithEtAlSRS(..),
  Purpose, Background, Scope, Motivation,
  -- ** Lenses
  HasSmithEtAlSRS(..),
  -- ** Constructors
  mkSmithEtAlICO,
  -- ** Hacks
  refbyLookup, traceLookup
) where

import Control.Lens (makeClassy, (^.))
import Data.Char (isSpace)
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe)

import Drasil.Database (UID, HasUID(..), ChunkDB)
import Language.Drasil (Quantity, MayHaveUnit, Concept, Reference, People, CI,
  Constrained, ConstQDef, abrv, LabelledContent, DefinedQuantityDict)
import Theory.Drasil (TheoryModel, GenDefn, DataDefinition, InstanceModel)
import Utils.Drasil (toPlainName)

import Drasil.System.Core (SystemMeta, Background, HasSystemMeta(..),
  mkSystemMeta, Motivation, Purpose, Scope)

-- | Data structure for holding all of the requisite information about a system
-- to be used in artifact generation.
data SmithEtAlSRS where
 ICO :: (Quantity h, MayHaveUnit h, Concept h,
  Quantity i, MayHaveUnit i, Concept i,
  HasUID j, Constrained j) =>
  { SmithEtAlSRS -> SystemMeta
_meta         :: SystemMeta
  , SmithEtAlSRS -> String
_programName  :: String
  , SmithEtAlSRS -> [TheoryModel]
_theoryModels :: [TheoryModel]
  , SmithEtAlSRS -> [GenDefn]
_genDefns     :: [GenDefn]
  , SmithEtAlSRS -> [DataDefinition]
_dataDefns    :: [DataDefinition]
  , SmithEtAlSRS -> [InstanceModel]
_instModels   :: [InstanceModel]
  , ()
_inputs       :: [h]
  , ()
_outputs      :: [i]
  , ()
_constraints  :: [j]
  , SmithEtAlSRS -> [ConstQDef]
_constants    :: [ConstQDef]
  -- FIXME: This is a list of all 'quantites' (variables) used/referenced in an
  -- SRS. Why is this here? For type-checking the SRS later. Should
  -- type-checking be done on the SRS level? No. This is a temporary hack.
  , SmithEtAlSRS -> [DefinedQuantityDict]
_quantities   :: [DefinedQuantityDict]
  -- FIXME: This is a list of all labelled content required for the SRS to be
  -- generated. In particular, this is needed for the mdBook generator which
  -- _must_ export a CSV containing a list of all external resources that the
  -- mdBook compiler is allowed to access. This list should be re-written as
  -- part of a stateful renderer for the SRS instead.
  , SmithEtAlSRS -> [LabelledContent]
_lbldCntnt    :: [LabelledContent]
  -- FIXME: Hacks to be removed once 'Reference's are rebuilt.
  , SmithEtAlSRS -> Map UID Reference
_refTable     :: M.Map UID Reference
  , SmithEtAlSRS -> Map UID [UID]
_refbyTable   :: M.Map UID [UID]
  , SmithEtAlSRS -> Map UID [UID]
_traceTable   :: M.Map UID [UID]
  } -> SmithEtAlSRS

makeClassy ''SmithEtAlSRS

instance HasSystemMeta SmithEtAlSRS where
  systemMeta :: Lens' SmithEtAlSRS SystemMeta
systemMeta = (SystemMeta -> f SystemMeta) -> SmithEtAlSRS -> f SmithEtAlSRS
forall c. HasSmithEtAlSRS c => Lens' c SystemMeta
Lens' SmithEtAlSRS SystemMeta
meta

-- | Build a 'System'.
mkSmithEtAlICO :: (Quantity h, MayHaveUnit h, Concept h,
  Quantity i, MayHaveUnit i, Concept i,
  HasUID j, Constrained j) =>
  CI -> People -> Purpose -> Background -> Scope -> Motivation ->
    [TheoryModel] -> [GenDefn] -> [DataDefinition] -> [InstanceModel] ->
    [h] -> [i] -> [j] -> [ConstQDef] -> [DefinedQuantityDict] ->
    [LabelledContent] -> ChunkDB -> [Reference] -> SmithEtAlSRS
mkSmithEtAlICO :: forall h i j.
(Quantity h, MayHaveUnit h, Concept h, Quantity i, MayHaveUnit i,
 Concept i, HasUID j, Constrained j) =>
CI
-> People
-> Background
-> Background
-> Background
-> Background
-> [TheoryModel]
-> [GenDefn]
-> [DataDefinition]
-> [InstanceModel]
-> [h]
-> [i]
-> [j]
-> [ConstQDef]
-> [DefinedQuantityDict]
-> [LabelledContent]
-> ChunkDB
-> [Reference]
-> SmithEtAlSRS
mkSmithEtAlICO CI
nm People
ppl Background
prps Background
bkgrd Background
scp Background
motive [TheoryModel]
tms [GenDefn]
gds [DataDefinition]
dds [InstanceModel]
ims [h]
hs [i]
is [j]
js [ConstQDef]
cqds [DefinedQuantityDict]
qs [LabelledContent]
lcs ChunkDB
db [Reference]
refs
  = SystemMeta
-> String
-> [TheoryModel]
-> [GenDefn]
-> [DataDefinition]
-> [InstanceModel]
-> [h]
-> [i]
-> [j]
-> [ConstQDef]
-> [DefinedQuantityDict]
-> [LabelledContent]
-> Map UID Reference
-> Map UID [UID]
-> Map UID [UID]
-> SmithEtAlSRS
forall h i j.
(Quantity h, MayHaveUnit h, Concept h, Quantity i, MayHaveUnit i,
 Concept i, HasUID j, Constrained j) =>
SystemMeta
-> String
-> [TheoryModel]
-> [GenDefn]
-> [DataDefinition]
-> [InstanceModel]
-> [h]
-> [i]
-> [j]
-> [ConstQDef]
-> [DefinedQuantityDict]
-> [LabelledContent]
-> Map UID Reference
-> Map UID [UID]
-> Map UID [UID]
-> SmithEtAlSRS
ICO (CI
-> People
-> Background
-> Background
-> Background
-> Background
-> ChunkDB
-> SystemMeta
mkSystemMeta CI
nm People
ppl Background
prps Background
bkgrd Background
scp Background
motive ChunkDB
db) String
progName [TheoryModel]
tms [GenDefn]
gds [DataDefinition]
dds [InstanceModel]
ims [h]
hs [i]
is [j]
js
      [ConstQDef]
cqds [DefinedQuantityDict]
qs [LabelledContent]
lcs Map UID Reference
refsMap Map UID [UID]
forall a. Monoid a => a
mempty Map UID [UID]
forall a. Monoid a => a
mempty
  where
    refsMap :: Map UID Reference
refsMap = [(UID, Reference)] -> Map UID Reference
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList ([(UID, Reference)] -> Map UID Reference)
-> [(UID, Reference)] -> Map UID Reference
forall a b. (a -> b) -> a -> b
$ (Reference -> (UID, Reference))
-> [Reference] -> [(UID, Reference)]
forall a b. (a -> b) -> [a] -> [b]
map (\Reference
x -> (Reference
x Reference -> Getting UID Reference UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID Reference UID
forall c. HasUID c => Getter c UID
Getter Reference UID
uid, Reference
x)) [Reference]
refs
    progName :: String
progName = String -> String
toPlainName (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isSpace) (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ CI -> String
forall c. CommonIdea c => c -> String
abrv CI
nm

-- | Find what chunks reference a specific chunk.
refbyLookup :: UID -> SmithEtAlSRS -> [UID]
refbyLookup :: UID -> SmithEtAlSRS -> [UID]
refbyLookup UID
u = [UID] -> Maybe [UID] -> [UID]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [UID] -> [UID])
-> (SmithEtAlSRS -> Maybe [UID]) -> SmithEtAlSRS -> [UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UID -> Map UID [UID] -> Maybe [UID]
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup UID
u (Map UID [UID] -> Maybe [UID])
-> (SmithEtAlSRS -> Map UID [UID]) -> SmithEtAlSRS -> Maybe [UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SmithEtAlSRS
-> Getting (Map UID [UID]) SmithEtAlSRS (Map UID [UID])
-> Map UID [UID]
forall s a. s -> Getting a s a -> a
^. Getting (Map UID [UID]) SmithEtAlSRS (Map UID [UID])
forall c. HasSmithEtAlSRS c => Lens' c (Map UID [UID])
Lens' SmithEtAlSRS (Map UID [UID])
refbyTable)

-- | Find what chunks a specific one references.
traceLookup :: UID -> SmithEtAlSRS -> [UID]
traceLookup :: UID -> SmithEtAlSRS -> [UID]
traceLookup UID
u = [UID] -> Maybe [UID] -> [UID]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [UID] -> [UID])
-> (SmithEtAlSRS -> Maybe [UID]) -> SmithEtAlSRS -> [UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UID -> Map UID [UID] -> Maybe [UID]
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup UID
u (Map UID [UID] -> Maybe [UID])
-> (SmithEtAlSRS -> Map UID [UID]) -> SmithEtAlSRS -> Maybe [UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SmithEtAlSRS
-> Getting (Map UID [UID]) SmithEtAlSRS (Map UID [UID])
-> Map UID [UID]
forall s a. s -> Getting a s a -> a
^. Getting (Map UID [UID]) SmithEtAlSRS (Map UID [UID])
forall c. HasSmithEtAlSRS c => Lens' c (Map UID [UID])
Lens' SmithEtAlSRS (Map UID [UID])
traceTable)