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, block, SVariable, SValue, MS, BodySym(..),
  BlockSym(..), TypeSym(..), var, VariableElim(..), Literal(..),
  VariableValue(..), MultiStatement(..), DeclStatement(..), FileHandling(..),
  PrintFile(..), lensMStoVS, ScopeSym(..), VariableSym)

-- | 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
  ::
    ( Literal r
    , VariableValue r
    , MultiStatement r stmt
    , DeclStatement r stmt bod
    , FileHandling r stmt
    , PrintFile r stmt
    , BlockSym r block stmt
    , BodySym r bod block
    , VariableElim r
    )
  => Label -> [SVariable r] -> [MS (r block)] -> GenState (MS (r bod))
logBody :: forall (r :: * -> *) stmt bod block.
(Literal r, VariableValue r, MultiStatement r stmt,
 DeclStatement r stmt bod, FileHandling r stmt, PrintFile r stmt,
 BlockSym r block stmt, BodySym r bod block, VariableElim r) =>
Label -> [SVariable r] -> [MS (r block)] -> GenState (MS (r bod))
logBody Label
n [SVariable r]
vars [MS (r block)]
b = do
  g <- StateT DrasilState Identity DrasilState
forall s (m :: * -> *). MonadState s m => m s
get
  return $ body $
    [loggedMethod (g ^. logName) n vars | LogFunc `elem` g ^. logKind] ++ 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
  ::
    ( Literal r
    , VariableValue r
    , MultiStatement r stmt
    , DeclStatement r stmt bod
    , FileHandling r stmt
    , PrintFile r stmt
    , BlockSym r block stmt
    , VariableElim r
    )
  => FilePath -> Label -> [SVariable r] -> MS (r block)
loggedMethod :: forall (r :: * -> *) stmt bod block.
(Literal r, VariableValue r, MultiStatement r stmt,
 DeclStatement r stmt bod, FileHandling r stmt, PrintFile r stmt,
 BlockSym r block stmt, VariableElim r) =>
Label -> Label -> [SVariable r] -> MS (r block)
loggedMethod Label
lName Label
n [SVariable r]
vars = [MS (r stmt)] -> MS (r block)
forall {k} (r :: k -> *) (block :: k) (stmt :: k).
BlockSym r block stmt =>
[MS (r stmt)] -> MS (r block)
block [
      SVariable r -> r ScopeData -> MS (r stmt)
forall (r :: * -> *) stmt bod.
DeclStatement r stmt bod =>
SVariable r -> r ScopeData -> MS (r stmt)
varDec SVariable r
forall (r :: * -> *). VariableSym r => SVariable r
varLogFile r ScopeData
forall (r :: * -> *). ScopeSym r => r ScopeData
local,
      SVariable r -> SValue r -> MS (r stmt)
forall (r :: * -> *) stmt.
FileHandling r stmt =>
SVariable r -> SValue r -> MS (r stmt)
openFileA SVariable r
forall (r :: * -> *). VariableSym r => SVariable r
varLogFile (Label -> SValue r
forall (r :: * -> *). Literal r => Label -> SValue r
litString Label
lName),
      SValue r -> Label -> MS (r stmt)
forall (r :: * -> *) stmt.
PrintFile r stmt =>
SValue r -> Label -> MS (r stmt)
printFileStrLn SValue r
forall (r :: * -> *). VariableValue 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: {"),
      [MS (r stmt)] -> MS (r stmt)
forall {k} (r :: k -> *) (stmt :: k).
MultiStatement r stmt =>
[MS (r stmt)] -> MS (r stmt)
multi ([MS (r stmt)] -> MS (r stmt)) -> [MS (r stmt)] -> MS (r stmt)
forall a b. (a -> b) -> a -> b
$ [SVariable r] -> [MS (r stmt)]
forall {r :: * -> *} {stmt}.
(PrintFile r stmt, VariableValue r, VariableElim r) =>
[StateT ValueState Identity (r Variable)]
-> [StateT MethodState Identity (r stmt)]
printInputs [SVariable r]
vars,
      SValue r -> Label -> MS (r stmt)
forall (r :: * -> *) stmt.
PrintFile r stmt =>
SValue r -> Label -> MS (r stmt)
printFileStrLn SValue r
forall (r :: * -> *). VariableValue r => SValue r
valLogFile Label
"  }",
      SValue r -> MS (r stmt)
forall (r :: * -> *) stmt.
FileHandling r stmt =>
SValue r -> MS (r stmt)
closeFile SValue r
forall (r :: * -> *). VariableValue r => SValue r
valLogFile]
  where
    printInputs :: [StateT ValueState Identity (r Variable)]
-> [StateT MethodState Identity (r stmt)]
printInputs [] = []
    printInputs [StateT ValueState Identity (r Variable)
v] = [
      LensLike'
  (Zoomed (StateT ValueState Identity) (r Variable))
  MethodState
  ValueState
-> StateT ValueState Identity (r Variable)
-> StateT MethodState Identity (r Variable)
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))
  MethodState
  ValueState
