package com.oddlabs.tt.particle;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;

import com.oddlabs.tt.animation.Animated;
import com.oddlabs.tt.animation.AnimationManager;
import com.oddlabs.tt.global.Globals;
import com.oddlabs.tt.camera.CameraState;
import com.oddlabs.tt.landscape.World;
import com.oddlabs.tt.model.Element;
import com.oddlabs.tt.model.ElementVisitor;
import com.oddlabs.tt.model.AbstractElementNode;
import com.oddlabs.tt.render.TextureKey;
import com.oddlabs.tt.util.StateChecksum;
import com.oddlabs.tt.util.StrictMatrix4f;

public final strictfp class Lightning extends Element implements Animated {
	private final static float SQRT_2 = (float)StrictMath.sqrt(2f);

	private final AnimationManager manager;
	private final List particles = new ArrayList();
	private final Vector3f src;
	private final Vector3f dst;
	private final float width;
	private final int num_particles;
	private final Vector4f color;
	private final Vector4f delta_color;
	private final TextureKey texture;
	private final World world;

	private float energy;

	public Lightning(World world, Vector3f src, Vector3f dst, float width,
			int num_particles, Vector4f color, Vector4f delta_color,
			TextureKey texture, float energy,
			AnimationManager manager) {
		super(world.getElementRoot());
		this.world = world;
		this.src = src;
		this.dst = dst;
		this.width = width;
		this.num_particles = num_particles;
		this.color = color;
		this.delta_color = delta_color;
		this.texture = texture;
		this.energy = energy;
		this.manager = manager;
		initParticles();
		register();
	}

	public final List getParticles() {
		return particles;
	}

	public final TextureKey getTexture() {
		return texture;
	}

	private final void initParticles() {
		Random random = world.getRandom();
		random.nextFloat();
		float x = src.getX();
		float y = src.getY();
		float z = src.getZ();
		float height = dst.getZ() - src.getZ();
		float random_limit = height/6f;
		float dz = (height)/num_particles;

		for (int i = 0; i < num_particles; i++) {
			float base_dx = (dst.getX() - x)/(num_particles - i);
			float base_dy = (dst.getY() - y)/(num_particles - i);
			float dx = base_dx + (random.nextFloat() - .5f)*random_limit;
			float dy = base_dy + (random.nextFloat() - .5f)*random_limit;
			StretchParticle particle = new StretchParticle();
			particle.setSrc(x, y, z);

			if (i == num_particles - 1) {
				x = dst.getX();
				y = dst.getY();
				z = dst.getZ();
				particle.setDstWidth(width/2);
			} else {
				x += dx;
				y += dy;
				z += dz;
				particle.setDstWidth(width);
			}
			particle.setDst(x, y, z);
			initParticle(particle);
			particles.add(particle);
		}
	}

	private final void initParticle(StretchParticle particle) {
		particle.setSrcWidth(width);
		particle.setColor(color.getX(), color.getY(), color.getZ(), color.getW());
		particle.setDeltaColor(delta_color.getX(), delta_color.getY(), delta_color.getZ(), delta_color.getW());
		particle.setRadius(0f, 0f, 0f);
		particle.setGrowthRate(0f, 0f, 0f);
		particle.setEnergy(energy);
	}

	public final void animate(float t) {
		float x_min = Float.POSITIVE_INFINITY;
		float x_max = Float.NEGATIVE_INFINITY;
		float y_min = Float.POSITIVE_INFINITY;
		float y_max = Float.NEGATIVE_INFINITY;
		float z_min = Float.POSITIVE_INFINITY;
		float z_max = Float.NEGATIVE_INFINITY;

		for (int i = 0; i < particles.size(); i++) {
			StretchParticle particle = (StretchParticle)particles.get(i);
			if (particle.getEnergy() > 0f) {
				particle.update(t);
				float x = particle.getSrcX();
				float y = particle.getSrcY();
				float z = particle.getSrcZ();
				float radius_x = particle.getRadiusX()*SQRT_2;
				float radius_y = particle.getRadiusY()*SQRT_2;
				float radius_z = particle.getRadiusZ()*SQRT_2;
				x_min = StrictMath.min(x_min, x - radius_x);
				x_max = StrictMath.max(x_max, x + radius_x);
				y_min = StrictMath.min(y_min, y - radius_y);
				y_max = StrictMath.max(y_max, y + radius_y);
				z_min = StrictMath.min(z_min, z - radius_z);
				z_max = StrictMath.max(z_max, z + radius_z);
			} else {
				particles.remove(i);
			}
		}
		setBounds(x_min, x_max, y_min, y_max, z_min, z_max);
		reregister();
		if (particles.size() == 0) {
			remove();
		}
	}

	protected void register() {
		super.register();
		manager.registerAnimation(this);
	}

	public final void visit(ElementVisitor visitor) {
		visitor.visitLightning(this);
	}

	protected final void remove() {
		super.remove();
		manager.removeAnimation(this);
	}

	public final void updateChecksum(StateChecksum checksum) {
	}
}