{-# LANGUAGE TemplateHaskell #-}
-- | 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.SRS.SmithEtAlSRS (
  -- * System ** Types
  SmithEtAlSRS(..),
  Purpose, Background, Scope, Motivation,
  -- ** Lenses
  HasSmithEtAlSRS(..),
  -- ** Constructors
  mkSmithEtAlICO,
  -- ** Hacks
  refbyLookup, traceLookup
) where

import Control.Lens (makeClassy, (^.))
import qualified Data.List.NonEmpty as NE
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe)

import Drasil.Database (UID, HasUID(..), ChunkDB)
import Language.Drasil (Quantity, MayHaveUnit, Concept, People, Constrained,
  ConstQDef, DefinedQuantityDict)
import Theory.Drasil (TheoryModel, GenDefn, DataDefinition, InstanceModel)

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

-- | 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 -> [TheoryModel]
_theoryModels :: [TheoryModel]
  , SmithEtAlSRS -> [GenDefn]
_genDefns     :: [GenDefn]
  , SmithEtAlSRS -> [DataDefinition]
_dataDefns    :: [DataDefinition]
  , SmithEtAlSRS -> [InstanceModel]
_instModels   :: [InstanceModel]
  , ()
_inputs       :: NE.NonEmpty h
  , ()
_outputs      :: NE.NonEmpty 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: Hacks to be removed once 'Reference's are rebuilt.
  , 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

instance HasProjectName SmithEtAlSRS where
  projectName :: Lens' SmithEtAlSRS ProjectName
projectName = (SystemMeta -> f SystemMeta) -> SmithEtAlSRS -> f SmithEtAlSRS
forall c. HasSmithEtAlSRS c => Lens' c SystemMeta
Lens' SmithEtAlSRS SystemMeta
meta ((SystemMeta -> f SystemMeta) -> SmithEtAlSRS -> f SmithEtAlSRS)
-> ((ProjectName -> f ProjectName) -> SystemMeta -> f SystemMeta)
-> (ProjectName -> f ProjectName)
-> SmithEtAlSRS
-> f SmithEtAlSRS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ProjectName -> f ProjectName) -> SystemMeta -> f SystemMeta
forall c. HasProjectName c => Lens' c ProjectName
Lens' SystemMeta ProjectName
projectName

-- | Build a 'System'.
mkSmithEtAlICO :: (Quantity h, MayHaveUnit h, Concept h,
  Quantity i, MayHaveUnit i, Concept i,
  HasUID j, Constrained j) =>
  ProjectName -> People -> Purpose -> Background -> Scope -> Motivation ->
    [TheoryModel] -> [GenDefn] -> [DataDefinition] -> [InstanceModel] ->
    NE.NonEmpty h -> NE.NonEmpty i -> [j] -> [ConstQDef] -> [DefinedQuantityDict] ->
    ChunkDB -> SmithEtAlSRS
mkSmithEtAlICO :: forall h i j.
(Quantity h, MayHaveUnit h, Concept h, Quantity i, MayHaveUnit i,
 Concept i, HasUID j, Constrained j) =>
ProjectName
-> People
-> Background
-> Background
-> Background
-> Background
-> [TheoryModel]
-> [GenDefn]
-> [DataDefinition]
-> [InstanceModel]
-> NonEmpty h
-> NonEmpty i
-> [j]
-> [ConstQDef]
-> [DefinedQuantityDict]
-> ChunkDB
-> SmithEtAlSRS
mkSmithEtAlICO ProjectName
pn People
ppl Background
prps Background
bkgrd Background
scp Background
motive [TheoryModel]
tms [GenDefn]
gds [DataDefinition]
dds [InstanceModel]
ims NonEmpty h
hs NonEmpty i
is [j]
js [ConstQDef]
cqds [DefinedQuantityDict]
qs ChunkDB
db
  = SystemMeta
-> [TheoryModel]
-> [GenDefn]
-> [DataDefinition]
-> [InstanceModel]
-> NonEmpty h
-> NonEmpty i
-> [j]
-> [ConstQDef]
-> [DefinedQuantityDict]
-> 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
-> [TheoryModel]
-> [GenDefn]
-> [DataDefinition]
-> [InstanceModel]
-> NonEmpty h
-> NonEmpty i
-> [j]
-> [ConstQDef]
-> [DefinedQuantityDict]
-> Map UID [UID]
-> Map UID [UID]
-> SmithEtAlSRS
ICO (ProjectName
-> People
-> Background
-> Background
-> Background
-> Background
-> ChunkDB
-> SystemMeta
mkSystemMeta ProjectName
pn People
ppl Background
prps Background
bkgrd Background
scp Background
motive ChunkDB
db) [TheoryModel]
tms [GenDefn]
gds [DataDefinition]
dds [InstanceModel]
ims NonEmpty h
hs NonEmpty i
is [j]
js
      [ConstQDef]
cqds [DefinedQuantityDict]
qs Map UID [UID]
forall a. Monoid a => a
mempty Map UID [UID]
forall a. Monoid a => a
mempty

-- | 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)