package ei.engine.effects;

import java.awt.Rectangle;

import org.lwjgl.opengl.GL11;

import ei.engine.scene.Entity;
import ei.engine.texture.Texture;
import ei.engine.texture.TextureLoader;

/**
 * @author Mark Bernard
 * date:    23-Jun-2004
 * @author Ziver Koc
 * date:    19-Mar-2007
 *
 * Port of NeHe's Lesson 19 to LWJGL
 * Title: Particle Engine Using Triangle Strips
 * Uses version 0.9alpha of LWJGL http://www.lwjgl.org/
 *
 */
public class Particles extends Entity{
    public boolean regenerate = true;
    public boolean rainbow = true;             // Rainbow Mode?

    public float slowdown = 1.0f;              // Slow Down Particles
    public float xspeed = 0.0f;                  // Base X Speed (To Allow Keyboard Direction Of Tail)
    public float yspeed = 0.0f;                 // Base Y Speed (To Allow Keyboard Direction Of Tail)
    
    public float MaxSpeedX = 500;				// The Max Random Speed On X Axis
    public float MaxSpeedY = 500;				// The Max Random Speed On Y Axis
    public float MaxSpeedZ = 500;				// The Max Random Speed On Z Axis
    
    public float pullForceX = 0.0f;				// Set Horizontal Pull To Zero
    public float pullForceY = -0.8f;			// Set Vertical Pull Downward
    public float pullForceZ = 0.0f;				// Set Pull On Z Axis To Zero
    
    public float life = 1.0f;
    public float size = 20.0f;    
    
    public float colors[][][]=       // Rainbow Of Colors
    {
        {{1.0f,0.5f,0.5f},{1.0f,1.0f,0.5f}},
        {{1.0f,0.0f,0.5f},{0.0f,1.0f,0.5f}},
        {{0.0f,1.0f,0.5f},{0.0f,0.0f,1.0f}},
        {{1.0f,0.0f,1.0f},{1.0f,0.0f,0.0f}}
    };
    
	private int maxParticles = 300;
    private boolean enabled = true;
    private Particle particle[];
    private int col;                            // Current Color Selection
    private int delay;                          // Rainbow Effect Delay
    private Texture texture;                        // Storage For Our Particle Texture

    /**
     * Initiats the particle engine
     * 
     * @param name The name of the particle engine
     */
    public Particles(String name){
    	super(name);
		this.texture = TextureLoader.getTextureLoaderInstance().getTexture("data/particle.bmp");
		setDefault();
		reset();
    }
          
    private void setDefault() {
        MaxSpeedX = 500;
        MaxSpeedY = 500;
        MaxSpeedZ = 0;
        
        pullForceX = 0.0f;
        pullForceY = 0.0f;
        pullForceZ = 0.0f;
        
        life = 1.0f;
        size = 20.0f;    
        regenerate = true;
    }
    
    /**
     * Set the number of particles
     * WARNING this will reset all the particles!!
     * 
     * @param max The number of particles
     */
    public void setMaxParticles(int max){
    	maxParticles = max;
    	reset();
    }
    
    /**
     * Resets all the paricals
     *
     */
    public void reset(){
        particle = new Particle[maxParticles];

        for(int i=0;i<maxParticles;i++) {
            particle[i] = new Particle();
            particle[i].life = -1.0f;
        }
        
        init();
    }
    
