-- | Implementations defined here are valid in some, but not all, language renderers
module Drasil.Shared.LanguageRenderer.Common (
  boolRender, bool, extVar, funcType, extFuncAppMixedArgs, listAccessFunc,
  forEach', varDecDef, listSize, increment
) where

import Prelude hiding (print, pi, (<>))
import Control.Lens.Zoom (zoom)
import Control.Monad.State (modify)
import Text.PrettyPrint.HughesPJ (text, empty, Doc)

import Drasil.Shared.CodeType (CodeType(..))
import Drasil.Shared.InterfaceCommon (Body, Variable, SVariable, MixedCall,
  Value, SValue, ValueSym, TypeSym(int), VariableElim(variableName), Label,
  Library, funcApp, getCodeType, EmptyStatement, AssignStatement,
  ValueExpression)
import Drasil.Shared.RendererClassesCommon (scopeData, call,
  RenderFunction(funcFromData), RenderVariable, RenderValue, ValueElim,
  RenderStatement, ScopeElim, InternalVarElim)
import Drasil.Shared.LanguageRenderer (access, intValue)
import qualified Drasil.Shared.LanguageRenderer as R (extVar, listAccessFunc,
  addAssign)
import Drasil.Shared.LanguageRenderer.Constructors(mkStmtNoEnd, mkStateVar,
  typeFromData)
import Drasil.Shared.State (MS, VS, lensMStoVS, useVarName, setVarScope)
import qualified Drasil.Shared.InterfaceCommon as IC
import Drasil.Shared.AST (ScopeData, TypeData, FuncData)

-- Swift and Julia --

boolRender :: String
boolRender :: String
boolRender = String
"Bool"

bool :: (Monad r) => VS (r TypeData)
bool :: forall (r :: * -> *). Monad r => VS (r TypeData)
bool = CodeType -> String -> Doc -> VS (r TypeData)
forall (r :: * -> *).
Monad r =>
CodeType -> String -> Doc -> VS (r TypeData)
typeFromData CodeType
Boolean String
boolRender (String -> Doc
text String
boolRender)

-- Python, Java, C#, and Julia --

extVar :: (RenderVariable r) => Label -> Label -> VS (r TypeData) -> SVariable r
extVar :: forall (r :: * -> *).
RenderVariable r =>
String -> String -> VS (r TypeData) -> SVariable r
extVar String
l String
n VS (r TypeData)
t = String -> VS (r TypeData) -> Doc -> SVariable r
forall (r :: * -> *).
RenderVariable r =>
String -> VS (r TypeData) -> Doc -> SVariable r
mkStateVar (String
l String -> String -> String
`access` String
n) VS (r TypeData)
t (String -> String -> Doc
R.extVar String
l String
n)

-- Python, Java, and Julia --

funcType :: (Monad r, IC.TypeElim r) => [VS (r TypeData)] ->
  VS (r TypeData) -> VS (r TypeData)
funcType :: forall (r :: * -> *).
(Monad r, TypeElim r) =>
[VS (r TypeData)] -> VS (r TypeData) -> VS (r TypeData)
funcType [VS (r TypeData)]
ps' VS (r TypeData)
r' =  do
  ps <- [VS (r TypeData)] -> StateT ValueState Identity [r TypeData]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
forall (m :: * -> *) a. Monad m => [m a] -> m [a]
sequence [VS (r TypeData)]
ps'
  r <- r'
  typeFromData (Func (map getCodeType ps) (getCodeType r)) "" empty

-- Python, Java, C#, Swift, and Julia --
extFuncAppMixedArgs :: (RenderValue r) => Library -> MixedCall r
extFuncAppMixedArgs :: forall (r :: * -> *). RenderValue r => String -> MixedCall r
extFuncAppMixedArgs String
l = Maybe String -> Maybe Doc -> MixedCall r
forall (r :: * -> *).
RenderValue r =>
Maybe String -> Maybe Doc -> MixedCall r
call (String -> Maybe String
forall a. a -> Maybe a
Just String
l) Maybe Doc
forall a. Maybe a
Nothing

-- Python, C#, Swift, and Julia --

listAccessFunc
  :: (RenderFunction r, IC.TypeElim r, ValueElim r, ValueSym r)
  => VS (r TypeData) -> SValue r -> VS (r FuncData)
