package net.optifine;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.Entity;
import net.minecraft.util.BlockPos;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.biome.BiomeGenBase;
import net.optifine.config.ConnectedParser;
import net.optifine.config.Matches;
import net.optifine.config.RangeListInt;
import net.optifine.render.Blender;
import net.optifine.util.NumUtils;
import net.optifine.util.SmoothFloat;
import net.optifine.util.TextureUtils;

public class CustomSkyLayer {
	public static final float[] DEFAULT_AXIS = new float[] { 1.0F, 0.0F, 0.0F };
	private static final String WEATHER_CLEAR = "clear";
	private static final String WEATHER_RAIN = "rain";
	private static final String WEATHER_THUNDER = "thunder";
	public String source = null;
	private int startFadeIn = -1;
	private int endFadeIn = -1;
	private int startFadeOut = -1;
	private int endFadeOut = -1;
	private int blend = 1;
	private boolean rotate = false;
	private float speed = 1.0F;
	private float[] axis;
	private RangeListInt days;
	private int daysLoop;
	private boolean weatherClear;
	private boolean weatherRain;
	private boolean weatherThunder;
	public BiomeGenBase[] biomes;
	public RangeListInt heights;
	private float transition;
	private SmoothFloat smoothPositionBrightness;
	public int textureId;
	private World lastWorld;

	public CustomSkyLayer(Properties props, String defSource) {
		this.axis = DEFAULT_AXIS;
		this.days = null;
		this.daysLoop = 8;
		this.weatherClear = true;
		this.weatherRain = false;
		this.weatherThunder = false;
		this.biomes = null;
		this.heights = null;
		this.transition = 1.0F;
		this.smoothPositionBrightness = null;
		this.textureId = -1;
		this.lastWorld = null;
		ConnectedParser connectedparser = new ConnectedParser("CustomSky");
		this.source = props.getProperty("source", defSource);
		this.startFadeIn = this.parseTime(props.getProperty("startFadeIn"));
		this.endFadeIn = this.parseTime(props.getProperty("endFadeIn"));
		this.startFadeOut = this.parseTime(props.getProperty("startFadeOut"));
		this.endFadeOut = this.parseTime(props.getProperty("endFadeOut"));
		this.blend = Blender.parseBlend(props.getProperty("blend"));
		this.rotate = this.parseBoolean(props.getProperty("rotate"), true);
		this.speed = this.parseFloat(props.getProperty("speed"), 1.0F);
		this.axis = this.parseAxis(props.getProperty("axis"), DEFAULT_AXIS);
		this.days = connectedparser.parseRangeListInt(props.getProperty("days"));
		this.daysLoop = connectedparser.parseInt(props.getProperty("daysLoop"), 8);
		List<String> list = this.parseWeatherList(props.getProperty("weather", "clear"));
		this.weatherClear = list.contains("clear");
		this.weatherRain = list.contains("rain");
		this.weatherThunder = list.contains("thunder");
		this.biomes = connectedparser.parseBiomes(props.getProperty("biomes"));
		this.heights = connectedparser.parseRangeListInt(props.getProperty("heights"));
		this.transition = this.parseFloat(props.getProperty("transition"), 1.0F);
	}

	private float getFadeBrightness(int timeOfDay) {
		if (this.timeBetween(timeOfDay, this.startFadeIn, this.endFadeIn)) {
			int k = this.normalizeTime(this.endFadeIn - this.startFadeIn);
			int l = this.normalizeTime(timeOfDay - this.startFadeIn);
			return (float) l / (float) k;
		} else if (this.timeBetween(timeOfDay, this.endFadeIn, this.startFadeOut)) {
			return 1.0F;
		} else if (this.timeBetween(timeOfDay, this.startFadeOut, this.endFadeOut)) {
			int i = this.normalizeTime(this.endFadeOut - this.startFadeOut);
			int j = this.normalizeTime(timeOfDay - this.startFadeOut);
			return 1.0F - (float) j / (float) i;
		} else {
			return 0.0F;
		}
	}

	private float getPositionBrightness(World world) {
		if (this.biomes == null && this.heights == null) {
			return 1.0F;
		} else {
			float f = this.getPositionBrightnessRaw(world);

			if (this.smoothPositionBrightness == null) {
				this.smoothPositionBrightness = new SmoothFloat(f, this.transition);
			}

			f = this.smoothPositionBrightness.getSmoothValue(f);
			return f;
		}
	}

	private float getPositionBrightnessRaw(World world) {
		Entity entity = Minecraft.getMinecraft().getRenderViewEntity();

		if (entity == null) {
			return 0.0F;
		} else {
			BlockPos blockpos = entity.getPosition();

			if (this.biomes != null) {
				BiomeGenBase biomegenbase = world.getBiomeGenForCoords(blockpos);

				if (biomegenbase == null) {
					return 0.0F;
				}

				if (!Matches.biome(biomegenbase, this.biomes)) {
					return 0.0F;
				}
			}

			return this.heights != null && !this.heights.isInRange(blockpos.getY()) ? 0.0F : 1.0F;
		}
	}