    private void init() {
        for (int i=0;i<maxParticles;i++){                           // Initials All The Textures
            particle[i].active = true;                              // Make All The Particles Active
            particle[i].life = (float) (Math.random()*(life+0.7f));        // Give All The Particles Full Life
            particle[i].fade = ((float)(Math.random() * 100.0)) / 1000.0f + 0.003f;   // Random Fade Speed
            particle[i].r = colors[i * (colors.length / maxParticles)][0][0];    // Select Red Rainbow Color
            particle[i].g = colors[i * (colors.length / maxParticles)][0][1];    // Select Red Rainbow Color
            particle[i].b = colors[i * (colors.length / maxParticles)][0][2];    // Select Red Rainbow Color
            particle[i].colorId = i * (colors.length / maxParticles);
            particle[i].xi = ((float)((Math.random() * MaxSpeedX)) - (MaxSpeedX/2)) * 10.0f;       // Random Speed On X Axis
            particle[i].yi = ((float)((Math.random() * MaxSpeedY)) - (MaxSpeedY/2)) * 10.0f;       // Random Speed On Y Axis
            particle[i].zi = ((float)((Math.random() * MaxSpeedZ)) - (MaxSpeedZ/2)) * 10.0f;       // Random Speed On Z Axis
            particle[i].xg = pullForceX;                                   // Set Horizontal Pull To Zero
            particle[i].yg = pullForceY;                                    // Set Vertical Pull Downward
            particle[i].zg = pullForceZ;                                     // Set Pull On Z Axis To Zero
            particle[i].x = getLocation().getX();        // Center On X Axis
            particle[i].y = getLocation().getY();        // Center On Y Axis
            particle[i].z = getLocation().getZ();        // Center On Z Axis        
        }
    }
    
    /**
     * Updates the particles
     */
    public void update() {
        if(rainbow && (delay > 25)) {
            delay = 0;                      // Reset The Rainbow Color Cycling Delay
            col++;                          // Change The Particle Color
            if(col >= colors.length) {
                col = 0;                    // If Color Is Too High Reset It
            }
        }        
        delay++;                            // Increase Rainbow Mode Color Cycling Delay Counter
    }
    
    /**
     * Draw the particles
     */
    public void render() {
    	if(enabled){
			// store the current model matrix
			GL11.glPushMatrix();
	
	        texture.bindGL();
	        
	        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
	        
	        for(int i=0;i<maxParticles;i++) {                      // Loop Through All The Particles
	            if (particle[i].active) {                           // If The Particle Is Active
	                float x = particle[i].x;                        // Grab Our Particle X Position
	                float y = particle[i].y;                        // Grab Our Particle Y Position
	                float z = particle[i].z;                 		// Particle Z Pos + Zoom
	
	                // Draw The Particle Using Our RGB Values, Fade The Particle Based On It's Life
	                GL11.glColor4f(particle[i].r, particle[i].g, particle[i].b, particle[i].life);
	                //Build Quad From A Triangle Strip
	                GL11.glBegin(GL11.GL_TRIANGLE_STRIP);{
		                //Top Right
		                GL11.glTexCoord2f(1.0f, 1.0f);
		                GL11.glVertex3f(x + size, y + size, z);
		                //Top Left
		                GL11.glTexCoord2f(0.0f, 1.0f);
		                GL11.glVertex3f(x - size, y + size, z);
		                //Bottom Right
		                GL11.glTexCoord2f(1.0f, 0.0f);
		                GL11.glVertex3f(x + size, y - size, z);
		                //Bottom Left
		                GL11.glTexCoord2f(0.0f, 0.0f);
		                GL11.glVertex3f(x - size, y - size, z);
	                //Done Building Triangle Strip
	                }GL11.glEnd();
	
	                particle[i].x += particle[i].xi / (slowdown * 1000);// Move On The X Axis By X Speed
	                particle[i].y += particle[i].yi / (slowdown * 1000);// Move On The Y Axis By Y Speed
	                particle[i].z += particle[i].zi / (slowdown * 1000);// Move On The Z Axis By Z Speed
	
	                particle[i].xi += particle[i].xg;           // Take Pull On X Axis Into Account
	                particle[i].yi += particle[i].yg;           // Take Pull On Y Axis Into Account
	                particle[i].zi += particle[i].zg;           // Take Pull On Z Axis Into Account
	                particle[i].life -= particle[i].fade;       // Reduce Particles Life By 'Fade'
	
	                // Fading to another color
                    if(particle[i].r < colors[particle[i].colorId][1][0])
                    	particle[i].r += (particle[i].fade/life);
                    if(particle[i].r > colors[particle[i].colorId][1][0])
                    	particle[i].r -= (particle[i].fade/life);
                    
                    if(particle[i].g < colors[particle[i].colorId][1][1])
                    	particle[i].g += (particle[i].fade/life);
                    if(particle[i].g > colors[particle[i].colorId][1][1])
                    	particle[i].g -= (particle[i].fade/life);
                    
                    if(particle[i].b < colors[particle[i].colorId][1][2])
                    	particle[i].b += (particle[i].fade/life);
                    if(particle[i].b > colors[particle[i].colorId][1][2])
                    	particle[i].b -= (particle[i].fade/life); 
                    
                    
	                if (particle[i].life < 0.0f) {                  // If Particle Is Burned Out
	                	particle[i].active = true;                              // Make All The Particles Active
	                    particle[i].life = life;                   // Give It New Life
	                    particle[i].fade = ((float)(Math.random() * 100.0)) / 1000.0f + 0.003f;   // Random Fade Value
	                    
	                    particle[i].x = getLocation().getX();        // Center On X Axis
	                    particle[i].y = getLocation().getY();        // Center On Y Axis
	                    particle[i].z = getLocation().getZ();        // Center On Z Axis
	                    
	                    particle[i].xi = xspeed + ((float)((Math.random() * MaxSpeedX)) - (MaxSpeedX/2)) * 10.0f;  // X Axis Speed And Direction
	                    particle[i].yi = yspeed + ((float)((Math.random() * MaxSpeedY)) - (MaxSpeedY/2)) * 10.0f;  // Y Axis Speed And Direction
	                    particle[i].zi = ((float)((Math.random() * MaxSpeedZ)) - (MaxSpeedZ/2)) * 10.0f; // Z Axis Speed And Direction
	                    
	                    particle[i].r = colors[col][0][0];            // Select Red From Color Table
	                    particle[i].g = colors[col][0][1];            // Select Green From Color Table
	                    particle[i].b = colors[col][0][2];            // Select Blue From Color Table
	                    particle[i].colorId = col;
	                    
	                    particle[i].xg = pullForceX;                                   // Set Horizontal Pull To Zero
	                    particle[i].yg = pullForceY;                                    // Set Vertical Pull Downward
	                    particle[i].zg = pullForceZ;                                     // Set Pull On Z Axis To Zero
	                    
	                    if(!regenerate){
	                    	particle[i].active = false;
	                    }
	                }
	            }
	        }

	        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
			// restore the model view matrix to prevent contamination
			GL11.glPopMatrix();
    	}
    }
    
