package common.init;

import static java.lang.annotation.ElementType.FIELD;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.TreeMap;

import common.util.ExtMath;

public abstract class Config {
	public static enum ValueType {
		STRING, BOOLEAN, INTEGER, FLOAT;
	}
	
	@Target(FIELD)
	@Retention(value = RetentionPolicy.RUNTIME)
	private static @interface Var {
		String name();
		float min() default (float)Integer.MIN_VALUE;
		float max() default (float)Integer.MAX_VALUE;
	}
	
	public static class Value {
		public final ValueType type;
		public final String def;
		private final Field field;
		private final float min;
		private final float max;
		
		private Runnable callback;

		private Value(Field field, Var value) {
			this.type = field.getType() == int.class ? ValueType.INTEGER : (field.getType() == boolean.class ? ValueType.BOOLEAN : 
				(field.getType() == String.class ? ValueType.STRING : (field.getType() == float.class ? ValueType.FLOAT : null)));
			if(this.type == null)
				throw new IllegalArgumentException(value.name() + ": Unbekannter Variablen-Typ - " + field.getType());
			this.field = field;
			this.def = this.getValue();
//			Clamped clamp = this.field.getAnnotation(Clamped.class);
			this.min = (this.type == ValueType.INTEGER || this.type == ValueType.FLOAT) 
					? value.min() : 0;
			this.max = (this.type == ValueType.INTEGER || this.type == ValueType.FLOAT) 
					? value.max() : (this.type == ValueType.BOOLEAN ? 1 : 0);
//			Update update = this.field.getAnnotation(Update.class);
//			if(value.callback() == Callback.class) {
//				this.callback = null;
//			}
//			else {
//				try {
//					this.callback = value.callback().getConstructor().newInstance();
//				}
//				catch(InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException 
//						| NoSuchMethodException e) {
//					throw new RuntimeException(e);
//				}
//			}
			this.setValue(this.def);
		}
		
		public String getValue() {
			try {
				return "" + this.field.get(null);
//				switch(this.type) {
//					case STRING:
//					default:
//						return (String)this.field.get(null);
//					case BOOLEAN:
//						return "" + this.field.getBoolean(null);
//					case INTEGER:
//						return "" + this.field.getInt(null);
//					case FLOAT:
//						return "" + this.field.getFloat(null);
//				}
			}
			catch(IllegalArgumentException | IllegalAccessException e) {
				throw new RuntimeException(e);
			}
		}

		private void setValue(String value) {
			try {
				switch(this.type) {
					case STRING:
						this.field.set(null, value);
						break;
					case BOOLEAN:
						this.field.setBoolean(null, Boolean.parseBoolean(value));
						break;
					case INTEGER:
						int inum = 0;
						try {
							inum = Integer.parseInt(value);
						}
						catch(NumberFormatException e) {
						}
						this.field.setInt(null, ExtMath.clampi(inum, (int)this.min, (int)this.max));
						break;
					case FLOAT:
						float fnum = 0.0f;
						try {
							fnum = Float.parseFloat(value);
						}
						catch(NumberFormatException e) {
						}
						fnum = ExtMath.clampf(fnum, this.min, this.max);
						int round = (int)(fnum * 1000.0f);
						this.field.setFloat(null, (float)round / 1000.0f);
						break;
				}
			}
			catch(IllegalArgumentException | IllegalAccessException e) {
				throw new RuntimeException(e);
			}
		}
	}

	public static final int PROTOCOL = 666;
	public static final int PORT = 26666;
	public static final String NAME = "TCR";
	public static final String VERSION = "v2.2.1-alpha";
	public static final String CLIENT_VERSION = NAME + " Client " + VERSION;
	
	public static final Map<String, Config.Value> VARS = new TreeMap();

