package lumien.custommainmenu.lib.textures;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.lwjgl.opengl.GL11;

import net.ellerton.japng.Png;
import net.ellerton.japng.argb8888.Argb8888Bitmap;
import net.ellerton.japng.argb8888.Argb8888BitmapSequence;
import net.ellerton.japng.argb8888.Argb8888BitmapSequence.Frame;
import net.ellerton.japng.chunks.PngAnimationControl;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.texture.TextureUtil;
import net.minecraft.util.ResourceLocation;

public class TextureApng implements ITexture
{
	ResourceLocation rl;

	HashMap<Frame, Integer> frameTextureID;
	List<Frame> frames;

	PngAnimationControl animationControl;
	int currentFrame;

	boolean loaded;
	boolean errored;

	long lastTimeStamp;
	int currentFrameDelay;

	public TextureApng(String textureString)
	{
		rl = new ResourceLocation(textureString);
		errored = false;
	}

	@Override
	public void bind()
	{
		if (!loaded)
		{
			load();
			loaded = true;
		}

		if (errored)
		{
			GlStateManager.bindTexture(TextureUtil.MISSING_TEXTURE.getGlTextureId());
			return;
		}
		
		while (System.currentTimeMillis() - lastTimeStamp >= currentFrameDelay)
		{
			currentFrame++;
			if (currentFrame > animationControl.numFrames - 1)
			{
				currentFrame = 0;
			}

			Frame f = frames.get(currentFrame);

			float numerator = f.control.delayNumerator;
			float denominator = f.control.delayDenominator > 0 ? f.control.delayDenominator : 100;

			this.lastTimeStamp += currentFrameDelay;
			this.currentFrameDelay = (int) ((numerator / denominator) * 1000);
		}

		GlStateManager.bindTexture(frameTextureID.get(frames.get(currentFrame)));
	}

	private void load()
	{
		frameTextureID = new HashMap<Frame, Integer>();
		try
		{
			InputStream inputStream = Minecraft.getMinecraft().getResourceManager().getResource(rl).getInputStream();

			Argb8888BitmapSequence pngContainer = Png.readArgb8888BitmapSequence(inputStream);
			animationControl = pngContainer.getAnimationControl();

			frames = Collections.synchronizedList(pngContainer.getAnimationFrames());

			BufferedImage canvas = new BufferedImage(pngContainer.defaultImage.width, pngContainer.defaultImage.height, BufferedImage.TYPE_INT_ARGB);
			Graphics2D graphics = canvas.createGraphics();
			graphics.setBackground(new Color(0, 0, 0, 0));

			BufferedImage frameBackup = new BufferedImage(pngContainer.defaultImage.width, pngContainer.defaultImage.height, BufferedImage.TYPE_INT_ARGB);
			Graphics2D graphicsBackup = frameBackup.createGraphics();
			graphicsBackup.setBackground(new Color(0, 0, 0, 0));

			for (int i = 0; i < frames.size(); i++)
			{
				Frame f = frames.get(i);

				switch (f.control.blendOp)
				{
					case 0:
						graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
						break;
					case 1:
						graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
						break;
				}

				Argb8888Bitmap bitmap = f.bitmap;

				BufferedImage buffered = new BufferedImage(bitmap.width, bitmap.height, BufferedImage.TYPE_INT_ARGB);
				buffered.setRGB(0, 0, bitmap.width, bitmap.height, bitmap.getPixelArray(), 0, bitmap.width);

				graphicsBackup.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
				graphicsBackup.drawImage(canvas, 0, 0, canvas.getWidth(), canvas.getHeight(), null);

				graphics.drawImage(buffered, f.control.xOffset, f.control.yOffset, f.control.xOffset + f.control.width, f.control.yOffset + f.control.height, 0, 0, buffered.getWidth(), buffered.getHeight(), null);

				frameTextureID.put(f, TextureUtil.uploadTextureImageAllocate(GL11.glGenTextures(), canvas, false, false));

				switch (f.control.disposeOp)
				{
					case 0:
						break;
					case 1:
						graphics.clearRect(f.control.xOffset, f.control.yOffset, f.control.width, f.control.height);
						break;
					case 2:
						graphics.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
						graphics.drawImage(frameBackup, 0, 0, canvas.getWidth(), canvas.getHeight(), null);
						break;
				}
			}
			
			graphics.dispose();
			graphicsBackup.dispose();
		}
		catch (Exception e)
		{
			e.printStackTrace();
			errored = true;
			return;
		}
		
		Frame frame = frames.get(0);

		float numerator = frame.control.delayNumerator;
		float denominator = frame.control.delayDenominator > 0 ? frame.control.delayDenominator : 100;

		this.currentFrameDelay = (int) (numerator / denominator * 1000);
		this.lastTimeStamp = System.currentTimeMillis();
	}
}