	private float getWeatherBrightness(float rainStrength, float thunderStrength) {
		float f = 1.0F - rainStrength;
		float f1 = rainStrength - thunderStrength;
		float f2 = 0.0F;

		if (this.weatherClear) {
			f2 += f;
		}

		if (this.weatherRain) {
			f2 += f1;
		}

		if (this.weatherThunder) {
			f2 += thunderStrength;
		}

		f2 = NumUtils.limit(f2, 0.0F, 1.0F);
		return f2;
	}

	public boolean isActive(World world, int timeOfDay) {
		if (world != this.lastWorld) {
			this.lastWorld = world;
			this.smoothPositionBrightness = null;
		}

		if (this.timeBetween(timeOfDay, this.endFadeOut, this.startFadeIn)) {
			return false;
		} else {
			if (this.days != null) {
				long i = world.getWorldTime();
				long j;

				for (j = i - (long) this.startFadeIn; j < 0L; j += (long) (24000 * this.daysLoop)) {
					;
				}

				int k = (int) (j / 24000L);
				int l = k % this.daysLoop;

				if (!this.days.isInRange(l)) {
					return false;
				}
			}

			return true;
		}
	}

	public boolean isValid(String path) {
		if (this.source == null) {
			Config.warn("No source texture: " + path);
			return false;
		} else {
			this.source = TextureUtils.fixResourcePath(this.source, TextureUtils.getBasePath(path));

			if (this.startFadeIn >= 0 && this.endFadeIn >= 0 && this.endFadeOut >= 0) {
				int i = this.normalizeTime(this.endFadeIn - this.startFadeIn);

				if (this.startFadeOut < 0) {
					this.startFadeOut = this.normalizeTime(this.endFadeOut - i);

					if (this.timeBetween(this.startFadeOut, this.startFadeIn, this.endFadeIn)) {
						this.startFadeOut = this.endFadeIn;
					}
				}

				int j = this.normalizeTime(this.startFadeOut - this.endFadeIn);
				int k = this.normalizeTime(this.endFadeOut - this.startFadeOut);
				int l = this.normalizeTime(this.startFadeIn - this.endFadeOut);
				int i1 = i + j + k + l;

				if (i1 != 24000) {
					Config.warn("Invalid fadeIn/fadeOut times, sum is not 24h: " + i1);
					return false;
				} else if (this.speed < 0.0F) {
					Config.warn("Invalid speed: " + this.speed);
					return false;
				} else if (this.daysLoop <= 0) {
					Config.warn("Invalid daysLoop: " + this.daysLoop);
					return false;
				} else {
					return true;
				}
			} else {
				Config.warn("Invalid times, required are: startFadeIn, endFadeIn and endFadeOut.");
				return false;
			}
		}
	}

	private int normalizeTime(int timeMc) {
		while (timeMc >= 24000) {
			timeMc -= 24000;
		}

		while (timeMc < 0) {
			timeMc += 24000;
		}

		return timeMc;
	}

	private float[] parseAxis(String str, float[] defVal) {
		if (str == null) {
			return defVal;
		} else {
			String[] astring = Config.tokenize(str, " ");

			if (astring.length != 3) {
				Config.warn("Invalid axis: " + str);
				return defVal;
			} else {
				float[] afloat = new float[3];

				for (int i = 0; i < astring.length; ++i) {
					afloat[i] = Config.parseFloat(astring[i], Float.MIN_VALUE);

					if (afloat[i] == Float.MIN_VALUE) {
						Config.warn("Invalid axis: " + str);
						return defVal;
					}

					if (afloat[i] < -1.0F || afloat[i] > 1.0F) {
						Config.warn("Invalid axis values: " + str);
						return defVal;
					}
				}

				float f2 = afloat[0];
				float f = afloat[1];
				float f1 = afloat[2];

				float l = f2 * f2 + f * f + f1 * f1;
				if (l < 1.0E-5F) {
					Config.warn("Invalid axis values: " + str);
					return defVal;
				} else {
					l = MathHelper.sqrt_float(l);
					return new float[] { f1 / l, f / l, -f2 / l };
				}
			}
		}
	}

	private boolean parseBoolean(String str, boolean defVal) {
		if (str == null) {
			return defVal;
		} else if (str.toLowerCase().equals("true")) {
			return true;
		} else if (str.toLowerCase().equals("false")) {
			return false;
		} else {
			Config.warn("Unknown boolean: " + str);
			return defVal;
		}
	}

	private float parseFloat(String str, float defVal) {
		if (str == null) {
			return defVal;
		} else {
			float f = Config.parseFloat(str, Float.MIN_VALUE);

			if (f == Float.MIN_VALUE) {
				Config.warn("Invalid value: " + str);
				return defVal;
			} else {
				return f;
			}
		}
	}