	@Var(name = "fireTick")
	public static boolean fire = true;
	@Var(name = "mobGriefing")
	public static boolean mobGrief = true;
	@Var(name = "mobSpawning")
	public static boolean mobs = true;
	@Var(name = "tickSpawning")
	public static boolean tickSpawn = true;
	@Var(name = "genSpawning")
	public static boolean genSpawn = true;
	@Var(name = "spawners")
	public static boolean spawners = true;
	@Var(name = "spawnVillagers")
	public static boolean spawnVillager = true;
	@Var(name = "spawnCagedVillagers")
	public static boolean spawnCagedVillager = true;
	@Var(name = "spawnHutMages")
	public static boolean spawnHutMage = true;
	@Var(name = "spawnEggChickens")
	public static boolean spawnEggChicken = true;
	@Var(name = "spawnMoreZombies")
	public static boolean spawnMoreZombie = true;
	@Var(name = "spawnSplitSlimes")
	public static boolean spawnSplitSlime = true;
	@Var(name = "chargeHaunter")
	public static boolean chargeHaunter = true;
	@Var(name = "convertVillageZombie")
	public static boolean convertZombie = true;
	@Var(name = "dropLoot")
	public static boolean dropLoot = true;
	@Var(name = "dropBlockXP")
	public static boolean blockXP = true;
	@Var(name = "dropBreedingXP")
	public static boolean breedingXP = true;
	@Var(name = "dropSmeltingXP")
	public static boolean smeltingXP = true;
	@Var(name = "dropFishingXP")
	public static boolean fishingXP = true;
	@Var(name = "dropMobXP")
	public static boolean mobXP = true;
	@Var(name = "dropBlocks")
	public static boolean blockDrop = true;
	@Var(name = "dropObjects")
	public static boolean objectDrop = true;
	@Var(name = "naturalRegeneration")
	public static boolean regeneration = true;
	@Var(name = "daylightCycle")
	public static boolean dayCycle = true;
	@Var(name = "weatherChanges")
	public static boolean weather = true;
	@Var(name = "seasonLeafUpdate")
	public static boolean seasonLeaves = true;
	@Var(name = "leavesDecay")
	public static boolean leavesDecay = true;
	@Var(name = "repairExperience")
	public static boolean repairXP = true;
	@Var(name = "mobTick")
	public static boolean mobTick = true;
	@Var(name = "mobAttacks")
	public static boolean mobAttacks = true;
	@Var(name = "portals")
	public static boolean portals = true;
	@Var(name = "portalVoid")
	public static boolean voidPortal = true;
	@Var(name = "dropPlayerSkulls")
	public static boolean skullDrop = true;
	@Var(name = "dropPlayerItems")
	public static boolean playerDrop = true;
	@Var(name = "blockGravity")
	public static boolean blockGravity = true;
	@Var(name = "liquidPhysics")
	public static boolean liquidPhysics = true;
	@Var(name = "lavaFire")
	public static boolean lavaFire = true;
	@Var(name = "mergeWater")
	public static boolean mergeWater = true;
	@Var(name = "mergeInfinite")
	public static boolean mergeInfinite = true;
	@Var(name = "infighting")
	public static boolean infight = true;
//	@Var(name = "infightSameType")
//	public static boolean infightSame = true;
	@Var(name = "damageFall")
	public static boolean damageFall = true;
	@Var(name = "damageFire")
	public static boolean damageFire = true;
	@Var(name = "damageLava")
	public static boolean damageLava = true;
	@Var(name = "damageMolten")
	public static boolean damageMolten = true;
	@Var(name = "damageLightning")
	public static boolean damageLightning = true;
	@Var(name = "damageSquish")
	public static boolean damageSquish = true;
	@Var(name = "damageAcme")
	public static boolean damageAcme = true;
	@Var(name = "damageRadiation")
	public static boolean damageRadiation = true;
	@Var(name = "damagePoison")
	public static boolean damagePoison = true;
	@Var(name = "damagePotion")
	public static boolean damagePotion = true;
	@Var(name = "damageFlyingBox")
	public static boolean damageFlyingBox = true;
	@Var(name = "damageVoid")
	public static boolean damageVoid = true;
	@Var(name = "damageThorns")
	public static boolean damageThorns = true;
	@Var(name = "damageFireball")
	public static boolean damageFireball = true;
	@Var(name = "damageArrow")
	public static boolean damageArrow = true;
	@Var(name = "damageBullet")
	public static boolean damageBullet = true;
	@Var(name = "damageMobs")
	public static boolean damageMobs = true;
	@Var(name = "damageExplosion")
	public static boolean damageExplosion = true;
	@Var(name = "damageWall")
	public static boolean damageWall = true;
	@Var(name = "radiation")
	public static boolean radiation = true;
	@Var(name = "anvilFallDecay")
	public static boolean anvilFallDecay = true;
	@Var(name = "anvilRepairDecay")
	public static boolean anvilRepairDecay = true;
	@Var(name = "attacking")
	public static boolean attack = true;
	@Var(name = "cactusPrickly")
	public static boolean cactusDamage = true;
	@Var(name = "waterMobDrying")
	public static boolean waterMobDry = true;
	@Var(name = "itemBurning")
	public static boolean itemBurn = true;
	@Var(name = "xpOrbBurning")
	public static boolean xpOrbBurn = true;
	@Var(name = "smackingOrb")
	public static boolean knockOrb = true;
	@Var(name = "smackingDynamite")
	public static boolean knockDynamite = true;
	@Var(name = "smackingEgg")
	public static boolean knockEgg = true;
	@Var(name = "smackingSnowball")
	public static boolean knockSnowball = true;
	@Var(name = "smackingFishHook")
	public static boolean knockHook = true;
	@Var(name = "entityFishing")
	public static boolean hookEntity = true;
	@Var(name = "hookingRequiresDamage")
	public static boolean hookCheckDamage = true;
	@Var(name = "grassSpread")
	public static boolean grassSpread = true;
	@Var(name = "blackenedSoilSpread")
	public static boolean darkSoilSpread = true;
	@Var(name = "blackenedDirtSpread")
	public static boolean darkDirtSpread = true;
	@Var(name = "grassDecay")
	public static boolean grassDecay = true;
	@Var(name = "blackenedSoilDecay")
	public static boolean darkSoilDecay = true;
	@Var(name = "grassDrying")
	public static boolean grassDry = true;
	@Var(name = "tallgrassDrying")
	public static boolean tallgrassDry = true;
	@Var(name = "flowerDrying")
	public static boolean flowerDry = true;
	@Var(name = "plantDrying")
	public static boolean plantDry = true;
	@Var(name = "leafDrying")
	public static boolean leafDry = true;
	@Var(name = "saplingDrying")
	public static boolean saplingDry = true;
	@Var(name = "reedDrying")
	public static boolean reedDry = true;
	@Var(name = "vineDrying")
	public static boolean vineDry = true;
	@Var(name = "myceliumSpread")
	public static boolean mycelSpread = true;
	@Var(name = "myceliumDecay")
	public static boolean mycelDecay = true;
	@Var(name = "farmlandDecay")
	public static boolean cropDecay = true;
	@Var(name = "farmlandDrying")
	public static boolean cropDrying = true;
	@Var(name = "farmlandSoaking")
	public static boolean cropSoaking = true;
	@Var(name = "farmlandTrampling")
	public static boolean cropTrampling = true;
	@Var(name = "iceMelting")
	public static boolean iceMelt = true;
	@Var(name = "snowMelting")
	public static boolean snowMelt = true;
	@Var(name = "snowBlockMelting")
	public static boolean snowFullMelt = true;
	@Var(name = "chestLocking")
	public static boolean locking = true;
	@Var(name = "teleFragging")
	public static boolean telefrag = true;
	@Var(name = "checkRespawn")
	public static boolean checkBed = true;
	@Var(name = "chunkLoaders")
	public static boolean loaders = true;
	@Var(name = "fragileItems")
	public static boolean itemFallDamage = true;
	@Var(name = "registration")
	public static boolean register = true;
	@Var(name = "preload_chunks")
	public static boolean preload = true;

