package com.team254.lib.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

/**
 * ConstantsBase
 * 
 * Base class for storing robot constants. Anything stored as a public static
 * field will be reflected and be able to set externally
 * 
 * @author Tom Bottiglieri
 */
public abstract class ConstantsBase {
	HashMap<String, Boolean> modifiedKeys = new HashMap<String, Boolean>();

	public abstract String getFileLocation();

	public static class Constant {
		public String name;
		public Class type;
		public Object value;

		public Constant(String name, Class type, Object value) {
			this.name = name;
			this.type = type;
			this.value = value;
		}

		public boolean equals(Object o) {
			String itsName = ((Constant) o).name;
			Class itsType = ((Constant) o).type;
			Object itsValue = ((Constant) o).value;
			return o instanceof Constant && this.name.equals(itsName)
					&& this.type.equals(itsType) && this.value.equals(itsValue);
		}
	}

	public File getFile() {
		String filePath = getFileLocation();
		filePath = filePath.replaceFirst("^~", System.getProperty("user.home"));
		return new File(filePath);
	}

	public boolean setConstant(String name, Double value) {
		return setConstantRaw(name, value);
	}

	public boolean setConstant(String name, Integer value) {
		return setConstantRaw(name, value);
	}

	public boolean setConstant(String name, String value) {
		return setConstantRaw(name, value);
	}

	private boolean setConstantRaw(String name, Object value) {
		boolean success = false;
		Field[] declaredFields = this.getClass().getDeclaredFields();
		for (Field field : declaredFields) {
			if (java.lang.reflect.Modifier.isStatic(field.getModifiers())
					&& field.getName().equals(name)) {
				try {
					field.set(this, value);
					success = true;
					modifiedKeys.put(name, true);
				} catch (IllegalArgumentException | IllegalAccessException e) {
					System.out.println("Could not set field: " + name);
				}
			}
		}
		return success;
	}

	public Object getValueForConstant(String name) throws Exception {
		Field[] declaredFields = this.getClass().getDeclaredFields();
		for (Field field : declaredFields) {
			if (java.lang.reflect.Modifier.isStatic(field.getModifiers())
					&& field.getName().equals(name)) {
				try {
					return field.get(this);
				} catch (IllegalArgumentException | IllegalAccessException e) {
					throw new Exception("Constant not found");
				}
			}
		}
		throw new Exception("Constant not found");
	}

	public Constant getConstant(String name) {
		Field[] declaredFields = this.getClass().getDeclaredFields();
		for (Field field : declaredFields) {
			if (java.lang.reflect.Modifier.isStatic(field.getModifiers())
					&& field.getName().equals(name)) {
				try {
					return new Constant(field.getName(), field.getType(),
							field.get(this));
				} catch (IllegalArgumentException | IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return new Constant("", Object.class, 0);
	}

	public Collection<Constant> getConstants() {
		Field[] declaredFields = this.getClass().getDeclaredFields();
		List<Constant> constants = new ArrayList<Constant>(
				declaredFields.length);
		for (Field field : declaredFields) {
			if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
				Constant c;
				try {
					c = new Constant(field.getName(), field.getType(),
							field.get(this));
					constants.add(c);
				} catch (IllegalArgumentException | IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return constants;
	}

	public JSONObject getJSONObjectFromFile() throws IOException,
			ParseException {
		File file = getFile();
		if (file == null || !file.exists()) {
			return new JSONObject();
		}
		FileReader reader;
		reader = new FileReader(file);
		JSONParser jsonParser = new JSONParser();
		JSONObject jsonObject = (JSONObject) jsonParser.parse(reader);
		return jsonObject;
	}

	public void loadFromFile() {
		try {
			JSONObject jsonObject = getJSONObjectFromFile();
			Set keys = jsonObject.keySet();
			for (Object o : keys) {
				String key = (String) o;
				Object value = jsonObject.get(o);
				if (value instanceof Long
						&& getConstant(key).type.equals(int.class)) {
					value = new BigDecimal((Long) value).intValueExact();
				}
				setConstantRaw(key, value);
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void saveToFile() {
		File file = getFile();
		if (file == null) {
			return;
		}
		try {
			JSONObject json = getJSONObjectFromFile();
			FileWriter writer = new FileWriter(file);
			for (String key : modifiedKeys.keySet()) {
				try {
					Object value = getValueForConstant(key);
					json.put(key, value);
				} catch (Exception e) {
				}
			}
			writer.write(json.toJSONString());
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

@@ -2125,24 +2125,63 @@
 %7B%0A%09%09%09%09try %7B%0A
+%09%09%09%09%09Object current = field.get(this);%0A
 %09%09%09%09%09field.s
@@ -2218,16 +2218,52 @@
 = true;%0A
+%09%09%09%09%09if (!value.equals(current)) %7B%0A%09
 %09%09%09%09%09mod
@@ -2285,24 +2285,31 @@
 ame, true);%0A
+%09%09%09%09%09%7D%0A
 %09%09%09%09%7D catch 
@@ -5501,24 +5501,50 @@
 eption e) %7B%0A
+%09%09%09%09%09e.printStackTrace();%0A
 %09%09%09%09%7D%0A%09%09%09%7D%0A%09

Fix bug in ConstantsBasE