	private int parseTime(String str) {
		if (str == null) {
			return -1;
		} else {
			String[] astring = Config.tokenize(str, ":");

			if (astring.length != 2) {
				Config.warn("Invalid time: " + str);
				return -1;
			} else {
				String s = astring[0];
				String s1 = astring[1];
				int i = Config.parseInt(s, -1);
				int j = Config.parseInt(s1, -1);

				if (i >= 0 && i <= 23 && j >= 0 && j <= 59) {
					i = i - 6;

					if (i < 0) {
						i += 24;
					}

					int k = i * 1000 + (int) ((double) j / 60.0D * 1000.0D);
					return k;
				} else {
					Config.warn("Invalid time: " + str);
					return -1;
				}
			}
		}
	}

	private List<String> parseWeatherList(String str) {
		List<String> list = Arrays.<String>asList(new String[] { "clear", "rain", "thunder" });
		List<String> list1 = new ArrayList();
		String[] astring = Config.tokenize(str, " ");

		for (int i = 0; i < astring.length; ++i) {
			String s = astring[i];

			if (!list.contains(s)) {
				Config.warn("Unknown weather: " + s);
			} else {
				list1.add(s);
			}
		}

		return list1;
	}

	public void render(World world, int timeOfDay, float celestialAngle, float rainStrength, float thunderStrength) {
		float f = this.getPositionBrightness(world);
		float f1 = this.getWeatherBrightness(rainStrength, thunderStrength);
		float f2 = this.getFadeBrightness(timeOfDay);
		float f3 = f * f1 * f2;
		f3 = Config.limit(f3, 0.0F, 1.0F);

		if (f3 >= 1.0E-4F) {
			GlStateManager.bindTexture(this.textureId);
			Blender.setupBlend(this.blend, f3);
			GlStateManager.pushMatrix();

			if (this.rotate) {
				float f4 = 0.0F;

				if (this.speed != (float) Math.round(this.speed)) {
					long i = (world.getWorldTime() + 18000L) / 24000L;
					double d0 = (double) (this.speed % 1.0F);
					double d1 = (double) i * d0;
					f4 = (float) (d1 % 1.0D);
				}

				GlStateManager.rotate(360.0F * (f4 + celestialAngle * this.speed), this.axis[0], this.axis[1],
						this.axis[2]);
			}

			Tessellator tessellator = Tessellator.getInstance();
			GlStateManager.rotate(90.0F, 1.0F, 0.0F, 0.0F);
			GlStateManager.rotate(-90.0F, 0.0F, 0.0F, 1.0F);
			this.renderSide(tessellator, 4);
			GlStateManager.pushMatrix();
			GlStateManager.rotate(90.0F, 1.0F, 0.0F, 0.0F);
			this.renderSide(tessellator, 1);
			GlStateManager.popMatrix();
			GlStateManager.pushMatrix();
			GlStateManager.rotate(-90.0F, 1.0F, 0.0F, 0.0F);
			this.renderSide(tessellator, 0);
			GlStateManager.popMatrix();
			GlStateManager.rotate(90.0F, 0.0F, 0.0F, 1.0F);
			this.renderSide(tessellator, 5);
			GlStateManager.rotate(90.0F, 0.0F, 0.0F, 1.0F);
			this.renderSide(tessellator, 2);
			GlStateManager.rotate(90.0F, 0.0F, 0.0F, 1.0F);
			this.renderSide(tessellator, 3);
			GlStateManager.popMatrix();
		}
	}

	private void renderSide(Tessellator tess, int side) {
		WorldRenderer worldrenderer = tess.getWorldRenderer();
		double d0 = (double) (side % 3) / 3.0D;
		double d1 = (double) (side / 3) / 2.0D;
		worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX);
		worldrenderer.pos(-100.0D, -100.0D, -100.0D).tex(d0, d1).endVertex();
		worldrenderer.pos(-100.0D, -100.0D, 100.0D).tex(d0, d1 + 0.5D).endVertex();
		worldrenderer.pos(100.0D, -100.0D, 100.0D).tex(d0 + 0.3333333333333333D, d1 + 0.5D).endVertex();
		worldrenderer.pos(100.0D, -100.0D, -100.0D).tex(d0 + 0.3333333333333333D, d1).endVertex();
		tess.draw();
	}

	private boolean timeBetween(int timeOfDay, int timeStart, int timeEnd) {
		return timeStart <= timeEnd ? timeOfDay >= timeStart && timeOfDay <= timeEnd
				: timeOfDay >= timeStart || timeOfDay <= timeEnd;
	}

	@Override
	public String toString() {
		return "" + this.source + ", " + this.startFadeIn + "-" + this.endFadeIn + " " + this.startFadeOut + "-"
				+ this.endFadeOut;
	}
}
