package thebetweenlands.client.render.entity;

import org.lwjgl.opengl.GL11;

import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.shader.Framebuffer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import thebetweenlands.common.entity.EntityCCGroundSpawner;
import thebetweenlands.common.lib.ModInfo;
import thebetweenlands.util.Stencil;

@SideOnly(Side.CLIENT)
public class RenderCCGroundSpawner extends Render<EntityCCGroundSpawner> {
	private static final ResourceLocation TEXTURE = new ResourceLocation(ModInfo.ID, "textures/entity/cc_ground_spawner_shingles.png");
	private static final ResourceLocation HOLE_TEXTURE = new ResourceLocation(ModInfo.ID, "textures/entity/cc_ground_spawner_hole.png");
	private static final ResourceLocation GROUND_TEXTURE = new ResourceLocation(ModInfo.ID, "textures/entity/cc_ground_spawner_ground.png");
	
	public RenderCCGroundSpawner(RenderManager renderManagerIn) {
		super(renderManagerIn);
	}

	@Override
	public void doRender(EntityCCGroundSpawner entity, double x, double y, double z, float entityYaw, float partialTicks) {
		super.doRender(entity, x, y, z, entityYaw, partialTicks);

		this.bindEntityTexture(entity);

		GlStateManager.pushMatrix();
		GlStateManager.translate(x - 0.5D, y + 0.01D, z - 0.5D);

		Framebuffer fbo = Minecraft.getMinecraft().getFramebuffer();

		try(Stencil stencil = Stencil.reserve(fbo)) {
			if(stencil.valid()) {
				GL11.glEnable(GL11.GL_STENCIL_TEST);

				stencil.clear(false);

				stencil.func(GL11.GL_ALWAYS, true);
				stencil.op(GL11.GL_REPLACE, GL11.GL_KEEP, GL11.GL_REPLACE);

				GlStateManager.depthMask(false);
				GlStateManager.colorMask(false, false, false, false);
				GlStateManager.alphaFunc(GL11.GL_GREATER, 0.5F);

				GlStateManager.disableAlpha();
				GlStateManager.disableTexture2D();

				//Polygon offset required so that there's no z fighting with the window and background wall
				GlStateManager.enablePolygonOffset();
				GlStateManager.doPolygonOffset(-5.0F, -5.0F);

				//Render window through which the hole will be visible
				this.renderWindow();

				GlStateManager.disablePolygonOffset();

				GlStateManager.enableAlpha();
				GlStateManager.enableTexture2D();

				GlStateManager.depthMask(true);
				GlStateManager.colorMask(true, true, true, true);
				GlStateManager.alphaFunc(GL11.GL_GREATER, 0.0F);

				stencil.func(GL11.GL_EQUAL, true);
				stencil.op(GL11.GL_KEEP);
			}

			//Render to depth only with reversed depth test such that in the next pass it can be rendered normally
			GlStateManager.depthFunc(GL11.GL_GEQUAL);
			GlStateManager.colorMask(false, false, false, false);

			this.renderModel();

			GlStateManager.colorMask(true, true, true, true);
			GlStateManager.depthFunc(GL11.GL_LEQUAL);

			GL11.glDisable(GL11.GL_STENCIL_TEST);
		}

		//Render visible pass
		this.renderModel();

		GlStateManager.popMatrix();
	}

	private void renderWindow() {
		GlStateManager.disableCull();

		Tessellator tessellator = Tessellator.getInstance();

		BufferBuilder buffer = tessellator.getBuffer();

		buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION);
		buffer.pos(-1, 1, 2).endVertex();
		buffer.pos(-1, 1, -1).endVertex();
		buffer.pos(2, 1, -1).endVertex();
		buffer.pos(2, 1, 2).endVertex();
		tessellator.draw();

