package com.jme3.scene.plugins.blender.modifiers;

import java.util.List;

import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;

/**
 * This class represents an object's modifier. The modifier object can be varied
 * and the user needs to know what is the type of it for the specified type
 * name. For example "ArmatureModifierData" type specified in blender is
 * represented by AnimData object from jMonkeyEngine.
 * 
 * @author Marcin Roguski (Kaelthas)
 */
public abstract class Modifier {
    public static final String ARRAY_MODIFIER_DATA            = "ArrayModifierData";
    public static final String ARMATURE_MODIFIER_DATA         = "ArmatureModifierData";
    public static final String PARTICLE_MODIFIER_DATA         = "ParticleSystemModifierData";
    public static final String MIRROR_MODIFIER_DATA           = "MirrorModifierData";
    public static final String SUBSURF_MODIFIER_DATA          = "SubsurfModifierData";
    public static final String OBJECT_ANIMATION_MODIFIER_DATA = "ObjectAnimationModifierData";

    /** This variable indicates if the modifier is invalid (<b>true</b>) or not (<b>false</b>). */
    protected boolean          invalid;
    /**
     * A variable that tells if the modifier causes modification. Some modifiers like ArmatureModifier might have no
     * Armature object attached and thus not really modifying the feature. In such cases it is good to know if it is
     * sense to add the modifier to the list of object's modifiers.
     */
    protected boolean          modifying                      = true;

    /**
     * This method applies the modifier to the given node.
     * 
     * @param node
     *            the node that will have modifier applied
     * @param blenderContext
     *            the blender context
     */
    public abstract void apply(Node node, BlenderContext blenderContext);

    /**
     * The method that is called when geometries are already created.
     * @param node
     *            the node that will have the modifier applied
     * @param blenderContext
     *            the blender context
     */
    public void postMeshCreationApply(Node node, BlenderContext blenderContext) {
    }

    /**
     * Determines if the modifier can be applied multiple times over one mesh.
     * At this moment only armature and object animation modifiers cannot be
     * applied multiple times.
     * 
     * @param modifierType
     *            the type name of the modifier
     * @return <b>true</b> if the modifier can be applied many times and
     *         <b>false</b> otherwise
     */
    public static boolean canBeAppliedMultipleTimes(String modifierType) {
        return !(ARMATURE_MODIFIER_DATA.equals(modifierType) || OBJECT_ANIMATION_MODIFIER_DATA.equals(modifierType));
    }

    protected boolean validate(Structure modifierStructure, BlenderContext blenderContext) {
        Structure modifierData = (Structure) modifierStructure.getFieldValue("modifier");
        Pointer pError = (Pointer) modifierData.getFieldValue("error");
        invalid = pError.isNotNull();
        return !invalid;
    }

    /**
     * @return <b>true</b> if the modifier causes feature's modification or <b>false</b> if not
     */
    public boolean isModifying() {
        return modifying;
    }

    protected TemporalMesh getTemporalMesh(Node node) {
        List<Spatial> children = node.getChildren();
        if (children != null && children.size() == 1 && children.get(0) instanceof TemporalMesh) {
            return (TemporalMesh) children.get(0);
        }
        return null;
    }
}
