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

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

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

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

-- | Generates a statement that logs the given variable's value, if the user
-- chose to turn on logging of variable assignments.
maybeLog :: (SharedProg r) => SVariable r -> GenState [MSStatement r]
maybeLog :: forall (r :: * -> *).
SharedProg r =>
SVariable r -> GenState [MSStatement r]
maybeLog SVariable r
v = do
  DrasilState
g <- StateT DrasilState Identity DrasilState
forall s (m :: * -> *). MonadState s m => m s
get
  [StateT DrasilState Identity (MSStatement r)]
-> GenState [MSStatement r]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
forall (m :: * -> *) a. Monad m => [m a] -> m [a]
sequence [SVariable r -> StateT DrasilState Identity (MSStatement r)
forall (r :: * -> *).
SharedProg r =>
SVariable r -> GenState (MSStatement r)
loggedVar SVariable r
v | Logging
LogVar Logging -> [Logging] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` DrasilState -> [Logging]
logKind DrasilState
g]

-- | Generates a statement that logs the name of the given variable, its current
-- value, and the current module name.
loggedVar :: (SharedProg r) => SVariable r -> GenState (MSStatement r)
loggedVar :: forall (r :: * -> *).
SharedProg r =>
SVariable r -> GenState (MSStatement r)
loggedVar SVariable r
v = do
  DrasilState
g <- StateT DrasilState Identity DrasilState
forall s (m :: * -> *). MonadState s m => m s
get
  MSStatement r -> GenState (MSStatement r)
forall a. a -> StateT DrasilState Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (MSStatement r -> GenState (MSStatement r))
-> MSStatement r -> GenState (MSStatement r)
forall a b. (a -> b) -> a -> b
$ [MSStatement r] -> MSStatement r
forall (r :: * -> *).
StatementSym r =>
[MSStatement r] -> MSStatement r
multi [
    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 (String -> SValue r
forall (r :: * -> *). Literal r => String -> SValue r
litString (String -> SValue r) -> String -> SValue r
forall a b. (a -> b) -> a -> b
$ DrasilState -> String
logName DrasilState
g),
    LensLike'
  (Zoomed (StateT ValueState Identity) (r (Variable r)))
  MethodState
  ValueState
-> SVariable 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 SVariable r
v StateT MethodState Identity (r (Variable r))
-> (r (Variable r) -> MSStatement r) -> MSStatement 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 -> String -> MSStatement r
forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStr SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (String
"var '" String -> String -> String
forall a. [a] -> [a] -> [a]
++
      r (Variable r) -> String
forall (r :: * -> *). VariableElim r => r (Variable r) -> String
variableName r (Variable r)
v' String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"' assigned ")),
    SValue r -> SValue r -> MSStatement r
forall (r :: * -> *).
IOStatement r =>
SValue r -> SValue r -> MSStatement r
printFile SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (SVariable r -> SValue r
forall (r :: * -> *). VariableValue r => SVariable r -> SValue r
valueOf SVariable r
v),
    SValue r -> String -> MSStatement r
forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStrLn SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (String
" in module " String -> String -> String
forall a. [a] -> [a] -> [a]
++ DrasilState -> String
currentModule DrasilState
g),
    SValue r -> MSStatement r
forall (r :: * -> *). IOStatement r => SValue r -> MSStatement r
closeFile SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile]

-- | 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 =>
String -> [SVariable r] -> [MSBlock r] -> GenState (MSBody r)
logBody String
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
$ [String -> String -> [SVariable r] -> MSBlock r
forall (r :: * -> *).
SharedProg r =>
String -> String -> [SVariable r] -> MSBlock r
loggedMethod (DrasilState -> String
logName DrasilState
g) String
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 -> [Logging]
logKind DrasilState
g] [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 =>
String -> String -> [SVariable r] -> MSBlock r
loggedMethod String
lName String
n [SVariable r]
vars = [MSStatement r] -> StateT MethodState Identity (r (Block r))
forall (r :: * -> *). BlockSym r => [MSStatement r] -> MSBlock r
block [
      SVariable r -> r (Scope r) -> MSStatement r
forall (r :: * -> *).
DeclStatement r =>
SVariable r -> r (Scope r) -> MSStatement r
varDec SVariable r
forall (r :: * -> *). SharedProg r => SVariable r
varLogFile r (Scope r)
forall (r :: * -> *). ScopeSym r => r (Scope r)
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 (String -> SValue r
forall (r :: * -> *). Literal r => String -> SValue r
litString String
lName),
      SValue r -> String -> MSStatement r
forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStrLn SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (String
"function " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
n String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" 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 -> String -> MSStatement r
forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStrLn SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile String
"  }",
      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 -> String -> StateT MethodState Identity (r (Statement r))
forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStr SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (String
"  " String -> String -> String
forall a. [a] -> [a] -> [a]
++
        r (Variable r) -> String
forall (r :: * -> *). VariableElim r => r (Variable r) -> String
variableName r (Variable r)
v' String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" = ")),
      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 -> String -> StateT MethodState Identity (r (Statement r))
forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStr SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile (String
"  " String -> String -> String
forall a. [a] -> [a] -> [a]
++
        r (Variable r) -> String
forall (r :: * -> *). VariableElim r => r (Variable r) -> String
variableName r (Variable r)
v' String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" = ")),
      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 -> String -> StateT MethodState Identity (r (Statement r))
forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStrLn SValue r
forall (r :: * -> *). SharedProg r => SValue r
valLogFile String
", "] [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 = String -> VSType r -> StateT ValueState Identity (r (Variable r))
forall (r :: * -> *).
VariableSym r =>
String -> VSType r -> SVariable r
var String
"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