/**
 * Copyright 2008 - 2012
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 * @project loon
 * @author cping
 * @email：javachenpeng@yahoo.com
 * @version 0.3.3
 */
package loon.action.sprite.painting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import loon.action.sprite.SpriteBatch;
import loon.core.timer.GameTime;

public final class GameComponentCollection {

	private ArrayList<IGameComponent> collections;

	private ArrayList<IGameComponent> collectionsToUpdate;

	private ArrayList<IGameComponent> collectionsToDraw;

	private Comparator<IGameComponent> igameDrawComparator = new Comparator<IGameComponent>() {
		public int compare(IGameComponent one, IGameComponent two) {
			if (one instanceof DrawableGameComponent
					&& two instanceof DrawableGameComponent) {
				return ((DrawableGameComponent) one).getDrawOrder()
						- ((DrawableGameComponent) two).getDrawOrder();
			}
			return 0;
		}
	};

	private Comparator<IGameComponent> igameUpdateComparator = new Comparator<IGameComponent>() {
		public int compare(IGameComponent one, IGameComponent two) {
			if (one instanceof GameComponent && two instanceof GameComponent) {
				return ((GameComponent) one).getUpdateOrder()
						- ((GameComponent) two).getUpdateOrder();
			}
			return 0;
		}
	};

	public GameComponentCollection() {
		this.collections = new ArrayList<IGameComponent>();
		this.collectionsToUpdate = new ArrayList<IGameComponent>();
		this.collectionsToDraw = new ArrayList<IGameComponent>();
	}

	public int size() {
		return collections.size();
	}

	public void sortDraw() {
		Collections.sort(collections, igameDrawComparator);
	}

	public void sortUpdate() {
		Collections.sort(collections, igameUpdateComparator);
	}

	public ArrayList<IGameComponent> list() {
		return new ArrayList<IGameComponent>(collections);
	}

	public IGameComponent get(int idx) {
		return collections.get(idx);
	}

	void draw(SpriteBatch batch, GameTime gameTime) {
		if (isClear) {
			return;
		}
		if (collectionsToDraw.size() > 0) {
			collectionsToDraw.clear();
		}
		for (IGameComponent drawable : collections) {
			collectionsToDraw.add(drawable);
		}
		for (IGameComponent drawable : collectionsToDraw) {
			if (drawable instanceof IDrawable) {
				IDrawable comp = (IDrawable) drawable;
				comp.draw(batch, gameTime);
			}
		}
	}

	void load() {
		isClear = false;
		for (IGameComponent comp : collections) {
			comp.initialize();
		}
	}

	void update(GameTime gameTime) {
		if (isClear) {
			return;
		}
		if (collectionsToUpdate.size() > 0) {
			collectionsToUpdate.clear();
		}
		for (IGameComponent drawable : collections) {
			collectionsToUpdate.add(drawable);
		}

		IGameComponent drawable;
		int screenIndex;
		for (; collectionsToUpdate.size() > 0;) {

			screenIndex = collectionsToUpdate.size() - 1;
			drawable = collectionsToUpdate.get(screenIndex);

			collectionsToUpdate.remove(screenIndex);

			if (drawable instanceof IUpdateable) {
				IUpdateable comp = (IUpdateable) drawable;
				comp.update(gameTime);
			}
		}

	}

	private boolean isClear;

	public void clear() {
		if (!isClear) {
			synchronized (GameComponentCollection.class) {
				collections.clear();
				collectionsToUpdate.clear();
				collectionsToDraw.clear();
				isClear = true;
			}
		}
	}

	public boolean add(IGameComponent gc) {
		if (isClear) {
			return false;
		}
		gc.initialize();
		boolean result = collections.add(gc);
		if (gc != null && Added != null) {
			Added.invoke(gc);
		}
		if (gc instanceof DrawableGameComponent) {
			if (((DrawableGameComponent) gc).getDrawOrder() != 0) {
				sortDraw();
			}
		}
		return result;
	}

	public boolean add(IGameComponent gc, int index) {
		if (isClear) {
			return false;
		}
		gc.initialize();
		boolean result = collections.add(gc);
		for (int i = 0; i < collections.size(); i++) {
			if (collections.get(i) instanceof DrawableGameComponent) {
				if (i == index) {
					((DrawableGameComponent) collections.get(i))
							.setEnabled(true);
				} else {
					((DrawableGameComponent) collections.get(i))
							.setEnabled(false);
				}
			}
		}
		if (gc != null && Added != null) {
			Added.invoke(gc);
		}
		if (gc instanceof DrawableGameComponent) {
			if (((DrawableGameComponent) gc).getDrawOrder() != 0) {
				sortDraw();
			}
		}
		return result;
	}

	public boolean remove(IGameComponent gc) {
		if (isClear) {
			return false;
		}
		boolean result = false;
		if (gc != null && gc instanceof DrawableGameComponent) {
			DrawableGameComponent comp = (DrawableGameComponent) gc;
			comp.unloadContent();
			result = collections.remove(gc);
			collectionsToUpdate.remove(gc);
			if (Removed != null) {
				Removed.invoke(gc);
			}
		}
		return result;
	}

	public boolean removeAt(int idx) {
		if (isClear) {
			return false;
		}
		IGameComponent comp = collections.remove(idx);
		boolean result = (comp != null);
		if (result) {
			if (comp instanceof DrawableGameComponent) {
				((DrawableGameComponent) comp).unloadContent();
			}
			collectionsToUpdate.remove(comp);
			if (Removed != null) {
				Removed.invoke(comp);
			}
		}
		return result;
	}

	private ComponentEvent Added;

	private ComponentEvent Removed;

	public ComponentEvent getAdded() {
		return Added;
	}

	public void setAdded(ComponentEvent added) {
		Added = added;
	}

	public ComponentEvent getRemoved() {
		return Removed;
	}

	public void setRemoved(ComponentEvent removed) {
		Removed = removed;
	}
}
