module Language.Drasil.Code.Imperative.GenODE (
  chooseODELib
) where

import Language.Drasil (Sentence(..), (+:+.))
import Language.Drasil.Code.ExtLibImport (ExtLibState(..),
  genExternalLibraryCall)
import Language.Drasil.Code.Lang (Lang(..))
import Language.Drasil.Chunk.Code (codeName)
import Language.Drasil.Chunk.CodeDefinition (odeDef)
import Language.Drasil.Mod (Name, Version)
import Language.Drasil.Data.ODELibPckg (ODELibPckg(..))

import Control.Monad.State (State, modify)
import Language.Drasil.Choices (ODE(..))

-- | Holds the generation information for an ordinary differential equation.
type ODEGenInfo = (Maybe FilePath, [(Name, ExtLibState)], (Name,Version))

-- | Chooses the first 'ODELibPckg' from the list specified by the user that is
-- compatible with the current target 'Lang'.
-- Interprets the ExternalLibrary and ExternalLibraryCall for the selected
-- 'ODELibPckg' by concretizing the ExternalLibraryCall with each of the 'ODEInfo's
-- The internal helper chooseODELib' keeps a read only preference list and a currently considered
-- preference list (which can change), this facilitates the 'firstChoiceODELib' check.
chooseODELib :: Lang -> Maybe ODE -> State [Sentence] ODEGenInfo
chooseODELib :: Lang -> Maybe ODE -> State [Sentence] ODEGenInfo
chooseODELib Lang
_ Maybe ODE
Nothing = forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. Maybe a
Nothing, [], (Name
"",Name
""))
chooseODELib Lang
l (Just ODE
ode) = [ODELibPckg] -> [ODELibPckg] -> State [Sentence] ODEGenInfo
chooseODELib' (ODE -> [ODELibPckg]
odeLib ODE
ode) (ODE -> [ODELibPckg]
odeLib ODE
ode)
  where chooseODELib' :: [ODELibPckg] -> [ODELibPckg] -> State [Sentence] ODEGenInfo
        chooseODELib' :: [ODELibPckg] -> [ODELibPckg] -> State [Sentence] ODEGenInfo
chooseODELib' [ODELibPckg]
_ [] = forall a. HasCallStack => Name -> a
error forall a b. (a -> b) -> a -> b
$ Name
"None of the chosen ODE libraries are " forall a. [a] -> [a] -> [a]
++
          Name
"compatible with " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> Name
show Lang
l
        chooseODELib' [ODELibPckg]
prefLibList (ODELibPckg
o:[ODELibPckg]
os) = if Lang
l forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ODELibPckg -> [Lang]
compatibleLangs ODELibPckg
o
          then do
            forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (forall a. [a] -> [a] -> [a]
++ [[ODELibPckg] -> ODELibPckg -> Sentence
firstChoiceODELib [ODELibPckg]
prefLibList ODELibPckg
o])
            forall (m :: * -> *) a. Monad m => a -> m a
return (ODELibPckg -> Maybe Name
libPath ODELibPckg
o, forall a b. (a -> b) -> [a] -> [b]
map (\ODEInfo
ode' -> (forall c. CodeIdea c => c -> Name
codeName forall a b. (a -> b) -> a -> b
$ ODEInfo -> CodeDefinition
odeDef ODEInfo
ode',
              ExternalLibrary -> ExternalLibraryCall -> ExtLibState
genExternalLibraryCall (ODELibPckg -> ExternalLibrary
libSpec ODELibPckg
o) forall a b. (a -> b) -> a -> b
$ ODELibPckg -> ODEInfo -> ExternalLibraryCall
libCall ODELibPckg
o ODEInfo
ode')) forall a b. (a -> b) -> a -> b
$ ODE -> [ODEInfo]
odeInfo ODE
ode,
                (ODELibPckg -> Name
libName ODELibPckg
o, ODELibPckg -> Name
libVers ODELibPckg
o))
          else forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (forall a. [a] -> [a] -> [a]
++ [Lang -> ODELibPckg -> Sentence
incompatibleLib Lang
l ODELibPckg
o]) forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> [ODELibPckg] -> [ODELibPckg] -> State [Sentence] ODEGenInfo
chooseODELib' [ODELibPckg]
prefLibList [ODELibPckg]
os

-- | Defines a design log message based on an incompatibility between the given
-- 'Lang' and chosen 'ODELibPckg'.
incompatibleLib :: Lang -> ODELibPckg -> Sentence
incompatibleLib :: Lang -> ODELibPckg -> Sentence
incompatibleLib Lang
lng ODELibPckg
lib = Name -> Sentence
S forall a b. (a -> b) -> a -> b
$ Name
"Language " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> Name
show Lang
lng forall a. [a] -> [a] -> [a]
++ Name
" is not " forall a. [a] -> [a] -> [a]
++
  Name
"compatible with chosen library " forall a. [a] -> [a] -> [a]
++ ODELibPckg -> Name
libName ODELibPckg
lib forall a. [a] -> [a] -> [a]
++ Name
", trying next choice."

-- | Defines a design log message if the first choice ODE Library, which is the head of
-- the preference list that the user selected, is compatible with the given 'Lang'.
firstChoiceODELib :: [ODELibPckg] -> ODELibPckg -> Sentence
firstChoiceODELib :: [ODELibPckg] -> ODELibPckg -> Sentence
firstChoiceODELib [ODELibPckg]
prefer ODELibPckg
olp =  if ODELibPckg -> Name
libName (forall a. [a] -> a
head [ODELibPckg]
prefer) forall a. Eq a => a -> a -> Bool
== ODELibPckg -> Name
libName ODELibPckg
olp  then
  Name -> Sentence
S Name
"Successfully selected first choice ODE Library package" Sentence -> Sentence -> Sentence
+:+. Name -> Sentence
S (ODELibPckg -> Name
libName ODELibPckg
olp)
  else Name -> Sentence
S Name
"ODE Library package selected as" Sentence -> Sentence -> Sentence
+:+. Name -> Sentence
S (ODELibPckg -> Name
libName ODELibPckg
olp)