package net.minecraft.src;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;

import net.minecraft.client.Minecraft;

import org.lwjgl.input.Keyboard;

/**
 * PowerCraft's property manager with advanced formatting and value checking.
 * 
 * @author MightyPork
 */
public class PC_PropertyManager {
	private String filename;
	private PC_SortedProperties pr = new PC_SortedProperties();
	public Hashtable<String, Property> stuff;
	private String comment = "";
	public boolean cfgSeparateSections = true;

	public PC_PropertyManager(Hashtable<String, Property> props, String filename, String comment) {
		this.filename = filename;
		this.stuff = props;
		this.comment = comment;
		// apply();
	}

	/**
	 * Create property manager from file path and an initial comment.
	 * 
	 * @param filename file with the props
	 * @param comment the initial comment. Use \n if you want.
	 */
	public PC_PropertyManager(String filename, String comment) {
		this.filename = filename;
		this.stuff = new Hashtable<String, Property>();
		this.comment = comment;
		stuff = new Hashtable<String, Property>();
	}

	/**
	 * Add a numeric property
	 * 
	 * @param n key
	 * @param d default value
	 * @param comment the in-file comment
	 * @return manager instance
	 */
	public PC_PropertyManager putInteger(String n, int d, String comment) {
		stuff.put(n, new Property(n, d, PropertyType.INT, comment));
		return this;
	}

	/**
	 * Add a numeric property
	 * 
	 * @param n key
	 * @param d default value
	 * @return manager instance
	 */
	public PC_PropertyManager putInteger(String n, int d) {
		stuff.put(n, new Property(n, d, PropertyType.INT, null));
		return this;
	}

	/**
	 * Add a numeric property
	 * 
	 * @param n key
	 * @param d default value
	 * @param comment the in-file comment
	 * @return manager instance
	 */
	public PC_PropertyManager putKey(String n, int d, String comment) {
		stuff.put(n, new Property(n, d, PropertyType.KEY, comment));
		return this;
	}

	/**
	 * Add a numeric property
	 * 
	 * @param n key
	 * @param d default value
	 * @return manager instance
	 */
	public PC_PropertyManager putKey(String n, int d) {
		stuff.put(n, new Property(n, d, PropertyType.KEY, null));
		return this;
	}

	/**
	 * Add a item property
	 * 
	 * @param n key
	 * @param d default value
	 * @param comment the in-file comment
	 * @return manager instance
	 */
	public PC_PropertyManager putItem(String n, int d, String comment) {
		stuff.put(n, new Property(n, d, PropertyType.ITEM, comment));
		return this;
	}

	/**
	 * Add a item property
	 * 
	 * @param n key
	 * @param d default value
	 * @return manager instance
	 */
	public PC_PropertyManager putItem(String n, int d) {
		stuff.put(n, new Property(n, d, PropertyType.ITEM, null));
		return this;
	}

	/**
	 * Add a block property
	 * 
	 * @param n key
	 * @param d default value
	 * @param comment the in-file comment
	 * @return manager instance
	 */
	public PC_PropertyManager putBlock(String n, int d, String comment) {
		stuff.put(n, new Property(n, d, PropertyType.BLOCK, comment));
		return this;
	}

	/**
	 * Add a block property
	 * 
	 * @param n key
	 * @param d default value
	 * @return manager instance
	 */
	public PC_PropertyManager putBlock(String n, int d) {
		stuff.put(n, new Property(n, d, PropertyType.BLOCK, null));
		return this;
	}

	/**
	 * Add a string property
	 * 
	 * @param n key
	 * @param d default value
	 * @param comment the in-file comment
	 * @return manager instance
	 */
	public PC_PropertyManager putString(String n, String d, String comment) {
		stuff.put(n, new Property(n, d, PropertyType.STRING, comment));
		return this;
	}

	/**
	 * Add a string property
	 * 
	 * @param n key
	 * @param d default value
	 * @return manager instance
	 */
	public PC_PropertyManager putString(String n, String d) {
		stuff.put(n, new Property(n, d, PropertyType.STRING, null));
		return this;
	}

