module Language.Drasil.Code.Imperative.Logging (
  logBody, loggedMethod, varLogFile
) where

import Control.Lens ((^.))
import Control.Lens.Zoom (zoom)
import Control.Monad.State (get)

import Language.Drasil.Code.Imperative.DrasilState (GenState, HasChoices(..))
import Language.Drasil.Choices (Logging(..))

import Drasil.GOOL (Label, MSBody, MSBlock, SVariable, SValue, SharedProg,
  BodySym(..), BlockSym(..), TypeSym(..), var, VariableElim(..), Literal(..),
  VariableValue(..), StatementSym(..), DeclStatement(..), IOStatement(..),
  lensMStoVS, ScopeSym(..))

-- | Generates the body of a function with the given name, list of parameters,
-- and blocks to include in the body. If the user chose to turn on logging of
-- function calls, statements that log how the function was called are added to
-- the beginning of the body.
logBody :: (SharedProg r) => Label -> [SVariable r] -> [MSBlock r] ->
  GenState (MSBody r)
logBody :: forall (r :: * -> *).
SharedProg r =>
Label -> [SVariable r] -> [MSBlock r] -> GenState (MSBody r)
logBody Label
n [SVariable r]
vars [MSBlock r]
b = do
  DrasilState
g <- StateT DrasilState Identity DrasilState
forall s (m :: * -> *). MonadState s m => m s
get
  MSBody r -> GenState (MSBody r)
