package com.imnotjames.tribbleengine.engine;

import com.imnotjames.tribbleengine.Family;
import com.imnotjames.tribbleengine.engine.events.EngineEntityEvent;
import com.imnotjames.tribbleengine.engine.events.EngineEntityEventListener;
import com.imnotjames.tribbleengine.engine.events.EngineSystemEvent;
import com.imnotjames.tribbleengine.engine.events.EngineSystemEventListener;
import com.imnotjames.tribbleengine.entity.Entity;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Engine {
	private static Comparator<EngineSystem> engineSystemPriorityComparator = new Comparator<EngineSystem>() {
		@Override
		public int compare(EngineSystem a, EngineSystem b) {
			return b.getPriority() - a.getPriority();
		}
	};

	private List<Entity> entities;

	private List<EngineSystem> engineSystems;

	private List<EngineEntityEventListener> engineEntityEventListeners = new ArrayList<EngineEntityEventListener>();
	private List<EngineSystemEventListener> engineSystemEventListeners = new ArrayList<EngineSystemEventListener>();

	public Engine() {
		this.entities = new ArrayList<Entity>();
		this.engineSystems = new ArrayList<EngineSystem>();
	}

	public void update(long delta) {
		if (delta <= 0) {
			throw new IllegalArgumentException("Update delta must be greater than 0");
		}

		for (EngineSystem s : engineSystems) {
			s.update(delta);
		}
	}

	public void addEntity(Entity entity) {
		this.entities.add(entity);

		EngineEntityEvent event = new EngineEntityEvent(this, entity);

		for (EngineEntityEventListener listener : this.engineEntityEventListeners) {
			listener.entityAdded(event);
		}
	}

	public void removeEntity(Entity entity) {
		// If we don't remove the entity don't fire events
		if (! this.entities.remove(entity)) {
			return;
		}

		EngineEntityEvent event = new EngineEntityEvent(this, entity);

		for (EngineEntityEventListener listener : this.engineEntityEventListeners) {
			listener.entityRemoved(event);
		}
	}

	public void removeEntities() {
		while (this.entities.size() > 0) {
			this.removeEntity(this.entities.get(0));
		}
	}

	public void removeEntities(Family family) {
		List<Entity> entities = this.getEntities(family);

		for (Entity entity : entities) {
			this.removeEntity(entity);
		}
	}

	public List<Entity> getEntities() {
		return this.entities;
	}

	public List<Entity> getEntities(Family family) {
		if (family == null) {
			return this.entities;
		}

		List<Entity> filteredEntities = new ArrayList<Entity>();

		for (Entity entity : this.entities) {
			if (family.matches(entity.getFamily())) {
				filteredEntities.add(entity);
			}
		}

		return filteredEntities;
	}

	public List<EngineSystem> getEngineSystems() {
		return this.engineSystems;
	}

	public void addEngineSystem(EngineSystem engineSystem) {
		this.engineSystems.add(engineSystem);

		engineSystem.setUp(this);

		this.engineSystems.sort(Engine.engineSystemPriorityComparator);

		EngineSystemEvent event = new EngineSystemEvent(this, engineSystem);

		for (EngineSystemEventListener listener : this.engineSystemEventListeners) {
			listener.systemAdded(event);
		}
	}

	public void removeEngineSystem(EngineSystem engineSystem) {
		if (this.engineSystems.remove(engineSystem)) {
			engineSystem.tearDown(this);

			EngineSystemEvent event = new EngineSystemEvent(this, engineSystem);

			for (EngineSystemEventListener listener : this.engineSystemEventListeners) {
				listener.systemRemoved(event);
			}
		}
	}

	public void removeEngineSystems() {
		while (this.engineSystems.size() > 0) {
			this.removeEngineSystem(this.engineSystems.get(0));
		}
	}

	public void addEngineEntityEventListener(EngineEntityEventListener listener) {
		this.engineEntityEventListeners.add(listener);
	}

	public void removeEngineEntityEventListener(EngineEntityEventListener listener) {
		this.engineEntityEventListeners.remove(listener);
	}

	public void addEngineSystemEventListener(EngineSystemEventListener listener) {
		this.engineSystemEventListeners.add(listener);
	}

	public void removeEngineSystemEventListener(EngineSystemEventListener listener) {
		this.engineSystemEventListeners.remove(listener);
	}
}

@@ -511,258 +511,91 @@
 st;%0A
-%0Apublic class Engine %7B%0A%09private static Comparator%3CEngineSystem%3E engineSystemPriorityComparator = new Comparator%3CEngineSystem%3E() %7B%0A%09%09@Override%0A%09%09public int compare(EngineSystem a, EngineSystem b) %7B%0A%09%09%09return b.getPriority() - a.getPriority();%0A%09%09%7D%0A%09%7D;%0A
+import java.util.Collection;%0Aimport java.util.PriorityQueue;%0A%0Apublic class Engine %7B
 %0A%09pr
@@ -625,36 +625,42 @@
 ties;%0A%0A%09private 
-List
+Collection
 %3CEngineSystem%3E e
@@ -996,32 +996,211 @@
 new 
-ArrayList%3CEngineSystem%3E(
+PriorityQueue%3CEngineSystem%3E(%0A%09%09%0910,%0A%09%09%09new Comparator%3CEngineSystem%3E() %7B%0A%09%09%09%09@Override%0A%09%09%09%09public int compare(EngineSystem a, EngineSystem b) %7B%0A%09%09%09%09%09return b.getPriority() - a.getPriority();%0A%09%09%09%09%7D%0A%09%09%09%7D%0A%09%09
 );%0A%09
@@ -2691,20 +2691,26 @@
 %09public 
-List
+Collection
 %3CEngineS
@@ -2902,75 +2902,8 @@
 );%0A%0A
-%09%09this.engineSystems.sort(Engine.engineSystemPriorityComparator);%0A%0A
 %09%09En
@@ -3476,44 +3476,149 @@
 %7B%0A%09%09
-while (this.engineSystems.size() %3E 0
+EngineSystem%5B%5D systems = this.engineSystems.toArray(new EngineSystem%5Bthis.engineSystems.size()%5D);%0A%0A%09%09for (EngineSystem engineSystem : systems
 ) %7B%0A
@@ -3644,21 +3644,16 @@
 eSystem(
-this.
 engineSy
@@ -3656,24 +3656,16 @@
 neSystem
-s.get(0)
 );%0A%09%09%7D%0A%09

use a Collection for EngineSystems in the Engine, and make use of the PriorityQueue