package flaxbeard.cyberware.common.handler;

import java.util.List;
import java.util.Stack;

import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.resources.I18n;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
import net.minecraftforge.fml.common.gameevent.TickEvent.RenderTickEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

import org.lwjgl.opengl.GL11;

import flaxbeard.cyberware.Cyberware;
import flaxbeard.cyberware.api.CyberwareAPI;
import flaxbeard.cyberware.api.ICyberwareUserData;
import flaxbeard.cyberware.api.hud.CyberwareHudDataEvent;
import flaxbeard.cyberware.api.hud.CyberwareHudEvent;
import flaxbeard.cyberware.api.hud.IHudElement;
import flaxbeard.cyberware.api.hud.IHudElement.EnumAnchorHorizontal;
import flaxbeard.cyberware.api.hud.IHudElement.EnumAnchorVertical;
import flaxbeard.cyberware.api.hud.NotificationInstance;
import flaxbeard.cyberware.api.item.IHudjack;
import flaxbeard.cyberware.client.KeyBinds;
import flaxbeard.cyberware.client.gui.GuiHudConfiguration;
import flaxbeard.cyberware.client.gui.hud.MissingPowerDisplay;
import flaxbeard.cyberware.client.gui.hud.NotificationDisplay;
import flaxbeard.cyberware.client.gui.hud.PowerDisplay;
import flaxbeard.cyberware.common.CyberwareConfig;
import flaxbeard.cyberware.common.CyberwareContent;

public class HudHandler
{
	public static final HudHandler INSTANCE = new HudHandler();
	
	
	// http://stackoverflow.com/a/16206356/1754640
	private static class NotificationStack<T> extends Stack<T>
	{
		private int maxSize;

		public NotificationStack(int size)
		{
			super();
			this.maxSize = size;
		}

		@Override
		public Object push(Object object)
		{
			while (this.size() >= maxSize)
			{
				this.remove(0);
			}
			return super.push((T) object);
		}
	}

	
	public static void addNotification(NotificationInstance not)
	{
		notifications.push(not);
	}
	
	public static final ResourceLocation HUD_TEXTURE = new ResourceLocation(Cyberware.MODID + ":textures/gui/hud.png");
	private static Iterable<ItemStack> inv;
	private static boolean lightArmor = false;
	public static Stack<NotificationInstance> notifications = new NotificationStack(5);
	private static int cachedCap = 0;
	private static int cachedTotal = 0;
	private static float cachedPercent = 0;
	private static int radioRange = -1;
	
	private static PowerDisplay pd = new PowerDisplay();
	private static MissingPowerDisplay mpd = new MissingPowerDisplay();
	private static NotificationDisplay nd = new NotificationDisplay();

	static
	{
		nd.setHorizontalAnchor(EnumAnchorHorizontal.LEFT);
		nd.setVerticalAnchor(EnumAnchorVertical.BOTTOM);
	}
	@SideOnly(Side.CLIENT)
	@SubscribeEvent
	public void addHudElements(CyberwareHudEvent event)
	{
		if (event.isHudjackAvailable())
		{
			event.addElement(pd);
			event.addElement(mpd);
			event.addElement(nd);

		}
	}
	
	@SideOnly(Side.CLIENT)
	@SubscribeEvent
	public void saveHudElements(CyberwareHudDataEvent event)
	{
		event.addElement(pd);
		event.addElement(mpd);
		event.addElement(nd);
	}
	