forall a. a -> StateT DrasilState Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (MSBody r -> GenState (MSBody r))
-> MSBody r -> GenState (MSBody r)
forall a b. (a -> b) -> a -> b
$ [MSBlock r] -> MSBody r
forall (r :: * -> *). BodySym r => [MSBlock r] -> MSBody r
body ([MSBlock r] -> MSBody r) -> [MSBlock r] -> MSBody r
forall a b. (a -> b) -> a -> b
$
    [Label -> Label -> [SVariable r] -> MSBlock r
forall (r :: * -> *).
SharedProg r =>
Label -> Label -> [SVariable r] -> MSBlock r
loggedMethod (DrasilState
g DrasilState -> Getting Label DrasilState Label -> Label
forall s a. s -> Getting a s a -> a
^. Getting Label DrasilState Label
forall a. HasChoices a => Lens' a Label
Lens' DrasilState Label
logName) Label
n [SVariable r]
vars | Logging
LogFunc Logging -> [Logging] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` DrasilState
g DrasilState -> Getting [Logging] DrasilState [Logging] -> [Logging]
forall s a. s -> Getting a s a -> a
^. Getting [Logging] DrasilState [Logging]
forall a. HasChoices a => Lens' a [Logging]
Lens' DrasilState [Logging]
logKind] [MSBlock r] -> [MSBlock r] -> [MSBlock r]
forall a. [a] -> [a] -> [a]
++ [MSBlock r]
b

-- | Generates a block that logs, to the given 'FilePath', the name of a function,
-- and the names and values of the passed list of variables. Intended to be
-- used as the first block in the function, to log that it was called and what
-- inputs it was called with.
loggedMethod :: (SharedProg r) => FilePath -> Label -> [SVariable r] -> MSBlock r
loggedMethod :: forall (r :: * -> *).
SharedProg r =>
Label -> Label -> [SVariable r] -> MSBlock r
loggedMethod Label
lName Label
n [SVariable r]
vars = [MSStatement r] -> StateT MethodState Identity (r (Block r))
forall (r :: * -> *). BlockSym r => [MSStatement r] -> MSBlock r
block [
      SVariable r -> r ScopeData -> MSStatement r
forall (r :: * -> *).
DeclStatement r =>
SVariable r -> r ScopeData -> MSStatement r
varDec SVariable r
forall (r :: * -> *). SharedProg r => SVariable r
varLogFile r ScopeData
forall (r :: * -> *). ScopeSym r => r ScopeData
local,
      SVariable r -> SValue r -> MSStatement r
forall (r :: * -> *).
IOStatement r =>
SVariable r -> SValue r -> MSStatement r
openFileA SVariable r
forall (r :: * -> *). SharedProg r => SVariable r
varLogFile (Label -> SValue r
forall (r :: * -> *). Literal r => Label -> SValue r
litString Label
lName),
      SValue r -> Label -> MSStatement r
forall (r :: * -> *).
IOStatement r =>
SValue r -> Label -> MSStatement r
printFileStrLn SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (Label
"function " Label -> Label -> Label
forall a. [a] -> [a] -> [a]
++ Label
n Label -> Label -> Label
forall a. [a] -> [a] -> [a]
++ Label
" called with inputs: {"),
      [MSStatement r] -> MSStatement r
forall (r :: * -> *).
StatementSym r =>
[MSStatement r] -> MSStatement r
multi ([MSStatement r] -> MSStatement r)
-> [MSStatement r] -> MSStatement r
forall a b. (a -> b) -> a -> b
$ [SVariable r] -> [MSStatement r]
forall {r :: * -> *}.
SharedProg r =>
[StateT ValueState Identity (r (Variable r))]
-> [StateT MethodState Identity (r (Statement r))]
printInputs [SVariable r]
vars,
      SValue r -> Label -> MSStatement r
forall (r :: * -> *).
IOStatement r =>
SValue r -> Label -> MSStatement r
printFileStrLn SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile Label
"  }",
      SValue r -> MSStatement r
forall (r :: * -> *). IOStatement r => SValue r -> MSStatement r
closeFile SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile]
  where
    printInputs :: [StateT ValueState Identity (r (Variable r))]
-> [StateT MethodState Identity (r (Statement r))]
printInputs [] = []
    printInputs [StateT ValueState Identity (r (Variable r))
v] = [
      LensLike'
  (Zoomed (StateT ValueState Identity) (r (Variable r)))
  MethodState
  ValueState
-> StateT ValueState Identity (r (Variable r))
-> StateT MethodState Identity (r (Variable r))
forall c.
LensLike'
  (Zoomed (StateT ValueState Identity) c) MethodState ValueState
-> StateT ValueState Identity c -> StateT MethodState Identity c
forall (m :: * -> *) (n :: * -> *) s t c.
Zoom m n s t =>
LensLike' (Zoomed m c) t s -> m c -> n c
zoom LensLike'
  (Zoomed (StateT ValueState Identity) (r (Variable r)))
  MethodState
  ValueState
(ValueState -> Focusing Identity (r (Variable r)) ValueState)
-> MethodState -> Focusing Identity (r (Variable r)) MethodState
Lens' MethodState ValueState
lensMStoVS StateT ValueState Identity (r (Variable r))
v StateT MethodState Identity (r (Variable r))
-> (r (Variable r)
    -> StateT MethodState Identity (r (Statement r)))
-> StateT MethodState Identity (r (Statement r))
forall a b.
StateT MethodState Identity a
-> (a -> StateT MethodState Identity b)
-> StateT MethodState Identity b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\r (Variable r)
v' -> SValue r -> Label -> StateT MethodState Identity (r (Statement r))
forall (r :: * -> *).
IOStatement r =>
SValue r -> Label -> MSStatement r
printFileStr SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (Label
"  " Label -> Label -> Label
forall a. [a] -> [a] -> [a]
++
        r (Variable r) -> Label
forall (r :: * -> *). VariableElim r => r (Variable r) -> Label
variableName r (Variable r)
v' Label -> Label -> Label
forall a. [a] -> [a] -> [a]
++ Label
" = ")),
      SValue r
-> SValue r -> StateT MethodState Identity (r (Statement r))
forall (r :: * -> *).
IOStatement r =>
SValue r -> SValue r -> MSStatement r
printFileLn SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (StateT ValueState Identity (r (Variable r)) -> SValue r
forall (r :: * -> *). VariableValue r => SVariable r -> SValue r
valueOf StateT ValueState Identity (r (Variable r))
v)]
    printInputs (StateT ValueState Identity (r (Variable r))
v:[StateT ValueState Identity (r (Variable r))]
vs) = [
      LensLike'
  (Zoomed (StateT ValueState Identity) (r (Variable r)))
  MethodState
  ValueState
-> StateT ValueState Identity (r (Variable r))
-> StateT MethodState Identity (r (Variable r))
forall c.
LensLike'
  (Zoomed (StateT ValueState Identity) c) MethodState ValueState
-> StateT ValueState Identity c -> StateT MethodState Identity c
forall (m :: * -> *) (n :: * -> *) s t c.
Zoom m n s t =>
LensLike' (Zoomed m c) t s -> m c -> n c
zoom LensLike'
  (Zoomed (StateT ValueState Identity) (r (Variable r)))
  MethodState
  ValueState
(ValueState -> Focusing Identity (r (Variable r)) ValueState)
-> MethodState -> Focusing Identity (r (Variable r)) MethodState
Lens' MethodState ValueState
lensMStoVS StateT ValueState Identity (r (Variable r))
v StateT MethodState Identity (r (Variable r))
-> (r (Variable r)
    -> StateT MethodState Identity (r (Statement r)))
-> StateT MethodState Identity (r (Statement r))
forall a b.
StateT MethodState Identity a
-> (a -> StateT MethodState Identity b)
-> StateT MethodState Identity b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\r (Variable r)
v' -> SValue r -> Label -> StateT MethodState Identity (r (Statement r))
forall (r :: * -> *).
IOStatement r =>
SValue r -> Label -> MSStatement r
printFileStr SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (Label
"  " Label -> Label -> Label
forall a. [a] -> [a] -> [a]
++
        r (Variable r) -> Label
forall (r :: * -> *). VariableElim r => r (Variable r) -> Label
variableName r (Variable r)
v' Label -> Label -> Label
forall a. [a] -> [a] -> [a]
++ Label
" = ")),
      SValue r
-> SValue r -> StateT MethodState Identity (r (Statement r))
forall (r :: * -> *).
IOStatement r =>
SValue r -> SValue r -> MSStatement r
printFile SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (StateT ValueState Identity (r (Variable r)) -> SValue r
forall (r :: * -> *). VariableValue r => SVariable r -> SValue r
valueOf StateT ValueState Identity (r (Variable r))
v),
      SValue r -> Label -> StateT MethodState Identity (r (Statement r))
forall (r :: * -> *).
IOStatement r =>
SValue r -> Label -> MSStatement r
printFileStrLn SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile Label
", "] [StateT MethodState Identity (r (Statement r))]
-> [StateT MethodState Identity (r (Statement r))]
-> [StateT MethodState Identity (r (Statement r))]
forall a. [a] -> [a] -> [a]
++ [StateT ValueState Identity (r (Variable r))]
-> [StateT MethodState Identity (r (Statement r))]
printInputs [StateT ValueState Identity (r (Variable r))]
vs

-- | The variable representing the log file in write mode.
varLogFile :: (SharedProg r) => SVariable r
varLogFile :: forall (r :: * -> *). SharedProg r => SVariable r
varLogFile = Label -> VSType r -> StateT ValueState Identity (r (Variable r))
forall (r :: * -> *).
VariableSym r =>
Label -> VSType r -> SVariable r
var Label
"outfile" VSType r
forall (r :: * -> *). TypeSym r => VSType r
outfile

-- | The value of the variable representing the log file in write mode.
valLogFile :: (SharedProg r) => SValue r
valLogFile :: forall (r :: * -> *). SharedProg r => SValue r
valLogFile = SVariable r -> StateT ValueState Identity (r (Value r))
forall (r :: * -> *). VariableValue r => SVariable r -> SValue r
valueOf SVariable r
forall (r :: * -> *). SharedProg r => SVariable r
varLogFile