using System.Collections;
using Newtonsoft.Json.Linq;
using System.IO;
using Newtonsoft.Json;
using UnityEngine;
using System.Collections.Generic;

public abstract class Module {
	private int id;
	private string name;
	private string uniqueName;
	private float health;
	private float resistance;

	protected Sprite icon;
	protected Sprite silhouette;
	private bool statusDisplayed;

	protected Module(JObject json) {
		id = Util.classFreshId();
		name = (string)json["name"];
		uniqueName = (string)json["uniqueName"];
		health = (float)json["hitPoints"];
		resistance = (float)json["resistance"];

		JToken token;
		statusDisplayed = true;
		if (json.TryGetValue("statusDisplayed", out token)) {
			statusDisplayed = (bool)json["statusDisplayed"];
		}
	}

	public static JObject jsonFromFile(string jsonPath) {
		StreamReader reader = File.OpenText(jsonPath);
		return (JObject)JToken.ReadFrom(new JsonTextReader(reader));
	}

	public int getID() {
		return id;
	}

	public string getName() {
		return name;
	}

	public string getUniqueName() {
		return uniqueName;
	}

	public float getHealth() {
		return health;
	}

	public float getResistance() {
		return resistance;
	}

	public bool isStatusDisplayed() {
		return statusDisplayed;
	}

	public Sprite getSprite() {
		return icon;
	}

	public Sprite getSilhouette() {
		return silhouette;
	}

	virtual public bool canBeDisabled() {
		return true;
	}

	public abstract void updateClasses();
	public abstract ModuleData getData(int parent, float angle, Vector position);
}

public abstract class ModuleData {
	protected float health;
	protected int parent;
	protected int id;
	protected Module template;
	protected bool wasFiring;
	protected bool firing;
	private bool disabled;
	private Vector position;

	private List<ModuleData> hiddenTwins; // list of 'twin' moduleDatas that share the same health, (only used by server)
	private bool isHiddenTwin; // (only used by server)

	public ModuleData(int parent, Module template, Vector position) {
		this.parent = parent;
		this.id = Util.freshId();
		this.template = template;
		this.health = template.getHealth();
		this.position = position;

		this.hiddenTwins = new List<ModuleData>();
		this.isHiddenTwin = false;

		GameController.addModuleData(this);
	}

	//SHOULD ONLY BE CALLED BY NETWORK MANAGER, WHEN RECEIVING A CREATE MODULE DATA COMMAND
	public void setID(int newID) {
		this.id = newID;
	}

	public int getID() {
		return id;
	}

	public int getParent() {
		return parent;
	}

	public void addHiddenTwin(ModuleData twin) {
		hiddenTwins.Add(twin);
		twin.isHiddenTwin = true;
	}

	public bool isAHiddenTwin() {
		return isHiddenTwin;
	}

	public Module getTemplate() {
		return template;
	}

	public Vector getPosition() {
		return position;
	}

	public float getHealth() {
		return health;
	}

	public void addDamage(float amount) {
		if (health == 0.0f) {
			return;
		}

		float newHealth = Mathf.Max(0.0f, health - amount * template.getResistance());

		foreach(ModuleData twin in hiddenTwins) {
			Requests.setHealth(twin, newHealth);
		}

		Requests.setHealth(this, newHealth);
	}

	virtual public void setHealth(float newHealth) {
		health = newHealth;
	}

	virtual public void updateTime(float delta) {
	}

	virtual public bool isFiring() {
		if (NetworkManager.isServer()) {
			return false;
		}
		else {
			return firing;
		}
	}

	public bool isDestroyed() {
		return getHealth() == 0.0f;
	}

	public bool isDisabled() {
		return disabled;
	}

	public bool isOperationnal() {
		return !isDestroyed() && !isDisabled();
	}

	//only called by server to send state updates to the clients
	public void updateState() {
		if (wasFiring != isFiring()) {
			Requests.setIsFiring(this, isFiring());
			wasFiring = isFiring();
		}
	}

	//only used by clients
	public void setFiring(bool value) {
		firing = value;
	}

	virtual public void setDisabled(bool value) {
		disabled = value;
	}

	virtual public void checkDestroyedUnits(List<DeployedUnit> destroyedUnits) {
	}
}