	@Var(name = "keepInventory")
	public static boolean keepInventory = false;
	@Var(name = "cleanCut")
	public static boolean cleanCut = false;
	@Var(name = "mergeLava")
	public static boolean mergeLava = false;
	@Var(name = "mergeFinite")
	public static boolean mergeFinite = false;
	@Var(name = "veryHungryRabbits")
	public static boolean rabidRabbits = false;
	@Var(name = "snowStacking")
	public static boolean snowStack = false;
	@Var(name = "authentication")
	public static boolean auth = false;
//	@Var(name = "teleportForAll")
//	public static boolean teleportAllowed = false;
//	@Var(name = "preload_chunks_all") // Vorsicht Lag!!
//	public static boolean preloadAll = false;
	
//	@Var(name = "maxPolygonalPoints")
//	public static int polygonalPoints = -1;
//	@Var(name = "maxPolyhedronPoints")
//	public static int polyhedronPoints = -1;
//	@Var(name = "maxEditRadius")
//	public static int editRadius = -1;
//	@Var(name = "maxBrushRadius")
//	public static int brushRadius = 500;
//	@Var(name = "historySize")
//	public static int history = 300;
	
	@Var(name = "randomTickSpeed")
	public static int randomTick = 3;
	@Var(name = "weatherTickSpeed")
	public static int weatherTick = 1;
	@Var(name = "lightningChance")
	public static int boltChance = 100000;
	@Var(name = "igniteChance")
	public static int igniteChance = 100;
	@Var(name = "spawnRadius")
	public static int spawnRadius = 10;
	@Var(name = "originRadius", min = 0, max = 262144)
	public static int originRadius = 16384;
	@Var(name = "respawnTime")
	public static int respawnTime = 0;
	@Var(name = "hurtCooldown")
	public static int hurtDelay = 20;
	@Var(name = "attackCooldown")
	public static int attackDelay = 0;
	@Var(name = "spawnGroupCount")
	public static int spawnGroups = 3;
	@Var(name = "spawnGroupDistance")
	public static int spawnGroupDist = 6;
	@Var(name = "mobSpawnRadius")
	public static int mobSpawnDist = 8;
	@Var(name = "mobPlayerDistance")
	public static int mobPlayerDist = 24;
	@Var(name = "saveInterval")
	public static int saveInterval = 900;
	@Var(name = "maxPlayers")
	public static int playerLimit = 0;
	@Var(name = "compressAbove")
	public static int compression = 256;
	@Var(name = "pistonPushLimit")
	public static int pistonLimit = 16;
	@Var(name = "gravelFlintChance")
	public static int flintChance = 10;
	@Var(name = "timeFlow")
	public static int timeFlow = 1;
	@Var(name = "emptyTicks")
	public static int unloadTicks = 1200;
	@Var(name = "rabbitMateChance")
	public static int rabbitMateChance = 10;
	@Var(name = "killerBunnyChance")
	public static int killerBunnyChance = 1000;
	@Var(name = "fallPortalHeight")
	public static int portalHeight = 256;
	@Var(name = "damageOrb")
	public static int orbDamageSelf = 5;
	@Var(name = "vineGrowthChance")
	public static int vineGrowth = 4;
	@Var(name = "blueShroomChance")
	public static int blueShroomGrowth = 25;
	@Var(name = "mushroomChance")
	public static int shroomGrowth = 25;
	@Var(name = "cropGrowthChance")
	public static int cropGrowth = 26;
	@Var(name = "stemGrowthChance")
	public static int stemGrowth = 26;
	@Var(name = "treeGrowthChance")
	public static int treeGrowth = 7;
	@Var(name = "wartGrowthChance")
	public static int wartGrowth = 10;
	@Var(name = "cocoaGrowthChance")
	public static int cocoaGrowth = 5;
	@Var(name = "reedGrowthHeight")
	public static int reedHeight = 3;
	@Var(name = "cactusGrowthHeight")
	public static int cactusHeight = 3;
	@Var(name = "orbThorns")
	public static int orbDamageOther = 0;
	@Var(name = "weatherChance")
	public static int weatherChance = 48000;
	@Var(name = "viewDistance", min = 2, max = 128)
	public static int distance = 10;
	@Var(name = "healChance")
	public static int healChance = 5;
	@Var(name = "hopperCooldown", min = 0, max = 160)
	public static int hopperDelay = 2;
	@Var(name = "hopperCartCooldown", min = 0, max = 160)
	public static int hopperCartDelay = 1;
	@Var(name = "xpCooldown", min = 0, max = 10)
	public static int xpDelay = 0; // 2
	@Var(name = "maxSpawns")
	public static int maxMobs = 120;
	@Var(name = "eggLayTime")
	public static int eggTimer = 6000;
	@Var(name = "connectionTimeout", min = 10, max = 300)
	public static int timeout = 30;

//	@Var(name = "spawnX", min = -World.MAX_SIZE + 1, max = World.MAX_SIZE - 1)
//	public static int spawnX = 0;
//	@Var(name = "spawnY", min = -1, max = 511)
//	public static int spawnY = 64;
//	@Var(name = "spawnZ", min = -World.MAX_SIZE + 1, max = World.MAX_SIZE - 1)
//	public static int spawnZ = 0;
//	@Var(name = "spawnDim")
//	public static int spawnDim = 0;