(ValueState -> Focusing Identity (r Variable) ValueState)
-> MethodState -> Focusing Identity (r Variable) MethodState
Lens' MethodState ValueState
lensMStoVS StateT ValueState Identity (r Variable)
v StateT MethodState Identity (r Variable)
-> (r Variable -> StateT MethodState Identity (r stmt))
-> StateT MethodState Identity (r stmt)
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
v' -> SValue r -> Label -> StateT MethodState Identity (r stmt)
forall (r :: * -> *) stmt.
PrintFile r stmt =>
SValue r -> Label -> MS (r stmt)
printFileStr SValue r
forall (r :: * -> *). VariableValue r => SValue r
valLogFile (Label
"  " Label -> Label -> Label
forall a. [a] -> [a] -> [a]
++
        r Variable -> Label
forall (r :: * -> *). VariableElim r => r Variable -> Label
variableName r Variable
v' Label -> Label -> Label
forall a. [a] -> [a] -> [a]
++ Label
" = ")),
      SValue r -> SValue r -> StateT MethodState Identity (r stmt)
forall (r :: * -> *) stmt.
PrintFile r stmt =>
SValue r -> SValue r -> MS (r stmt)
printFileLn SValue r
forall (r :: * -> *). VariableValue r => SValue r
valLogFile (StateT ValueState Identity (r Variable) -> SValue r
forall (r :: * -> *). VariableValue r => SVariable r -> SValue r
valueOf StateT ValueState Identity (r Variable)
v)]
    printInputs (StateT ValueState Identity (r Variable)
v:[StateT ValueState Identity (r Variable)]
vs) = [
      LensLike'
  (Zoomed (StateT ValueState Identity) (r Variable))
  MethodState
  ValueState
-> StateT ValueState Identity (r Variable)
-> StateT MethodState Identity (r Variable)
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))
  MethodState
  ValueState
(ValueState -> Focusing Identity (r Variable) ValueState)
-> MethodState -> Focusing Identity (r Variable) MethodState
Lens' MethodState ValueState
lensMStoVS StateT ValueState Identity (r Variable)
v StateT MethodState Identity (r Variable)
-> (r Variable -> StateT MethodState Identity (r stmt))
-> StateT MethodState Identity (r stmt)
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
v' -> SValue r -> Label -> StateT MethodState Identity (r stmt)
forall (r :: * -> *) stmt.
PrintFile r stmt =>
SValue r -> Label -> MS (r stmt)
printFileStr SValue r
forall (r :: * -> *). VariableValue r => SValue r
valLogFile (Label
"  " Label -> Label -> Label
forall a. [a] -> [a] -> [a]
++
        r Variable -> Label
forall (r :: * -> *). VariableElim r => r Variable -> Label
variableName r Variable
v' Label -> Label -> Label
forall a. [a] -> [a] -> [a]
++ Label
" = ")),
      SValue r -> SValue r -> StateT MethodState Identity (r stmt)
forall (r :: * -> *) stmt.
PrintFile r stmt =>
SValue r -> SValue r -> MS (r stmt)
printFile SValue r
forall (r :: * -> *). VariableValue r => SValue r
valLogFile (StateT ValueState Identity (r Variable) -> SValue r
forall (r :: * -> *). VariableValue r => SVariable r -> SValue r
valueOf StateT ValueState Identity (r Variable)
v),
      SValue r -> Label -> StateT MethodState Identity (r stmt)
forall (r :: * -> *) stmt.
PrintFile r stmt =>
SValue r -> Label -> MS (r stmt)
printFileStrLn SValue r
forall (r :: * -> *). VariableValue r => SValue r
valLogFile Label
", "] [StateT MethodState Identity (r stmt)]
-> [StateT MethodState Identity (r stmt)]
-> [StateT MethodState Identity (r stmt)]
forall a. [a] -> [a] -> [a]
++ [StateT ValueState Identity (r Variable)]
-> [StateT MethodState Identity (r stmt)]
printInputs [StateT ValueState Identity (r Variable)]
vs

-- | The variable representing the log file in write mode.
varLogFile :: (VariableSym r) => SVariable r
varLogFile :: forall (r :: * -> *). VariableSym r => SVariable r
varLogFile = Label -> VS (r TypeData) -> SVariable r
forall (r :: * -> *).
VariableSym r =>
Label -> VS (r TypeData) -> SVariable r
var Label
"outfile" VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
outfile

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