    /**
     * Set if the particle engine is active
     * 
     * @param b The active status of the particle engine
     */
    public void setEnabled(boolean b){
    	enabled = b;
    }
    
    /**
     * Returns if this effect is enabled
     * @return If this effect is enabled
     */
    public boolean isEnabled(){
    	return enabled;
    }
    
    /**
     * Returns true if all the particles are disabled
     * @return True if all the particles are disabled
     */
    public boolean isDead(){
        for(int i=0;i<maxParticles;i++) {
            if(particle[i].active)
            	return false;
        }
        return true;
    }
    
    /**
     * Activates all the particles
     *
     */
    public void activateAllParticles(){
    	for(int i=0; i<particle.length ;i++){
    		particle[i].active = true;
    	}
    }
    
	/**
	 * Returns the bound of this class
	 */
	public Rectangle getBound() {
		return new Rectangle(
				(int)getLocation().getX(),
				(int)getLocation().getY(),
				(int)size,	(int)size);
	}

	@Override
	public Texture getTexture() {
		return texture;
	}
}

class Particle {                         // Particles Structure
    public boolean active;               // Active (Yes/No)
    public float life;                   // Particle Life
    public float fade;                   // Fade Speed
    public int colorId;
    
    public float r;                      // Red Value
    public float g;                      // Green Value
    public float b;                      // Blue Value
    
    public float x;                      // X Position
    public float y;                      // Y Position
    public float z;                      // Z Position
    
    public float xi;                     // X Direction
    public float yi;                     // Y Direction
    public float zi;                     // Z Direction
    
    public float xg;                     // X Gravity
    public float yg;                     // Y Gravity
    public float zg;                     // Z Gravity
}