		GlStateManager.enableCull();
	}

	private void renderModel() {
		GlStateManager.disableCull();

		Tessellator tessellator = Tessellator.getInstance();

		BufferBuilder buffer = tessellator.getBuffer();

		this.bindTexture(TEXTURE);
		
		GlStateManager.doPolygonOffset(0.05F, 3.0F);
		GlStateManager.enablePolygonOffset();
		
		buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.OLDMODEL_POSITION_TEX_NORMAL);

		//north
		buffer.pos(-1, 1, -1).tex(0, 1).normal(0, 0, -1).endVertex();
		buffer.pos(-1, 0, -1).tex(0, 0).normal(0, 0, -1).endVertex();
		buffer.pos(2, 0, -1).tex(3, 0).normal(0, 0, -1).endVertex();
		buffer.pos(2, 1, -1).tex(3, 1).normal(0, 0, -1).endVertex();
		//slope
		buffer.pos(-1, 1, -1).tex(0, 0).normal(0, 0, -1).endVertex();
		buffer.pos(-1, 0, 0).tex(0, 1.0f).normal(0, 0, -1).endVertex();
		buffer.pos(2, 0, 0).tex(3, 1.0f).normal(0, 0.5f, -1).endVertex();
		buffer.pos(2, 1, -1).tex(3, 0).normal(0, 0.5f, -1).endVertex();

		//south
		buffer.pos(-1, 1, 2).tex(0, 1).normal(0, 0, 1).endVertex();
		buffer.pos(-1, 0, 2).tex(0, 0).normal(0, 0, 1).endVertex();
		buffer.pos(2, 0, 2).tex(3, 0).normal(0, 0, 1).endVertex();
		buffer.pos(2, 1, 2).tex(3, 1).normal(0, 0, 1).endVertex();
		//slope
		buffer.pos(-1, 1, 2).tex(0, 0).normal(0, 0, 1).endVertex();
		buffer.pos(-1, 0, 1).tex(0, 1.0f).normal(0, 0, 1).endVertex();
		buffer.pos(2, 0, 1).tex(3, 1.0f).normal(0, 0.5f, 1).endVertex();
		buffer.pos(2, 1, 2).tex(3, 0).normal(0, 0.5f, 1).endVertex();

		//west
		buffer.pos(-1, 1, -1).tex(0, 1).normal(-1, 0, 0).endVertex();
		buffer.pos(-1, 0, -1).tex(0, 0).normal(-1, 0, 0).endVertex();
		buffer.pos(-1, 0, 2).tex(3, 0).normal(-1, 0, 0).endVertex();
		buffer.pos(-1, 1, 2).tex(3, 1).normal(-1, 0, 0).endVertex();
		//slope
		buffer.pos(-1, 1, -1).tex(0, 0).normal(-1, 0, 0).endVertex();
		buffer.pos(0, 0, -1).tex(0, 1.0f).normal(-1, 0, 0).endVertex();
		buffer.pos(0, 0, 2).tex(3, 1.0f).normal(-1, 0.5f, 0).endVertex();
		buffer.pos(-1, 1, 2).tex(3, 0).normal(-1, 0.5f, 0).endVertex();

		//east
		buffer.pos(2, 1, -1).tex(0, 1).normal(1, 0, 0).endVertex();
		buffer.pos(2, 0, -1).tex(0, 0).normal(1, 0, 0).endVertex();
		buffer.pos(2, 0, 2).tex(3, 0).normal(1, 0, 0).endVertex();
		buffer.pos(2, 1, 2).tex(3, 1).normal(1, 0, 0).endVertex();
		//slope
		buffer.pos(2, 1, -1).tex(0, 0).normal(1, 0, 0).endVertex();
		buffer.pos(1, 0, -1).tex(0, 1.0f).normal(1, 0, 0).endVertex();
		buffer.pos(1, 0, 2).tex(3, 1.0f).normal(1, 0.5f, 0).endVertex();
		buffer.pos(2, 1, 2).tex(3, 0).normal(1, 0.5f, 0).endVertex();

		tessellator.draw();

		this.bindTexture(HOLE_TEXTURE);
		
		GlStateManager.doPolygonOffset(0, 0);
		GlStateManager.disablePolygonOffset();
		
		GlStateManager.enableCull();
		
		buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.OLDMODEL_POSITION_TEX_NORMAL);
		
		//ground
		buffer.pos(-1, 0, -1).tex(0, 0).normal(0, 1, 0).endVertex();
		buffer.pos(-1, 0, 2).tex(0, 1).normal(0, 1, 0).endVertex();
		buffer.pos(2, 0, 2).tex(1, 1).normal(0, 1, 0).endVertex();
		buffer.pos(2, 0, -1).tex(1, 0).normal(0, 1, 0).endVertex();

		tessellator.draw();
		
		this.bindTexture(GROUND_TEXTURE);
		
		buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.OLDMODEL_POSITION_TEX_NORMAL);
		
		//ground
		buffer.pos(-1, 0, 2).tex(0, 1).normal(0, 1, 0).endVertex();
		buffer.pos(-1, 0, -1).tex(0, 0).normal(0, 1, 0).endVertex();
		buffer.pos(2, 0, -1).tex(1, 0).normal(0, 1, 0).endVertex();
		buffer.pos(2, 0, 2).tex(1, 1).normal(0, 1, 0).endVertex();

		tessellator.draw();
	}

	@Override
	protected ResourceLocation getEntityTexture(EntityCCGroundSpawner entity) {
		return TEXTURE;
	}
}
