package client.renderer;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.opengl.GL11;

import common.block.Block;
import common.block.liquid.BlockLiquid;
import common.entity.Entity;
import common.entity.npc.EntityNPC;
import common.util.BlockPos;
import common.util.ExtMath;
import common.util.Vec3;
import common.world.State;
import common.world.World;

public class MatrixState {
	private static final IntBuffer VIEWPORT = ByteBuffer.allocateDirect(16 << 2).order(ByteOrder.nativeOrder()).asIntBuffer();
	private static final FloatBuffer MODELVIEW = ByteBuffer.allocateDirect(16 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
	private static final FloatBuffer PROJECTION = ByteBuffer.allocateDirect(16 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
	private static final FloatBuffer OBJECTCOORDS = ByteBuffer.allocateDirect(3 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
	private static final FloatBuffer SCREENCOORDS = ByteBuffer.allocateDirect(3 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();

	private static Vec3 position = new Vec3(0.0D, 0.0D, 0.0D);
	private static float rotationX;
	private static float rotationXZ;
	private static float rotationZ;
	private static float rotationYZ;
	private static float rotationXY;

	public static void update(EntityNPC player, boolean flip) {
		GL11.glGetFloatv(GL11.GL_MODELVIEW_MATRIX, MODELVIEW);
		GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, PROJECTION);
		GL11.glGetIntegerv(GL11.GL_VIEWPORT, VIEWPORT);
		float x = (float)((VIEWPORT.get(0) + VIEWPORT.get(2)) / 2);
		float y = (float)((VIEWPORT.get(1) + VIEWPORT.get(3)) / 2);
		Project.gluUnProject(x, y, 0.0F, MODELVIEW, PROJECTION, VIEWPORT, OBJECTCOORDS);
		position = new Vec3((double)OBJECTCOORDS.get(0), (double)OBJECTCOORDS.get(1), (double)OBJECTCOORDS.get(2));
		int rot = flip ? 1 : 0;
		float pitch = player.rotPitch;
		float yaw = player.rotYaw;
		rotationX = ExtMath.cos(yaw * (float)Math.PI / 180.0F) * (float)(1 - rot * 2);
		rotationZ = ExtMath.sin(yaw * (float)Math.PI / 180.0F) * (float)(1 - rot * 2);
		rotationYZ = -rotationZ * ExtMath.sin(pitch * (float)Math.PI / 180.0F) * (float)(1 - rot * 2);
		rotationXY = rotationX * ExtMath.sin(pitch * (float)Math.PI / 180.0F) * (float)(1 - rot * 2);
		rotationXZ = ExtMath.cos(pitch * (float)Math.PI / 180.0F);
	}

	public static Vec3 getDisplayCoords(float x, float y, float z) {
		Project.gluProject(x, y, z, MODELVIEW, PROJECTION, VIEWPORT, SCREENCOORDS);
		return new Vec3((double)SCREENCOORDS.get(0), (double)SCREENCOORDS.get(1), (double)SCREENCOORDS.get(2));
	}

	private static Vec3 project(Entity entity, double partial) {
		double x = entity.prevX + (entity.posX - entity.prevX) * partial;
		double y = entity.prevY + (entity.posY - entity.prevY) * partial;
		double z = entity.prevZ + (entity.posZ - entity.prevZ) * partial;
		return new Vec3(x + position.xCoord, y + position.yCoord, z + position.zCoord);
	}

	public static Block getLookedBlock(World world, Entity entity, float partial) {
		Vec3 view = project(entity, (double)partial);
		BlockPos pos = new BlockPos(view);
		State state = world.getState(pos);
		Block block = state.getBlock();
		if(block.getMaterial().isLiquid()) {
			float height = 0.0F;
			if(state.getBlock() instanceof BlockLiquid)
				height = BlockLiquid.getLiquidHeightPercent(((Integer)state.getValue(BlockLiquid.LEVEL)).intValue()) - 0.11111111F;
			float y = (float)(pos.getY() + 1) - height;
			if(view.yCoord >= (double)y)
				block = world.getState(pos.up()).getBlock();
		}
		return block;
	}

	public static Vec3 getPosition() {
		return position;
	}

	public static float getRotationX() {
		return rotationX;
	}

	public static float getRotationXZ() {
		return rotationXZ;
	}

	public static float getRotationZ() {
		return rotationZ;
	}

	public static float getRotationYZ() {
		return rotationYZ;
	}

	public static float getRotationXY() {
		return rotationXY;
	}
}