	private int lastTick = 0;
	private double lastVelX = 0;
	private double lastVelY = 0;
	private double lastVelZ = 0;
	private double lastLastVelX = 0;
	private double lastLastVelY = 0;
	private double lastLastVelZ = 0;

	
	@SideOnly(Side.CLIENT)
	@SubscribeEvent
	public void onDrawScreenPost(RenderTickEvent event)
	{
		if (event.phase == Phase.END)
		{
			GlStateManager.pushMatrix();
			Minecraft mc = Minecraft.getMinecraft();
			EntityPlayerSP p = mc.thePlayer;
			if (p != null)
			{
				float floatAmt = 0F;
				boolean active = false;
				if (CyberwareAPI.hasCapability(p))
				{
					List<ItemStack> hudjackItems = CyberwareAPI.getCapability(p).getHudjackItems();
					for (ItemStack stack : hudjackItems)
					{
						if (((IHudjack) CyberwareAPI.getCyberware(stack)).isActive(stack))
						{
							active = true;
							if (CyberwareConfig.ENABLE_FLOAT)
							{
								if (CyberwareAPI.getCyberware(stack) == CyberwareContent.eyeUpgrades)
								{
									floatAmt = CyberwareConfig.HUDLENS_FLOAT;
								}
								else
								{
									floatAmt = CyberwareConfig.HUDJACK_FLOAT;
								}
							}
							break;
						}
					}
				}
							
				double accelLastY = lastVelY - lastLastVelY;
				double accelY = p.motionY - lastVelY;
				ScaledResolution sr = new ScaledResolution(Minecraft.getMinecraft());
				double accelPitch = accelLastY + (accelY - accelLastY) * (event.renderTickTime + p.ticksExisted - lastTick) / 2F;
							
				double pitchCameraMove = floatAmt * ((p.prevRenderArmPitch + (p.renderArmPitch - p.prevRenderArmPitch) * event.renderTickTime) - p.rotationPitch);
				double yawCameraMove = floatAmt * ((p.prevRenderArmYaw + (p.renderArmYaw - p.prevRenderArmYaw) * event.renderTickTime) - p.rotationYaw);
		
				GlStateManager.translate(yawCameraMove, pitchCameraMove + accelPitch * 50F * floatAmt, 0);
				
				if (p.ticksExisted > lastTick + 1)
				{
					lastTick = p.ticksExisted;
					lastLastVelX = lastVelX;
					lastLastVelY = lastVelY;
					lastLastVelZ = lastVelZ;
					lastVelX = p.motionX;
					lastVelY = p.motionY;
					lastVelZ = p.motionZ;
				}
				
				CyberwareHudEvent hudEvent = new CyberwareHudEvent(sr, active);
				MinecraftForge.EVENT_BUS.post(hudEvent);
				List<IHudElement> elements = hudEvent.getElements();
				boolean active2 = hudEvent.isHudjackAvailable();
				
				for (IHudElement element : elements)
				{
					if (element.getHeight() + GuiHudConfiguration.getAbsoluteY(sr, element) <= 3)
					{
						GuiHudConfiguration.setYFromAbsolute(sr, element, 0 - element.getHeight() + 4);
					}
					
					if (GuiHudConfiguration.getAbsoluteY(sr, element) >= sr.getScaledHeight() - 3)
					{
						GuiHudConfiguration.setYFromAbsolute(sr, element, sr.getScaledHeight() - 4);
					}
					
					if (element.getWidth() + GuiHudConfiguration.getAbsoluteX(sr, element) <= 3)
					{
						GuiHudConfiguration.setXFromAbsolute(sr, element, 0 - element.getWidth() + 4);
					}
					
					if (GuiHudConfiguration.getAbsoluteX(sr, element) >= sr.getScaledWidth() - 3)
					{
						GuiHudConfiguration.setXFromAbsolute(sr, element, sr.getScaledWidth() - 4);
					}
					
					GL11.glPushMatrix();
					element.render(p, sr, active2, mc.currentScreen instanceof GuiHudConfiguration, event.renderTickTime);
					GL11.glPopMatrix();
				}
				
				// Display a prompt to the user to open the radial menu if they haven't yet
				if (CyberwareAPI.hasCapability(mc.thePlayer))
				{
					ICyberwareUserData data = CyberwareAPI.getCapability(mc.thePlayer);
					if (data.getActiveItems().size() > 0)
					{
						boolean done = CyberwareAPI.getCapability(mc.thePlayer).hasOpenedRadialMenu();
						if (!done)
						{
							String s = I18n.format("cyberware.gui.openMenu", KeyBinds.menu.getDisplayName());
							FontRenderer fr = mc.fontRendererObj;
							fr.drawStringWithShadow(s, sr.getScaledWidth() - fr.getStringWidth(s) - 5, 5, CyberwareAPI.getHUDColorHex());
						}
					}
				}
						
					/*if (active)
					{
						float currTime = p.ticksExisted + event.getPartialTicks();
						
						GL11.glPushMatrix();
						GlStateManager.enableBlend();
						ICyberwareUserData data = CyberwareAPI.getCapability(p);
						
						mc.getTextureManager().bindTexture(HUD_TEXTURE);
				
						ScaledResolution res = event.getResolution();
						int left = 5;
						int top = 5;
						
						Iterable<ItemStack> currInv = p.getArmorInventoryList();
						if (currInv != inv)
						{
							inv = currInv;
							boolean temp = lightArmor;
							lightArmor = updateLightArmor();
							if (lightArmor != temp)
							{
								addNotification(new NotificationInstance(currTime, new NotificationArmor(lightArmor)));
							}
						}
			
						FontRenderer fr = mc.fontRendererObj;
						
						if (p.ticksExisted % 20 == 0)
						{
							cachedPercent = data.getPercentFull();
							cachedCap = data.getCapacity();
							cachedTotal = data.getStoredPower();
						}
			
						int temp = radioRange;
						radioRange = TileEntityBeacon.isInRange(p.worldObj, p.posX, p.posY, p.posZ);
						if (radioRange != temp)
						{
							addNotification(new NotificationInstance(currTime, new NotificationRadio(radioRange)));
						}
						
						float[] color = CyberwareAPI.getHUDColor();
						int colorHex = CyberwareAPI.getHUDColorHex();
						
						if (cachedPercent != -1)
						{
							int amount = Math.round((21F * cachedPercent));
			
							boolean danger = (cachedPercent <= .2F);
							boolean superDanger = danger && (cachedPercent <= .05F);
							int xOffset = (danger ? 39 : 0);
							
							if (!superDanger || p.ticksExisted % 4 != 0)
							{
								GlStateManager.pushMatrix();
								if (!danger) GlStateManager.color(color[0], color[1], color[2]);
								ClientUtils.drawTexturedModalRect(left, top, xOffset, 0, 13, 2 + (21 - amount));
								ClientUtils.drawTexturedModalRect(left, top + 2 + (21 - amount), 13 + xOffset, 2 + (21 - amount), 13, amount + 2);
								
								ClientUtils.drawTexturedModalRect(left, top + 2 + (21 - amount), 26 + xOffset, 2 + (21 - amount), 13, amount + 2);
								GlStateManager.popMatrix();
			
								fr.drawStringWithShadow(cachedTotal + " / " + cachedCap, left + 15, top + 8, danger ? 0xFF0000 : colorHex);
							}
							top += 28;
						}
						
						List<NotificationInstance> nTR = new ArrayList<NotificationInstance>();
						for (int i = 0; i < notifications.size(); i++)
						{
							NotificationInstance ni = notifications.get(i);
							INotification notification = ni.getNotification();
							if (currTime - ni.getCreatedTime() < notification.getDuration() + 25)
							{
								double pct = Math.max(0F, ((currTime - ni.getCreatedTime() - notification.getDuration()) / 30F));
				
								float move = (float) ((20 * Math.sin(pct * (Math.PI / 2F))));
								
								GL11.glPushMatrix();
								GL11.glColor3f(1.0F, 1.0F, 1.0F);
								GL11.glTranslatef(0F, move, 0F);
								int index = (notifications.size() - 1) - i;
								notification.render(5 + index * 18, res.getScaledHeight() - 5 - 14);
								GL11.glPopMatrix();
							}
							else
							{
								nTR.add(ni);
							}
						}
						
						for (NotificationInstance ni : nTR)
						{
							notifications.remove(ni);
						}
						
						RenderItem ir = mc.getRenderItem();
						List<ItemStack> stacks = data.getPowerOutages();
						List<Integer> stackTimes = data.getPowerOutageTimes();
						List<Integer> toRemove = new ArrayList<Integer>();
						left -= 1;
						float zL = ir.zLevel;
						ir.zLevel = -300;
						for (int i = stacks.size() - 1; i >= 0; i--)
						{
							ItemStack stack = stacks.get(i);
							if (stack != null)
							{
								int time = stackTimes.get(i);
								boolean keep = p.ticksExisted - time < 50;
								double pct = Math.max(0F, ((currTime - time - 20) / 30F));
			
								float move = (float) ((20 * Math.sin(pct * (Math.PI / 2F))));
								if (keep)
								{
									GL11.glPushMatrix();
									GL11.glTranslatef(-move, 0F, 0F);
									
									fr.drawStringWithShadow("!", left + 14, top + 8, 0xFF0000);
									
									RenderHelper.enableStandardItemLighting();
									ir.renderItemAndEffectIntoGUI(stack, left, top);
									RenderHelper.disableStandardItemLighting();
			
									GL11.glPopMatrix();
									top += 18;
								}
								else
								{
									toRemove.add(i);
								}
							}
						}
						ir.zLevel = zL;
						
						for (int i : toRemove)
						{
							stacks.remove(i);
							stackTimes.remove(i);
						}
						
						mc.getTextureManager().bindTexture(Gui.ICONS);
			
						GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
						GL11.glPopMatrix();
					}*/
			}
			GlStateManager.popMatrix();	
		}
	}
}