	/**
	 * Add a boolean property
	 * 
	 * @param n key
	 * @param d default value
	 * @param comment the in-file comment
	 * @return manager instance
	 */
	public PC_PropertyManager putBoolean(String n, boolean d, String comment) {
		stuff.put(n, new Property(n, d, PropertyType.BOOLEAN, comment));
		return this;
	}

	/**
	 * Add a boolean property
	 * 
	 * @param n key
	 * @param d default value
	 * @return manager instance
	 */
	public PC_PropertyManager putBoolean(String n, boolean d) {
		stuff.put(n, new Property(n, d, PropertyType.BOOLEAN, null));
		return this;
	}

	/**
	 * Get a property entry (rarely used)
	 * 
	 * @param n key
	 * @return the entry
	 */
	private Property get(String n) {
		try {
			return stuff.get(n);
		} catch (Throwable t) {
			return null;
		}
	}

	/**
	 * Get string property
	 * 
	 * @param n key
	 * @return the string found, or null
	 */
	public String getString(String n) {
		try {
			return get(n).getString();
		} catch (Throwable t) {
			return null;
		}
	}

	/**
	 * Get numeric property
	 * 
	 * @param n key
	 * @return the int found, or null
	 */
	public Integer getInteger(String n) {
		try {
			return get(n).getInteger();
		} catch (Throwable t) {
			return -1;
		}
	}

	/**
	 * Get boolean property
	 * 
	 * @param n key
	 * @return the boolean found, or false
	 */
	public Boolean getBoolean(String n) {
		try {
			return stuff.get(n).getBoolean();
		} catch (Throwable t) {
			return false;
		}
	}

	/**
	 * Is the key pressed? (works only for properties of type KEY)
	 * 
	 * @param n key of the key property
	 * @return is pressed
	 */
	public Boolean isKeyDown(String n) {
		try {
			return stuff.get(n).isKeyDown();
		} catch (Throwable t) {
			return false;
		}
	}

