/**
   * Creates a new effect, with parameters passed as InteractionContent.
   *
   * @param content parameters for the effect, including its class.
   * @return the new effect, created and initialized, ready to be triggered!
   */
  public Effect createEffect(InteractionContent content) {
    try {
      Class effectClass = registeredEffects.get(content.function);
      Constructor effectConstructor =
          effectClass.getConstructor(new Class[] {InteractionContent.class});
      Effect ef = (Effect) effectConstructor.newInstance(new Object[] {content});

      if (content.object1.equalsIgnoreCase("TIME") || content.object2[0].equalsIgnoreCase("TIME"))
        return new TimeEffect(content, ef);

      return ef;

    } catch (NoSuchMethodException e) {
      e.printStackTrace();
      System.out.println(
          "Error creating effect "
              + content.function
              + " between "
              + content.object1
              + " and "
              + content.object2);
    } catch (Exception e) {
      e.printStackTrace();
      System.out.println(
          "Error creating effect "
              + content.function
              + " between "
              + content.object1
              + " and "
              + content.object2);
    }

    return null;
  }
  /**
   * Creates a game, receiving a GameContent object
   *
   * @param content potential parameters for the class.
   * @return The game just created.
   */
  public Game createGame(GameContent content) {
    try {
      Class gameClass = registeredGames.get(content.referenceClass);
      Constructor gameConstructor = gameClass.getConstructor(new Class[] {GameContent.class});
      return (Game) gameConstructor.newInstance(new Object[] {content});

    } catch (NoSuchMethodException e) {
      e.printStackTrace();
      System.out.println("Error creating game of class " + content.referenceClass);
    } catch (Exception e) {
      e.printStackTrace();
      System.out.println("Error creating game of class " + content.referenceClass);
    }

    return null;
  }
  /**
   * Creates a new termination, with parameters passed as TerminationContent.
   *
   * @param content parameters for the termination condition, including its class.
   * @return the new termination, created and initialized, ready to be checked!
   */
  public Termination createTermination(TerminationContent content) {
    try {
      Class terminationClass = registeredTerminations.get(content.identifier);
      Constructor terminationConstructor =
          terminationClass.getConstructor(new Class[] {TerminationContent.class});
      Termination ter = (Termination) terminationConstructor.newInstance(new Object[] {content});
      return ter;

    } catch (NoSuchMethodException e) {
      e.printStackTrace();
      System.out.println("Error creating termination condition " + content.identifier);
    } catch (Exception e) {
      e.printStackTrace();
      System.out.println("Error creating termination condition " + content.identifier);
    }

    return null;
  }
  /**
   * Creates a new sprite with a given dimension in a certain position. Parameters are passed as
   * SpriteContent.
   *
   * @param content parameters for the sprite, including its class.
   * @param position position of the object.
   * @param dim dimensions of the sprite on the world.
   * @return the new sprite, created and initialized, ready for play!
   */
  public VGDLSprite createSprite(SpriteContent content, Vector2d position, Dimension dim) {
    try {
      Class spriteClass = registeredSprites.get(content.referenceClass);
      Constructor spriteConstructor =
          spriteClass.getConstructor(
              new Class[] {Vector2d.class, Dimension.class, SpriteContent.class});
      return (VGDLSprite) spriteConstructor.newInstance(new Object[] {position, dim, content});

    } catch (NoSuchMethodException e) {
      e.printStackTrace();
      System.out.println(
          "Error creating sprite " + content.identifier + " of class " + content.referenceClass);
    } catch (Exception e) {
      e.printStackTrace();
      System.out.println(
          "Error creating sprite " + content.identifier + " of class " + content.referenceClass);
    }

    return null;
  }
  /**
   * Parses the parameters from content, assigns them to variables in obj.
   *
   * @param content contains the parameters to read.
   * @param obj object with the variables to assign.
   */
  public void parseParameters(Content content, Object obj) {
    // Get all fields from the class and store it as key->field
    Field[] fields = obj.getClass().getFields();
    HashMap<String, Field> fieldMap = new HashMap<String, Field>();
    for (Field field : fields) {
      String strField = field.toString();
      int lastDot = strField.lastIndexOf(".");
      String fieldName = strField.substring(lastDot + 1).trim();

      fieldMap.put(fieldName, field);
    }
    Object objVal = null;
    Field cfield = null;
    // Check all parameters from content
    for (String parameter : content.parameters.keySet()) {
      String value = content.parameters.get(parameter);
      if (fieldMap.containsKey(parameter)) {

        try {
          cfield = Types.processField(value);
          objVal = cfield.get(null);
        } catch (Exception e) {
          try {
            if (!parameter.equalsIgnoreCase("scoreChange")) objVal = Integer.parseInt(value);
            else objVal = value;
          } catch (NumberFormatException e1) {
            try {
              objVal = Double.parseDouble(value);
            } catch (NumberFormatException e2) {
              try {
                if ((value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false"))
                    && !parameter.equalsIgnoreCase("win")) objVal = Boolean.parseBoolean(value);
                else objVal = value;
              } catch (NumberFormatException e3) {
                objVal = value;
              }
            }
          }
        }
        try {
          fieldMap.get(parameter).set(obj, objVal);
        } catch (IllegalAccessException e) {
          e.printStackTrace(); // To change body of catch statement use File | Settings | File
          // Templates.
        } catch (Exception e) {
          e.printStackTrace();
        }
      } else {
        // Ignore unknown fields in dependent Effects (TimeEffect).
        boolean warn = true;
        boolean isInteraction = (content instanceof InteractionContent);
        if (isInteraction) {
          boolean isTimeEffect =
              ((InteractionContent) content).object2[0].equalsIgnoreCase("TIME")
                  || ((InteractionContent) content).object1.equalsIgnoreCase("TIME")
                  || (((InteractionContent) content).line.contains("addTimer"));
          if (isTimeEffect) warn = false;
        }

        if (warn)
          System.out.println(
              "Unknown field (" + parameter + "=" + value + ") from " + content.toString());
      }
    }
  }