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 GOOL.Drasil (Label, MSBody, MSBlock, SVariable, SValue, MSStatement,
  OOProg, BodySym(..), BlockSym(..), TypeSym(..), VariableSym(..),
  VariableElim(..), Literal(..), VariableValue(..), StatementSym(..),
  DeclStatement(..), IOStatement(..), lensMStoVS)

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 :: (OOProg r) => SVariable r -> GenState [MSStatement r]
maybeLog :: forall (r :: * -> *).
OOProg r =>
SVariable r -> GenState [MSStatement r]
maybeLog SVariable r
v = do
  DrasilState
g <- forall s (m :: * -> *). MonadState s m => m s
get
  forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence [forall (r :: * -> *).
OOProg r =>
SVariable r -> GenState (MSStatement r)
loggedVar SVariable r
v | Logging
LogVar 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 :: (OOProg r) => SVariable r -> GenState (MSStatement r)
loggedVar :: forall (r :: * -> *).
OOProg r =>
SVariable r -> GenState (MSStatement r)
loggedVar SVariable r
v = do
  DrasilState
g <- forall s (m :: * -> *). MonadState s m => m s
get
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (r :: * -> *).
StatementSym r =>
[MSStatement r] -> MSStatement r
multi [
    forall (r :: * -> *).
IOStatement r =>
SVariable r -> SValue r -> MSStatement r
openFileA forall (r :: * -> *). OOProg r => SVariable r
varLogFile (forall (r :: * -> *). Literal r => String -> SValue r
litString forall a b. (a -> b) -> a -> b
$ DrasilState -> String
logName DrasilState
g),
    forall (m :: * -> *) (n :: * -> *) s t c.
Zoom m n s t =>
LensLike' (Zoomed m c) t s -> m c -> n c
zoom Lens' MethodState ValueState
lensMStoVS SVariable r
v forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\r (Variable r)
v' -> forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStr forall (r :: * -> *). OOProg r => SValue r
valLogFile (String
"var '" forall a. [a] -> [a] -> [a]
++
      forall (r :: * -> *). VariableElim r => r (Variable r) -> String
variableName r (Variable r)
v' forall a. [a] -> [a] -> [a]
++ String
"' assigned ")),
    forall (r :: * -> *).
IOStatement r =>
SValue r -> SValue r -> MSStatement r
printFile forall (r :: * -> *). OOProg r => SValue r
valLogFile (forall (r :: * -> *). VariableValue r => SVariable r -> SValue r
valueOf SVariable r
v),
    forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStrLn forall (r :: * -> *). OOProg r => SValue r
valLogFile (String
" in module " forall a. [a] -> [a] -> [a]
++ DrasilState -> String
currentModule DrasilState
g),
    forall (r :: * -> *). IOStatement r => SValue r -> MSStatement r
closeFile forall (r :: * -> *). OOProg 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 :: (OOProg r) => Label -> [SVariable r] -> [MSBlock r] ->
  GenState (MSBody r)
logBody :: forall (r :: * -> *).
OOProg r =>
String -> [SVariable r] -> [MSBlock r] -> GenState (MSBody r)
logBody String
n [SVariable r]
vars [MSBlock r]
b = do
  DrasilState
g <- forall s (m :: * -> *). MonadState s m => m s
get
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (r :: * -> *). BodySym r => [MSBlock r] -> MSBody r
body forall a b. (a -> b) -> a -> b
$ [forall (r :: * -> *).
OOProg r =>
String -> String -> [SVariable r] -> MSBlock r
loggedMethod (DrasilState -> String
logName DrasilState
g) String
n [SVariable r]
vars | Logging
LogFunc forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` DrasilState -> [Logging]
logKind DrasilState
g] 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 :: (OOProg r) => FilePath -> Label -> [SVariable r] -> MSBlock r
loggedMethod :: forall (r :: * -> *).
OOProg r =>
String -> String -> [SVariable r] -> MSBlock r
loggedMethod String
lName String
n [SVariable r]
vars = forall (r :: * -> *). BlockSym r => [MSStatement r] -> MSBlock r
block [
      forall (r :: * -> *).
DeclStatement r =>
SVariable r -> MSStatement r
varDec forall (r :: * -> *). OOProg r => SVariable r
varLogFile,
      forall (r :: * -> *).
IOStatement r =>
SVariable r -> SValue r -> MSStatement r
openFileA forall (r :: * -> *). OOProg r => SVariable r
varLogFile (forall (r :: * -> *). Literal r => String -> SValue r
litString String
lName),
      forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStrLn forall (r :: * -> *). OOProg r => SValue r
valLogFile (String
"function " forall a. [a] -> [a] -> [a]
++ String
n forall a. [a] -> [a] -> [a]
++ String
" called with inputs: {"),
      forall (r :: * -> *).
StatementSym r =>
[MSStatement r] -> MSStatement r
multi forall a b. (a -> b) -> a -> b
$ forall {r :: * -> *}.
OOProg r =>
[StateT ValueState Identity (r (Variable r))]
-> [StateT MethodState Identity (r (Statement r))]
printInputs [SVariable r]
vars,
      forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStrLn forall (r :: * -> *). OOProg r => SValue r
valLogFile String
"  }",
      forall (r :: * -> *). IOStatement r => SValue r -> MSStatement r
closeFile forall (r :: * -> *). OOProg 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] = [
      forall (m :: * -> *) (n :: * -> *) s t c.
Zoom m n s t =>
LensLike' (Zoomed m c) t s -> m c -> n c
zoom Lens' MethodState ValueState
lensMStoVS StateT ValueState Identity (r (Variable r))
v forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\r (Variable r)
v' -> forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStr forall (r :: * -> *). OOProg r => SValue r
valLogFile (String
"  " forall a. [a] -> [a] -> [a]
++
        forall (r :: * -> *). VariableElim r => r (Variable r) -> String
variableName r (Variable r)
v' forall a. [a] -> [a] -> [a]
++ String
" = ")),
      forall (r :: * -> *).
IOStatement r =>
SValue r -> SValue r -> MSStatement r
printFileLn forall (r :: * -> *). OOProg r => SValue r
valLogFile (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) = [
      forall (m :: * -> *) (n :: * -> *) s t c.
Zoom m n s t =>
LensLike' (Zoomed m c) t s -> m c -> n c
zoom Lens' MethodState ValueState
lensMStoVS StateT ValueState Identity (r (Variable r))
v forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\r (Variable r)
v' -> forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStr forall (r :: * -> *). OOProg r => SValue r
valLogFile (String
"  " forall a. [a] -> [a] -> [a]
++
        forall (r :: * -> *). VariableElim r => r (Variable r) -> String
variableName r (Variable r)
v' forall a. [a] -> [a] -> [a]
++ String
" = ")),
      forall (r :: * -> *).
IOStatement r =>
SValue r -> SValue r -> MSStatement r
printFile forall (r :: * -> *). OOProg r => SValue r
valLogFile (forall (r :: * -> *). VariableValue r => SVariable r -> SValue r
valueOf StateT ValueState Identity (r (Variable r))
v),
      forall (r :: * -> *).
IOStatement r =>
SValue r -> String -> MSStatement r
printFileStrLn forall (r :: * -> *). OOProg r => SValue r
valLogFile String
", "] 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 :: (OOProg r) => SVariable r
varLogFile :: forall (r :: * -> *). OOProg r => SVariable r
varLogFile = forall (r :: * -> *).
VariableSym r =>
String -> VSType r -> SVariable r
var String
"outfile" forall (r :: * -> *). TypeSym r => VSType r
outfile

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