	@Var(name = "gravity")
	public static float gravity = 1.0f;
	@Var(name = "knockback")
	public static float knockback = 1.0f;

	@Var(name = "spawnYaw", min = -180.0f, max = 180.0f)
	public static float spawnYaw = -90.0f;
	@Var(name = "spawnPitch", min = -89.0f, max = 89.0f)
	public static float spawnPitch = 0.0f;

	@Var(name = "password")
	public static String password = "";
	
	static {
		for(Field field : Config.class.getDeclaredFields()) {
			if(field.isAnnotationPresent(Var.class)) {
				if(!Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()))
					throw new IllegalArgumentException("Feld für Variable " + field + " muss statisch und änderbar sein!");
				Var value = field.getAnnotation(Var.class);
				if(value.name().isEmpty())
					throw new IllegalArgumentException("Variablenname von " + field + " kann nicht leer sein!");
				if(VARS.containsKey(value.name()))
					throw new IllegalArgumentException("Variable " + value.name() + " existiert bereits!");
				VARS.put(value.name(), new Config.Value(field, value));
			}
		}
	}

	public static void clear() {
		for(Config.Value value : VARS.values()) {
			value.setValue(value.def);
		}
	}
	
	public static void setCallback(Runnable callback, String ... vars) {
		for(String key : vars) {
			VARS.get(key).callback = callback;
		}
	}

	public static void set(String key, String value, boolean update) {
		Config.Value vl = VARS.get(key);
		if(vl != null) {
			vl.setValue(value);
			if(update && vl.callback != null)
				vl.callback.run();
		}
	}
}
