/**
  * reads a list of ShaderNode{} blocks
  *
  * @param statements the list of statements to parse
  * @throws IOException
  */
 protected void readShaderNode(List<Statement> statements) throws IOException {
   for (Statement statement : statements) {
     String line = statement.getLine();
     String[] split = statement.getLine().split("[ \\{]");
     if (line.startsWith("Definition")) {
       ShaderNodeDefinition def = findDefinition(statement);
       shaderNode.setDefinition(def);
       if (def.isNoOutput()) {
         techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(shaderNode.getName());
       }
     } else if (line.startsWith("Condition")) {
       String condition = line.substring(line.lastIndexOf(":") + 1).trim();
       extractCondition(condition, statement);
       shaderNode.setCondition(conditionParser.getFormattedExpression());
     } else if (line.startsWith("InputMapping")) {
       for (Statement statement1 : statement.getContents()) {
         VariableMapping mapping = readInputMapping(statement1);
         techniqueDef
             .getShaderGenerationInfo()
             .getUnusedNodes()
             .remove(mapping.getRightVariable().getNameSpace());
         shaderNode.getInputMapping().add(mapping);
       }
     } else if (line.startsWith("OutputMapping")) {
       for (Statement statement1 : statement.getContents()) {
         VariableMapping mapping = readOutputMapping(statement1);
         techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(shaderNode.getName());
         shaderNode.getOutputMapping().add(mapping);
       }
     } else {
       throw new MatParseException("ShaderNodeDefinition", split[0], statement);
     }
   }
 }
  /**
   * reads an output mapping
   *
   * @param statement1 the staement being read
   * @return the mapping
   * @throws IOException
   */
  public VariableMapping readOutputMapping(Statement statement1) throws IOException {
    VariableMapping mapping = null;
    try {
      mapping = parseMapping(statement1, new boolean[] {true, false});
    } catch (Exception e) {
      throw new MatParseException("Unexpected mapping format", statement1, e);
    }
    ShaderNodeVariable left = mapping.getLeftVariable();
    ShaderNodeVariable right = mapping.getRightVariable();

    if (left.getType().startsWith("sampler") || right.getType().startsWith("sampler")) {
      throw new MatParseException("Samplers can only be inputs", statement1);
    }

    if (left.getNameSpace().equals("Global")) {
      left.setType("vec4"); // Globals are all vec4 for now (maybe forever...)
      storeGlobal(left, statement1);
    } else {
      throw new MatParseException(
          "Only Global nameSpace is allowed for outputMapping, got" + left.getNameSpace(),
          statement1);
    }

    if (!updateVariableFromList(right, shaderNode.getDefinition().getOutputs())) {
      throw new MatParseException(
          right.getName() + " is not an output variable of " + shaderNode.getDefinition().getName(),
          statement1);
    }

    checkTypes(mapping, statement1);

    return mapping;
  }
 /**
  * check the types of a mapping, left type must match right type tkae the swizzle into account
  *
  * @param mapping the mapping
  * @param statement1 the statement being read
  * @throws MatParseException
  */
 protected void checkTypes(VariableMapping mapping, Statement statement1)
     throws MatParseException {
   if (!ShaderUtils.typesMatch(mapping)) {
     String ls = mapping.getLeftSwizzling().length() == 0 ? "" : "." + mapping.getLeftSwizzling();
     String rs =
         mapping.getRightSwizzling().length() == 0 ? "" : "." + mapping.getRightSwizzling();
     throw new MatParseException(
         "Type mismatch, cannot convert "
             + mapping.getRightVariable().getType()
             + rs
             + " to "
             + mapping.getLeftVariable().getType()
             + ls,
         statement1);
   }
   if (!ShaderUtils.multiplicityMatch(mapping)) {
     String type1 =
         mapping.getLeftVariable().getType()
             + "["
             + mapping.getLeftVariable().getMultiplicity()
             + "]";
     String type2 =
         mapping.getRightVariable().getType()
             + "["
             + mapping.getRightVariable().getMultiplicity()
             + "]";
     throw new MatParseException(
         "Type mismatch, cannot convert " + type1 + " to " + type2, statement1);
   }
 }
 /**
  * updates the type of the right variable of a mapping from the type of the left variable
  *
  * @param mapping the mapping to consider
  */
 protected void updateRightTypeFromLeftType(VariableMapping mapping) {
   String type = mapping.getLeftVariable().getType();
   int card = ShaderUtils.getCardinality(type, mapping.getRightSwizzling());
   if (card > 0) {
     if (card == 1) {
       type = "float";
     } else {
       type = "vec" + card;
     }
   }
   mapping.getRightVariable().setType(type);
 }
 /**
  * updates the right variable of the given mapping from a UniformBinding (a WorldParam) it checks
  * if the unifrom hasn't already been loaded, add it to the maps if not.
  *
  * @param param the WorldParam UniformBinding
  * @param mapping the mapping
  * @param map the map of uniforms to search into
  * @return true if the param was added to the map
  */
 protected boolean updateRightFromUniforms(
     UniformBinding param, VariableMapping mapping, Map<String, DeclaredVariable> map) {
   ShaderNodeVariable right = mapping.getRightVariable();
   String name = "g_" + param.toString();
   DeclaredVariable dv = map.get(name);
   if (dv == null) {
     right.setType(param.getGlslType());
     right.setName(name);
     dv = new DeclaredVariable(right);
     map.put(right.getName(), dv);
     dv.addNode(shaderNode);
     mapping.setRightVariable(right);
     return true;
   }
   dv.addNode(shaderNode);
   mapping.setRightVariable(dv.var);
   return false;
 }
 /**
  * updates a variable from the Attribute list
  *
  * @param right the variable
  * @param mapping the mapping
  */
 public void updateVarFromAttributes(ShaderNodeVariable right, VariableMapping mapping) {
   DeclaredVariable dv = attributes.get(right.getName());
   if (dv == null) {
     dv = new DeclaredVariable(right);
     attributes.put(right.getName(), dv);
     updateRightTypeFromLeftType(mapping);
   } else {
     mapping.setRightVariable(dv.var);
   }
   dv.addNode(shaderNode);
 }
  /**
   * store a varying
   *
   * @param node the shaderNode
   * @param variable the variable to store
   */
  public void storeVaryings(ShaderNode node, ShaderNodeVariable variable) {
    variable.setShaderOutput(true);
    if (node.getDefinition().getType() == Shader.ShaderType.Vertex
        && shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) {
      DeclaredVariable dv = varyings.get(variable.getName());
      if (dv == null) {
        techniqueDef.getShaderGenerationInfo().getVaryings().add(variable);
        dv = new DeclaredVariable(variable);

        varyings.put(variable.getName(), dv);
      }
      dv.addNode(shaderNode);
      // if a variable is declared with the same name as an input and an output and is a varying,
      // set it as a shader output so it's declared as a varying only once.
      for (VariableMapping variableMapping : node.getInputMapping()) {
        if (variableMapping.getLeftVariable().getName().equals(variable.getName())) {
          variableMapping.getLeftVariable().setShaderOutput(true);
        }
      }
    }
  }
  protected void computeConditions() {

    updateConditions(vertexDeclaredUniforms);
    updateConditions(fragmentDeclaredUniforms);
    updateConditions(varyings);

    for (DeclaredVariable v : varyings.values()) {
      for (ShaderNode sn : techniqueDef.getShaderNodes()) {
        if (sn.getDefinition().getType() == Shader.ShaderType.Vertex) {
          for (VariableMapping mapping : sn.getInputMapping()) {
            if (mapping.getLeftVariable().equals(v.var)) {
              if (mapping.getCondition() == null || v.var.getCondition() == null) {
                mapping.setCondition(v.var.getCondition());
              } else {
                mapping.setCondition(
                    "(" + mapping.getCondition() + ") || (" + v.var.getCondition() + ")");
              }
            }
          }
        }
      }
    }

    updateConditions(attributes);
    //        updateConditions(fragmentGlobals);
    //        vertexGlobal.makeCondition();
  }
 /**
  * updates the right variable of the given mapping from a MatParam (a WorldParam) it checks if the
  * unifrom hasn't already been loaded, add it to the maps if not.
  *
  * @param param the MatParam
  * @param mapping the mapping
  * @param map the map of uniforms to search into
  * @return true if the param was added to the map
  */
 public boolean updateRightFromUniforms(
     MatParam param,
     VariableMapping mapping,
     Map<String, DeclaredVariable> map,
     Statement statement)
     throws MatParseException {
   ShaderNodeVariable right = mapping.getRightVariable();
   DeclaredVariable dv = map.get(param.getPrefixedName());
   if (dv == null) {
     right.setType(param.getVarType().getGlslType());
     right.setName(param.getPrefixedName());
     if (mapping.getLeftVariable().getMultiplicity() != null) {
       if (!param.getVarType().name().endsWith("Array")) {
         throw new MatParseException(param.getName() + " is not of Array type", statement);
       }
       String multiplicity = mapping.getLeftVariable().getMultiplicity();
       try {
         Integer.parseInt(multiplicity);
       } catch (NumberFormatException nfe) {
         // multiplicity is not an int attempting to find for a material parameter.
         MatParam mp = findMatParam(multiplicity);
         if (mp != null) {
           addDefine(multiplicity, VarType.Int);
           multiplicity = multiplicity.toUpperCase();
         } else {
           throw new MatParseException(
               "Wrong multiplicity for variable"
                   + mapping.getLeftVariable().getName()
                   + ". "
                   + multiplicity
                   + " should be an int or a declared material parameter.",
               statement);
         }
       }
       right.setMultiplicity(multiplicity);
     }
     dv = new DeclaredVariable(right);
     map.put(right.getName(), dv);
     dv.addNode(shaderNode);
     mapping.setRightVariable(right);
     return true;
   }
   dv.addNode(shaderNode);
   mapping.setRightVariable(dv.var);
   return false;
 }
    public void makeCondition() {
      var.setCondition(null);

      for (ShaderNode node : nodes) {
        String condition = null;
        for (VariableMapping mapping : node.getInputMapping()) {
          if (mapping.getRightVariable().equals(var)) {
            if (mapping.getCondition() == null) {
              condition = null;
              break;
            }
            if (condition == null) {
              condition = "(" + mapping.getCondition() + ")";
            } else {
              if (!condition.contains(mapping.getCondition())) {
                condition = condition + " || (" + mapping.getCondition() + ")";
              }
            }
          }
        }
        if (node.getCondition() == null && condition == null) {
          var.setCondition(null);
          return;
        }
        if (node.getCondition() != null) {
          if (condition == null) {
            condition = node.getCondition();
          } else {
            if (!condition.contains(node.getCondition())) {
              condition = "(" + node.getCondition() + ") && (" + condition + ")";
            }
          }
        }
        if (var.getCondition() == null) {
          var.setCondition(condition);
        } else {
          if (!var.getCondition().contains(condition)) {
            var.setCondition("(" + var.getCondition() + ") || (" + condition + ")");
          }
        }
      }
    }
  /**
   * reads a mapping statement. Sets the nameSpace, name and swizzling of the left variable. Sets
   * the name, nameSpace and swizzling of the right variable types will be determined later. <code>
   * Format : <nameSpace>.<varName>[.<swizzling>] =
   * <nameSpace>.<varName>[.<swizzling>][:Condition]
   * </code>
   *
   * @param statement the statement to read
   * @return the read mapping
   */
  protected VariableMapping parseMapping(Statement statement, boolean[] hasNameSpace)
      throws IOException {
    VariableMapping mapping = new VariableMapping();
    String[] cond = statement.getLine().split(":");

    String[] vars = cond[0].split("=");
    checkMappingFormat(vars, statement);
    ShaderNodeVariable[] variables = new ShaderNodeVariable[2];
    String[] swizzle = new String[2];
    for (int i = 0; i < vars.length; i++) {
      String[] expression = vars[i].trim().split("\\.");
      if (hasNameSpace[i]) {
        if (expression.length <= 3) {
          variables[i] = new ShaderNodeVariable("", expression[0].trim(), expression[1].trim());
        }
        if (expression.length == 3) {
          swizzle[i] = expression[2].trim();
        }
      } else {
        if (expression.length <= 2) {
          variables[i] = new ShaderNodeVariable("", expression[0].trim());
        }
        if (expression.length == 2) {
          swizzle[i] = expression[1].trim();
        }
      }
    }

    mapping.setLeftVariable(variables[0]);
    mapping.setLeftSwizzling(swizzle[0] != null ? swizzle[0] : "");
    mapping.setRightVariable(variables[1]);
    mapping.setRightSwizzling(swizzle[1] != null ? swizzle[1] : "");

    if (cond.length > 1) {
      extractCondition(cond[1], statement);
      mapping.setCondition(conditionParser.getFormattedExpression());
    }

    return mapping;
  }
  /**
   * reads an input mapping
   *
   * @param statement1 the statement being read
   * @return the mapping
   * @throws IOException
   */
  public VariableMapping readInputMapping(Statement statement1) throws IOException {
    VariableMapping mapping = null;
    try {
      mapping = parseMapping(statement1, new boolean[] {false, true});
    } catch (Exception e) {
      throw new MatParseException("Unexpected mapping format", statement1, e);
    }
    ShaderNodeVariable left = mapping.getLeftVariable();
    ShaderNodeVariable right = mapping.getRightVariable();
    if (!updateVariableFromList(left, shaderNode.getDefinition().getInputs())) {
      throw new MatParseException(
          left.getName() + " is not an input variable of " + shaderNode.getDefinition().getName(),
          statement1);
    }

    if (left.getType().startsWith("sampler") && !right.getNameSpace().equals("MatParam")) {
      throw new MatParseException("Samplers can only be assigned to MatParams", statement1);
    }

    if (right.getNameSpace().equals("Global")) {
      right.setType("vec4"); // Globals are all vec4 for now (maybe forever...)
      //        updateCondition(right, mapping);
      storeGlobal(right, statement1);

    } else if (right.getNameSpace().equals("Attr")) {
      if (shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) {
        throw new MatParseException(
            "Cannot have an attribute as input in a fragment shader" + right.getName(), statement1);
      }
      updateVarFromAttributes(mapping.getRightVariable(), mapping);
      //          updateCondition(mapping.getRightVariable(), mapping);
      storeAttribute(mapping.getRightVariable());
    } else if (right.getNameSpace().equals("MatParam")) {
      MatParam param = findMatParam(right.getName());
      if (param == null) {
        throw new MatParseException(
            "Could not find a Material Parameter named " + right.getName(), statement1);
      }
      if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) {
        if (updateRightFromUniforms(param, mapping, vertexDeclaredUniforms, statement1)) {
          storeVertexUniform(mapping.getRightVariable());
        }
      } else {
        if (updateRightFromUniforms(param, mapping, fragmentDeclaredUniforms, statement1)) {
          if (mapping.getRightVariable().getType().contains("|")) {
            String type = fixSamplerType(left.getType(), mapping.getRightVariable().getType());
            if (type != null) {
              mapping.getRightVariable().setType(type);
            } else {
              throw new MatParseException(
                  param.getVarType().toString()
                      + " can only be matched to one of "
                      + param.getVarType().getGlslType().replaceAll("\\|", ",")
                      + " found "
                      + left.getType(),
                  statement1);
            }
          }
          storeFragmentUniform(mapping.getRightVariable());
        }
      }

    } else if (right.getNameSpace().equals("WorldParam")) {
      UniformBinding worldParam = findWorldParam(right.getName());
      if (worldParam == null) {
        throw new MatParseException(
            "Could not find a World Parameter named " + right.getName(), statement1);
      }
      if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) {
        if (updateRightFromUniforms(worldParam, mapping, vertexDeclaredUniforms)) {
          storeVertexUniform(mapping.getRightVariable());
        }
      } else {
        if (updateRightFromUniforms(worldParam, mapping, fragmentDeclaredUniforms)) {
          storeFragmentUniform(mapping.getRightVariable());
        }
      }

    } else {
      ShaderNode node = nodes.get(right.getNameSpace());
      if (node == null) {
        throw new MatParseException(
            "Undeclared node"
                + right.getNameSpace()
                + ". Make sure this node is declared before the current node",
            statement1);
      }
      ShaderNodeVariable var = findNodeOutput(node.getDefinition().getOutputs(), right.getName());
      if (var == null) {
        throw new MatParseException(
            "Cannot find output variable" + right.getName() + " form ShaderNode " + node.getName(),
            statement1);
      }
      right.setNameSpace(node.getName());
      right.setType(var.getType());
      right.setMultiplicity(var.getMultiplicity());
      mapping.setRightVariable(right);
      storeVaryings(node, mapping.getRightVariable());
    }

    checkTypes(mapping, statement1);

    return mapping;
  }