	/**
	 * Load, fix and write to file.
	 * 
	 * @return hashtable of the entries (rarely used)
	 */
	public Hashtable<String, Property> apply() {
		boolean needsSave = false;
		try {
			
			(new File(Minecraft.getMinecraftDir(), filename)).mkdirs();
			
			pr.load(new FileInputStream(Minecraft.getMinecraftDir() + filename));
		} catch (IOException e) {
			needsSave = true;
			pr = new PC_SortedProperties();
		}

		pr.cfgSeparateSectionsByEmptyLine = cfgSeparateSections;

		ArrayList<String> a = new ArrayList<String>();

		for (Property entry : stuff.values()) {

			a.add(entry.name);

			String propOrig = pr.getProperty(entry.name);
			entry.parse(propOrig);
			entry.validate();

			if (propOrig == null || !entry.toString().equals(propOrig)) {

				pr.setProperty(entry.name, entry.toString());

				if (entry.comment != null) {
					pr.setKeyComment(entry.name, entry.comment);
				}

				needsSave = true;
			}
		}

		for (String propname : pr.keySet().toArray(new String[pr.keySet().size()])) {

			if (!a.contains(propname)) {
				pr.remove(propname);
				System.out.println("PowerCraft: Removing unused property \"" + propname + "\" from config file " + filename);
				needsSave = true;
			}

		}

		if (needsSave) {
			try {
				System.out.println("PowerCraft: Saving modified property file " + filename);
				pr.store(new FileOutputStream(Minecraft.getMinecraftDir() + filename), comment);
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
		}
		return stuff;
	}

	private class Property {
		public String name;
		public int defnum = -1;
		public String defstr = "";
		public boolean defbool = false;

		public PropertyType type;
		public int num = -1;
		public String str = "";
		public boolean bool = false;
		public String comment;

		/**
		 * Property entry
		 * 
		 * @param key property key
		 * @param default_value default value
		 * @param entry_type property type from enum
		 * @param entry_comment property comment or null
		 */
		public Property(String key, int default_value, PropertyType entry_type, String entry_comment) {
			name = key;
			defnum = default_value;
			type = entry_type;
			comment = entry_comment;
		}

		/**
		 * Property
		 * 
		 * @param n key
		 * @param d default value
		 * @param t type
		 */
		public Property(String key, String default_value, PropertyType entry_type, String entry_comment) {
			name = key;
			defstr = default_value;
			type = entry_type;
			comment = entry_comment;
		}

		/**
		 * Property
		 * 
		 * @param key key
		 * @param d default value
		 * @param entry_type type
		 */
		public Property(String key, boolean default_value, PropertyType entry_type, String entry_comment) {
			name = key;
			defbool = default_value;
			type = entry_type;
			comment = entry_comment;
		}

		/**
		 * prepare the contents for insertion into Properties
		 * 
		 * @return the string prepared, or null if type is invalid
		 */
		@Override
		public String toString() {
			if (!isValid()) {
				if (type == PropertyType.INT || type == PropertyType.ITEM || type == PropertyType.BLOCK) {
					num = defnum;
				}
			}
			if (type == PropertyType.BLOCK || type == PropertyType.ITEM || type == PropertyType.INT) { return Integer.toString(num); }
			if (type == PropertyType.STRING) { return str; }
			if (type == PropertyType.KEY) { return Keyboard.getKeyName(num) == null ? "none" : Keyboard.getKeyName(num); }
			if (type == PropertyType.BOOLEAN) { return bool ? "True" : "False"; }
			return null;
		}

		/**
		 * Load property value from a file
		 * 
		 * @param string the string loaded
		 * @return this entry
		 */
		public Property parse(String string) {
			if (type == PropertyType.BLOCK || type == PropertyType.ITEM || type == PropertyType.INT) {
				if (string == null) {
					num = defnum;
					return this;
				}
				try {
					num = Integer.parseInt(string);
				} catch (NumberFormatException e) {
					num = defnum;
				}
			}

			if (type == PropertyType.KEY) {
				if (string == null) {
					num = defnum;
					return this;
				}
				num = Keyboard.getKeyIndex(string);
				if (num == Keyboard.KEY_NONE) {
					num = defnum;
				}
			}

			if (type == PropertyType.STRING) {
				if (string == null) {
					str = defstr;
					return this;
				}
				this.str = string;
			}

			if (type == PropertyType.BOOLEAN) {
				if (string == null) {
					bool = defbool;
					return this;
				}
				String string2 = string.toLowerCase();
				bool = string2.equals("yes") || string2.equals("true") || string2.equals("on") || string2.equals("enabled")
						|| string2.equals("enable");
			}

			return this;
		}

		/**
		 * is this key pressed?
		 * 
		 * @return pressed state
		 */
		public boolean isKeyDown() {
			return type == PropertyType.KEY && Keyboard.isKeyDown(num);
		}

		/**
		 * Get number
		 * 
		 * @return the number
		 */
		public int getInteger() {
			return num;
		}

		/**
		 * Get string
		 * 
		 * @return the string
		 */
		public String getString() {
			return str;
		}

		/**
		 * Get boolean
		 * 
		 * @return the boolean
		 */
		public boolean getBoolean() {
			return bool;
		}

		/**
		 * Is this entry valid?
		 * 
		 * @return is valid
		 */
		public boolean isValid() {
			if (type == PropertyType.BLOCK) { return num > 0 && num <= Block.blocksList.length && Block.blocksList[num] == null; }
			if (type == PropertyType.ITEM) { return num > 200 && num <= (32000 - 256) && Item.itemsList[num + 256] == null; }
			if (type == PropertyType.KEY) { return Keyboard.getKeyName(num) != null; }
			if (type == PropertyType.STRING) { return str != null; }
			if (type == PropertyType.BOOLEAN || type == PropertyType.INT) { return true; }
			return false;
		}

		/**
		 * If this entry is not valid, change it to the dafault value.
		 */
		public void validate() {
			if (!isValid()) {
				/*
				 * if (type == PCMPType.BLOCK) {
				 * num = defnum;
				 * }
				 * if (type == PCMPType.ITEM) {
				 * num = defnum;
				 * }
				 */
				if (type == PropertyType.KEY) {
					num = defnum;
				}
				if (type == PropertyType.STRING) {
					str = defstr;
				}
			}
		}
	}

	private enum PropertyType {
		BLOCK, ITEM, KEY, STRING, BOOLEAN, INT;
	}
}