 
 GE.nodeVisitors =
   "action": GE.visitActionNode
+  "process": GE.visitProcessNode
   "foreach": GE.visitForeachNode
   "send": GE.visitSendNode
 
-# Constants are modelData, assets, actions, and serviceData
+# Constants are modelData, assets, actions, processes and serviceData
 GE.visitNode = (node, constants, bindings = {}) ->
   # TODO: defer action and call execution until whole tree is evaluated?
   # TODO: handle children as object in addition to array
-
   for nodeType, visitor of GE.nodeVisitors
     if nodeType of node
       return visitor(node, constants, bindings)


  return false

# Catches all errors in the function 
# The signals parameter is only used in the "handleSignals" call
GE.sandboxActionCall = (node, constants, bindings, methodName, signals = [], activeChildren = []) ->
  action = constants.actions[node.action]
  childNames = if node.children? then (child.name ? index.toString()) for index, child of node.children else []

  # TODO: compile expressions ahead of time
  evaluationContext = new GE.EvaluationContext(constants, bindings)

  # Set default paramOptions
  # Cannot use normal "for key, value of" loop because cannot handle replace null values
  for paramName of action.paramDefs
    if not action.paramDefs[paramName]? then action.paramDefs[paramName] = {}
    _.defaults action.paramDefs[paramName], 
      direction: "in"

  evaluatedParams = {}

  for paramName, paramOptions of action.paramDefs
    # Resolve parameter value. If the layout doesn't specify a value, use the default, it it exists. Otherwise, throw exception for input values
    if paramOptions.direction in ["in", "inout"] and node.params?.in?[paramName] 
      paramValue = node.params.in[paramName]
    else if paramOptions.direction in ["out", "inout"] and node.params?.out?[paramName] 
      paramValue = node.params.out[paramName]
    else if paramOptions.default? 
      paramValue = paramOptions.default
    else if paramOptions.direction in ["in", "inout"]
      throw new Error("Missing input parameter value for action: #{node.action}")

    try
      evaluatedParams[paramName] = evaluationContext.evaluateExpression(paramValue)
    catch error
      throw new Error("Error evaluating the input parameter expression '#{paramValue}' for node '#{node.action}':\n#{error.stack}")

  locals = 
    params: evaluatedParams
    children: childNames
    signals: signals
    activeChildren: activeChildren
    assets: constants.assets
    tools: constants.tools
    log: constants.log
  try
    methodResult = action[methodName].apply(locals)
  catch e
    # TODO: convert exceptions to error sigals that do not create patches
    constants.log(GE.logLevels.ERROR, "Calling action #{node.action}.#{methodName} raised an exception #{e}. Input params were #{JSON.stringify(locals.params)}. Children are #{JSON.stringify(locals.children)}.\n#{e.stack}")

  result = new GE.NodeVisitorResult(methodResult)

  # Only output parameters should be accessible
  outParams = _.pick(evaluatedParams, (paramName for paramName, paramOptions of action.paramDefs when paramOptions.direction in ["out", "inout"]))

  for paramName, paramValue of node.params.out
    try
      outputValue = evaluationContext.evaluateExpression(paramValue, outParams)
    catch error
      throw new Error("Error evaluating the output parameter value expression '#{paramValue}' for node '#{node.action}':\n#{error.stack}\nOutput params were #{JSON.stringify(outputParams)}.")

    [parent, key] = GE.getParentAndKey(evaluationContext, paramName.split("."))
    parent[key] = outputValue

  result.modelPatches = GE.makePatches(constants.modelData, evaluationContext.model)
  result.servicePatches = GE.makePatches(constants.serviceData, evaluationContext.services)

  return result

GE.calculateBindingSet = (node, constants, oldBindings) ->
  bindingSet = []


  return modelPatches

# Compile expression source into sandboxed function of (model, services, assets, tools, bindings, params) 
GE.compileExpression = (expressionText, evaluator) -> GE.compileSource("return #{expressionText};", evaluator, ["model", "services", "assets", "tools", "bindings", "params"])

# Compile tool source into sandboxed function of args..., "baking in" the "tools" and "log" parameters of "context" 
GE.compileTool = (expressionText, context, args, evaluator) -> 
  source = """
    return function(#{args.join(', ')}) { 
      var tools = context.tools; 
      var log = context.log; 
      #{expressionText} 
    };
  """
  return GE.compileSource(source, evaluator, ["context"])

# Compile action.update() source into sandboxed function of (params, tools, log) 
GE.compileUpdate = (expressionText, evaluator) -> GE.compileSource(expressionText, evaluator, ["params", "tools", "log"])

# Compile action listActiveChildren source into sandboxed function of (params, children, tools, log) 
GE.compileListActiveChildren = (expressionText, evaluator) -> GE.compileSource(expressionText, evaluator, ["params", "children", "tools", "log"])

# Compile action handleSignals source into sandboxed function of (params, children, activeChildren, signals, tools, log) 
GE.compileHandleSignals = (expressionText, evaluator) -> GE.compileSource(expressionText, evaluator, ["params", "children", "activeChildren", "signals", "tools", "log"])

# Compile source into sandboxed function of params
GE.compileSource = (expressionText, evaluator, params) ->
  # Parentheses are needed around function because of strange JavaScript syntax rules
  # TODO: use "new Function" instead of eval? 
  # TODO: add "use strict"?
  # TODO: detect errors
  functionText = "(function(#{params.join(', ')}) {\n#{expressionText}\n})"
  expressionFunc = evaluator(functionText)
  if typeof(expressionFunc) isnt "function" then throw new Error("Expression does not evaluate as a function") 
  return expressionFunc
