package org.distantstarschildren.jme.factory;

import com.jme3.asset.AssetManager;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.MeshCollisionShape;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.UserData;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import org.distantstarschildren.jme.DistantStarsChildrenApplication;
import org.distantstarschildren.jme.common.AssetConstants;
import org.distantstarschildren.jme.common.NodeConstants;
import org.distantstarschildren.jme.control.GameObjectCollisionControl;
import org.distantstarschildren.jme.common.PhysicsUtils;
import org.distantstarschildren.jme.common.SpatialsUtils;
import org.distantstarschildren.jme.control.BulletCollisionControl;
import org.distantstarschildren.jme.control.ExplosionControl;
import org.distantstarschildren.model.enemy.Enemy;
import org.distantstarschildren.model.level.LevelElement;
import org.distantstarschildren.model.player.Player;

/**
 *
 * @author Faith
 */
public class NodeFactory {

    private static NodeFactory instance;
    private AssetManager assetManager;
    private BulletAppState bulletAppState;
    private DistantStarsChildrenApplication dscApp;
    private Node rootNode;

    private NodeFactory(DistantStarsChildrenApplication dscApp) {
        assetManager = dscApp.getAssetManager();
        bulletAppState = dscApp.getStateManager().getState(BulletAppState.class);
        this.dscApp = dscApp;
        this.rootNode = dscApp.getRootNode();
    }

    public static synchronized NodeFactory getInstance(DistantStarsChildrenApplication dscApp) {
        if (instance == null) {
            instance = new NodeFactory(dscApp);
        }
        return instance;
    }

    public Node buildLevelElementNode(LevelElement element, Vector3f position) {
        String timeSuffix =  "_" + System.currentTimeMillis();
         final String elementId = NodeConstants.LEVEL_ELEMENT_ID + element.getId() + timeSuffix;
        final String elementIdSpatial = NodeConstants.LEVEL_ELEMENT_ID_SPATIAL + element.getId() + timeSuffix;

        Spatial elementSpatial = assetManager.loadModel(String.format(AssetConstants.MODEL_LEVEL_ELEMENT, element.getId()));
        elementSpatial.setName(elementIdSpatial);

        CollisionShape collisionShape = PhysicsUtils.getCollisionShape(elementSpatial);
        if (collisionShape != null) {
            GameObjectCollisionControl gameObjectCollisionControl = new GameObjectCollisionControl(bulletAppState, collisionShape, dscApp);
            bulletAppState.getPhysicsSpace().add(gameObjectCollisionControl);
            bulletAppState.getPhysicsSpace().addCollisionListener(gameObjectCollisionControl);
            elementSpatial.addControl(gameObjectCollisionControl);
            
            BulletCollisionControl bulletCollisionControl = new BulletCollisionControl(bulletAppState, collisionShape, dscApp);
            bulletAppState.getPhysicsSpace().add(bulletCollisionControl);
            bulletAppState.getPhysicsSpace().addTickListener(bulletCollisionControl);
            elementSpatial.addControl(bulletCollisionControl);
        }

        Node elementNode = new Node(elementId);
        elementNode.attachChild(elementSpatial);
        elementNode.move(position.getX(), position.getY(), position.getZ());
        return elementNode;
    }

    public Node buildPlayerNode(Player player) {
        Node playerNode = new Node(NodeConstants.PLAYER_ID);
        Spatial playerSpatial = assetManager.loadModel(AssetConstants.MODEL_PLAYER);
        playerSpatial.setName(NodeConstants.PLAYER_SPATIAL_ID);
        MeshCollisionShape collisionShape = new MeshCollisionShape(SpatialsUtils.getMeshFromSpatial(playerSpatial));
        collisionShape.setScale(new Vector3f(.525f, .7f, .7f));

        GameObjectCollisionControl gameObjectCollisionControl = new GameObjectCollisionControl(bulletAppState, collisionShape, dscApp);
        bulletAppState.getPhysicsSpace().add(gameObjectCollisionControl);
        bulletAppState.getPhysicsSpace().addCollisionListener(gameObjectCollisionControl);

        playerSpatial.addControl(gameObjectCollisionControl);

        playerNode.attachChild(playerSpatial);
        return playerNode;
    }
    
    public ParticleEmitter buildExplosionEmitter(Vector3f position){
        ParticleEmitter debrisEffect = new ParticleEmitter("explosion", ParticleMesh.Type.Triangle, 10);
        Material debrisMat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
        debrisMat.setTexture("Texture", assetManager.loadTexture("Effects/Debris.png"));
        debrisEffect.setMaterial(debrisMat);
        debrisEffect.setImagesX(1); 
        debrisEffect.setImagesY(1);
        debrisEffect.setRotateSpeed(4);
        debrisEffect.setSelectRandomImage(true);
        debrisEffect.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0f, -1f));
        debrisEffect.setStartColor(new ColorRGBA(1f, 0.5f, 0.0f, 0.5f));
        debrisEffect.setGravity(0f,0f,0f);
        debrisEffect.getParticleInfluencer().setVelocityVariation(.25f);
        debrisEffect.getParticleInfluencer().setInitialVelocity(new Vector3f(0f, 0f, -1f));
        debrisEffect.setLowLife(0.25f);
        debrisEffect.setHighLife(0.5f);
        debrisEffect.setLocalTranslation(position);
        
        debrisEffect.setUserData(NodeConstants.START_TIME, System.currentTimeMillis());
        debrisEffect.addControl(new ExplosionControl(rootNode));
        
        return debrisEffect;
    }
    
    public Node buildBulletNode(Vector3f origin, Vector3f direction){
        Sphere bullet = new Sphere(16, 16, 0.25f);
        Material bulletMat = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
        Geometry bulletGeo = new Geometry("geo", bullet);
        bulletGeo.setMaterial(bulletMat);
        Node bulletNode = new Node(NodeConstants.BULLET_ID + System.currentTimeMillis());
        bulletNode.attachChild(bulletGeo);
        
        bulletNode.setUserData(NodeConstants.NODE_COLLISION_SHAPE, "SPHERE");
        bulletNode.setUserData(NodeConstants.NODE_COLLISION_SHAPE_PARAM, "0.25");
        
        CollisionShape collisionShape = PhysicsUtils.getCollisionShape(bulletNode);
        if (collisionShape != null) {
            BulletCollisionControl bulletCollisionControl = new BulletCollisionControl(bulletAppState, collisionShape, dscApp);
            bulletAppState.getPhysicsSpace().add(bulletCollisionControl);
            bulletAppState.getPhysicsSpace().addTickListener(bulletCollisionControl);
            bulletNode.addControl(bulletCollisionControl);
        }
        
        bulletNode.move(origin);
        bulletNode.setUserData("DIRECTION", new UserData(Byte.valueOf("0"), direction));
        bulletNode.setUserData(NodeConstants.START_TIME, System.currentTimeMillis());
        
        return bulletNode;
    }
    
    public Node buildEnemyNode(Enemy enemy){
        Node mockEnemy = new Node("enemy_id_001_" + System.currentTimeMillis());
        Spatial spatial = assetManager.loadModel("Models/enemy_enemy_id_001.j3o");
        mockEnemy.attachChild(spatial);
        return mockEnemy;
    }
}