listAccessFunc :: forall (r :: * -> *).
(RenderFunction r, TypeElim r, ValueElim r, ValueSym r) =>
VS (r TypeData) -> SValue r -> VS (r FuncData)
listAccessFunc VS (r TypeData)
t SValue r
v = SValue r -> SValue r
forall (r :: * -> *).
(TypeElim r, ValueSym r) =>
SValue r -> SValue r
intValue SValue r
v SValue r
-> (r Value -> StateT ValueState Identity (r FuncData))
-> StateT ValueState Identity (r FuncData)
forall a b.
StateT ValueState Identity a
-> (a -> StateT ValueState Identity b)
-> StateT ValueState Identity b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ((Doc -> VS (r TypeData) -> StateT ValueState Identity (r FuncData)
forall (r :: * -> *).
RenderFunction r =>
Doc -> VS (r TypeData) -> VS (r FuncData)
`funcFromData` VS (r TypeData)
t) (Doc -> StateT ValueState Identity (r FuncData))
-> (r Value -> Doc)
-> r Value
-> StateT ValueState Identity (r FuncData)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. r Value -> Doc
forall (r :: * -> *). ValueElim r => r Value -> Doc
R.listAccessFunc)

-- Python, Swift, and Julia --

forEach' :: (RenderStatement r stmt) => (r Variable -> r Value ->
  r Body -> Doc) -> SVariable r -> SValue r -> MS (r Body) -> MS (r stmt)
forEach' :: forall (r :: * -> *) stmt.
RenderStatement r stmt =>
(r Variable -> r Value -> r Doc -> Doc)
-> SVariable r -> SValue r -> MS (r Doc) -> MS (r stmt)
forEach' r Variable -> r Value -> r Doc -> Doc
f SVariable r
i' SValue r
v' MS (r Doc)
b' = do
  i <- LensLike'
  (Zoomed (StateT ValueState Identity) (r Variable))
  MethodState
  ValueState
-> SVariable r -> 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 SVariable r
i'
  v <- zoom lensMStoVS v'
  b <- b'
  mkStmtNoEnd (f i v b)

-- Python and Julia --

varDecDef
  :: (EmptyStatement r stmt, AssignStatement r stmt, ScopeElim r, VariableElim r)
  => SVariable r -> r ScopeData -> Maybe (SValue r) -> MS (r stmt)
varDecDef :: forall (r :: * -> *) stmt.
(EmptyStatement r stmt, AssignStatement r stmt, ScopeElim r,
 VariableElim r) =>
SVariable r -> r ScopeData -> Maybe (SValue r) -> MS (r stmt)
varDecDef SVariable r
v r ScopeData
scp Maybe (SValue r)
e = do
  v' <- LensLike'
  (Zoomed (StateT ValueState Identity) (r Variable))
  MethodState
  ValueState
-> SVariable r -> 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 SVariable r
v
  modify $ useVarName (variableName v')
  modify $ setVarScope (variableName v') (scopeData scp)
  def e
  where
    def :: Maybe (SValue r) -> MS (r stmt)
def Maybe (SValue r)
Nothing = MS (r stmt)
forall {k} (r :: k -> *) (stmt :: k).
EmptyStatement r stmt =>
MS (r stmt)
IC.emptyStmt
    def (Just SValue r
d) = SVariable r -> SValue r -> MS (r stmt)
forall (r :: * -> *) stmt.
AssignStatement r stmt =>
SVariable r -> SValue r -> MS (r stmt)
IC.assign SVariable r
v SValue r
d

-- Python and Swift --

increment
  :: (InternalVarElim r, RenderStatement r stmt, ValueElim r)
  => SVariable r -> SValue r -> MS (r stmt)
increment :: forall (r :: * -> *) stmt.
(InternalVarElim r, RenderStatement r stmt, ValueElim r) =>
SVariable r -> SValue r -> MS (r stmt)
increment SVariable r
vr' SValue r
v'= do
  vr <- LensLike'
  (Zoomed (StateT ValueState Identity) (r Variable))
  MethodState
  ValueState
-> SVariable r -> 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 SVariable r
vr'
  v <- zoom lensMStoVS v'
  mkStmtNoEnd $ R.addAssign vr v

-- Python, Julia, and MATLAB --

-- | Call to get the size of a list as a function call
listSize :: (ValueExpression r) => String -> SValue r -> SValue r
listSize :: forall (r :: * -> *).
ValueExpression r =>
String -> SValue r -> SValue r
listSize String
fnName SValue r
list = PosCall r
forall (r :: * -> *). ValueExpression r => PosCall r
funcApp String
fnName VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
int [SValue r
list]