package loon.action.scripting;

import java.util.HashMap;
import java.util.LinkedList;

import loon.LTouch;
import loon.action.map.AStarFindHeuristic;
import loon.action.map.Config;
import loon.action.map.Field2D;
import loon.action.scripting.pack.PackAnimation;
import loon.action.scripting.pack.PackSprite;
import loon.action.scripting.pack.PackSprites;
import loon.action.scripting.pack.PackTile;
import loon.action.scripting.pack.PackTileFactory;
import loon.action.scripting.pack.PackTileMap;
import loon.action.scripting.pack.PackView;
import loon.core.geom.Vector2f;
import loon.core.graphics.Screen;
import loon.core.graphics.opengl.GLEx;
import loon.core.graphics.opengl.LTexturePack;
import loon.core.timer.LTimer;
import loon.core.timer.LTimerContext;


/**
 * Copyright 2008 - 2011
 * 
 * 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 loonframework
 * @author chenpeng
 * @email：ceponline@yahoo.com.cn
 * @version 0.1
 */
public abstract class ScriptScreen extends Screen {

	final class Go {

		LTimer timer = new LTimer(0);

		float speed = 1.5f;

		int direction;

		boolean isComplete;

		PackSprite sprite;

		LinkedList<Vector2f> findPath;

		float startX, startY, endX, endY, moveX, moveY;

		public boolean isComplete() {
			return findPath == null || findPath.size() == 0 || isComplete;
		}

		public int getWidth() {
			if (sprite != null) {
				return sprite.getWidth();
			}
			return 0;
		}

		public int getHeight() {
			if (sprite != null) {
				return sprite.getHeight();
			}
			return 0;
		}

		public void update() {
			if (sprite != null) {
				sprite.setLocation(startX, startY);
				sprite.setDirection(direction);
			}
		}

	}

	private HashMap<PackSprite, Go> paths;

	private String packName, mapName;

	private String scriptFile;

	private ScriptFactory scripts;

	private PackSprites packSprites;

	private Callback callback;

	private PackView view;

	private LTexturePack tempPack;

	private PackTileMap tempMap;

	private boolean isPack, isMap;

	private PackTileFactory tileFactory;

	public ScriptScreen(String res) {
		if (res == null) {
			throw new RuntimeException("script name is null !");
		}
		this.scriptFile = res;
	}

	@Override
	public void onCreate(int width, int height) {
		super.onCreate(width, height);
		this.isPack = false;
		this.isMap = false;
		if (paths != null) {
			paths.clear();
			paths = null;
		}
		this.paths = new HashMap<PackSprite, Go>(10);
		if (packSprites != null) {
			packSprites.clear();
			packSprites = null;
		}
		this.packSprites = new PackSprites();
		if (scripts != null) {
			scripts.clear();
			scripts = null;
		}
		this.scripts = new ScriptFactory(this, scriptFile);
	}

	public Callback getCallback() {
		return callback;
	}

	public void setCallback(Callback callback) {
		this.callback = callback;
		if (scripts != null) {
			scripts.setCallback(callback);
		}
	}

	public Callback onCallback() {
		return null;
	}

	@Override
	public final void onLoad() {
		if (scripts != null) {
			scripts.load();
			packName = scripts.getPackName();
			mapName = scripts.getMapName();
			onLoading();
		}
	}

	public Go getGo(PackSprite sprite) {
		Go go = paths.get(sprite);
		if (go != null) {
			return go;
		}
		return null;
	}

	public void stopMove(PackSprite sprite) {
		Go go = paths.get(sprite);
		if (go != null) {
			go.isComplete = true;
		}
	}

	public void updateMoveSpeed(PackSprite sprite, float speed) {
		Go go = paths.get(sprite);
		if (go != null) {
			go.speed = speed;
		}
	}

	public void updateMoveDelay(PackSprite sprite, long delay) {
		Go go = paths.get(sprite);
		if (go != null) {
			go.timer.setDelay(delay);
		}
	}

	public void toMove(PackSprite sprite, AStarFindHeuristic heuristic,
			LTouch touch) {
		toMove(sprite, heuristic, touch.x(), touch.y(), true);
	}

	public void toMove(PackSprite sprite, AStarFindHeuristic heuristic,
			LTouch touch, boolean flag) {
		toMove(sprite, heuristic, touch.x(), touch.y(), flag);
	}

	public void toMove(PackSprite sprite, AStarFindHeuristic heuristic, int x,
			int y) {
		toMove(sprite, heuristic, x, y, true);
	}

	public void toMove(PackSprite sprite, AStarFindHeuristic heuristic, int x,
			int y, boolean flag) {
		toMove(sprite, heuristic, 0, 2f, x, y, flag);
	}

	public void toMove(PackSprite sprite, AStarFindHeuristic heuristic,
			LTouch touch, float speed) {
		toMove(sprite, heuristic, 0, speed, touch.x(), touch.y(), true);
	}

	public void toMove(PackSprite sprite, AStarFindHeuristic heuristic,
			long delay, float speed, LTouch touch) {
		toMove(sprite, heuristic, delay, speed, touch.x(), touch.y(), true);
	}

	public void toMove(PackSprite sprite, AStarFindHeuristic heuristic,
			long delay, float speed, LTouch touch, boolean flag) {
		toMove(sprite, heuristic, delay, speed, touch.x(), touch.y(), flag);
	}

	public void toMove(PackSprite sprite, AStarFindHeuristic heuristic,
			long delay, float speed, int x, int y) {
		toMove(sprite, heuristic, delay, speed, x, y, true);
	}

	public void toMove(PackSprite sprite, AStarFindHeuristic heuristic,
			long delay, float speed, int x, int y, boolean flag) {
		toMove(findAStar(heuristic, sprite.x(), sprite.y(), x, y, flag),
				sprite, delay, speed);
	}

	public void toMove(LinkedList<Vector2f> path, PackSprite sprite,
			long delay, float speed) {
		synchronized (ScriptScreen.class) {
			Go go = paths.get(sprite);
			if (go == null) {
				go = new Go();
				paths.put(sprite, go);
			}
			if (go.findPath != null) {
				go.findPath.clear();
			}
			go.findPath = path;
			go.sprite = sprite;
			go.timer.setDelay(delay);
			go.speed = speed;
		}
	}

	public LinkedList<Vector2f> findAStar(AStarFindHeuristic heuristic, int x1,
			int y1, LTouch touch) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.findAStar(heuristic, x1, y1, touch);
		}
		return null;
	}

	public LinkedList<Vector2f> findAStar(AStarFindHeuristic heuristic, int x1,
			int y1, boolean flag, LTouch touch) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.findAStar(heuristic, x1, y1, flag, touch);
		}
		return null;
	}

	public LinkedList<Vector2f> findAStar(AStarFindHeuristic heuristic, int x1,
			int y1, int x2, int y2) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.findAStar(heuristic, x1, y1, x2, y2);
		}
		return null;
	}

	public LinkedList<Vector2f> findAStar(AStarFindHeuristic heuristic, int x1,
			int y1, int x2, int y2, boolean flag) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.findAStar(heuristic, x1, y1, x2, y2, flag);
		}
		return null;
	}

	public void setLimit(int[] list) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			map.limit(list);
		}
	}

	public int[] getLimit() {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.getLimit();
		}
		return null;
	}

	public void subMap(int x, int y, int w, int h) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			map.sub(x, y, w, h);
		}
	}

	public void subMap(int x, int y) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			map.sub(x, y);
		}
	}

	public void setMapPos(int x, int y) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			map.setPos(x, y);
		}
	}

	public PackSprite addPackSprite(String name, long delay, float x, float y) {
		return addPackSprite(name, getAnimation(name), delay, x, y);
	}

	public PackSprite addPackSprite(String name, float x, float y) {
		return addPackSprite(name, getAnimation(name), 150, x, y);
	}

	public PackSprite addPackSprite(String name, PackAnimation animation,
			float x, float y) {
		return addPackSprite(name, animation, 150, x, y);
	}

	public PackSprite addPackSprite(String name, PackAnimation animation,
			long delay, float x, float y) {
		if (packSprites != null) {
			if (animation != null) {
				PackSprite sprite = new PackSprite(animation);
				packSprites.add(sprite, x, y);
				sprite.setName(name);
				sprite.setDelay(delay);
				return sprite;
			} else {
				return null;
			}
		}
		return null;
	}

	public void removePackSprite(PackSprite sprite) {
		if (packSprites != null) {
			packSprites.remove(sprite);
		}
	}

	public PackSprite findPackSprite(String name) {
		return packSprites.find(name);
	}

	public void setMapBlockSize(int tileWidth, int tileHeight) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			map.setBlockSize(tileWidth, tileHeight);
		}
	}

	public int getMapWidth() {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.getWidth();
		}
		return 0;
	}

	public int getMapHeight() {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.getHeight();
		}
		return 0;
	}

	public PackTile getTile(int x, int y) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.getTile(x, y);
		}
		return null;
	}

	public void setTile(int x, int y, PackTile type) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			map.setTile(x, y, type);
		}
	}

	public boolean collidesWith(PackSprite sprite) {
		return !canMove(sprite, sprite.getX(), sprite.getY());
	}

	public int count(PackTile element) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.count(element);
		}
		return 0;
	}

	public boolean canMove(PackSprite sprite, float x, float y) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.canMove(sprite, x, y);
		}
		return false;
	}

	public void touch(PackSprite sprite) {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			map.touch(sprite);
		}
	}

	public int getTileWidth() {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.getTileWidth();
		}
		return 0;
	}

	public int getTileHeight() {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.getTileHeight();
		}
		return 0;
	}

	public Field2D getField2D() {
		PackTileMap map = getMap(mapName);
		if (map != null) {
			return map.getField2D();
		}
		return null;
	}

	public abstract void onLoading();

	@Override
	public final void onLoaded() {
		if (callback == null) {
			callback = onCallback();
		}
		if (scripts != null) {
			scripts.setCallback(callback);
		}
		this.setView(null);
		this.setPackName(packName);
		this.setMapName(mapName);
	}

	public void callScript(String name) {
		if (scripts != null) {
			if (packSprites != null) {
				packSprites.clear();
			}
			scripts.clear();
			scripts.call(name);
		}
	}

	public void clearScript() {
		if (scripts != null) {
			scripts.clear();
		}
	}

	public void clearSprite() {
		if (packSprites != null) {
			packSprites.clear();
		}
	}

	public void setView(PackView view) {
		if (view == null) {
			view = PackView.EmptyView.getInstance();
		}
		if (packSprites != null) {
			packSprites.setView(view);
		}
		PackTileMap map = scripts.getMap(mapName, tileFactory);
		if (map != null) {
			map.setView(view);
		}
		this.view = view;
	}

	public PackView getView() {
		return view;
	}

	public String getMapName() {
		return mapName;
	}

	public void setMapName(String name) {
		this.mapName = name;
		if (name != null && scripts != null) {
			this.scripts.setMapName(name);
			this.mapName = name;
			this.tempMap = scripts.getMap(mapName, tileFactory);
			if (tempMap != null) {
				isMap = true;
			} else {
				isMap = false;
			}
		}
	}

	public void setPackName(String name) {
		if (name != null && scripts != null) {
			this.scripts.setPackName(name);
			this.packName = name;
			this.tempPack = scripts.getPack(packName);
			if (tempPack != null) {
				isPack = true;
			} else {
				isPack = false;
			}
		}
	}

	public String getPackName() {
		return this.packName;
	}

	public PackAnimation getAnimation(String name) {
		return scripts.getAnimation(name);
	}

	public LTexturePack getPack(String name) {
		return scripts.getPack(name);
	}

	public PackTileMap getMap() {
		return scripts.getMap(mapName);
	}

	public PackTileMap getMap(String name) {
		return scripts.getMap(name, tileFactory);
	}

	public Script getScript(String name) {
		return scripts.getScript(name);
	}

	private void going() {
		if (!isMap) {
			return;
		}
		if (paths.size() > 0) {
			Field2D field2D = getField2D();
			if (field2D == null) {
				return;
			}
			synchronized (paths) {
				for (Go go : paths.values()) {
					if (go == null || go.findPath == null) {
						continue;
					}
					if (go.isComplete()) {
						paths.remove(go.sprite);
						return;
					}
					if (go.timer.action(elapsedTime)) {
						synchronized (go.findPath) {
							if (go.endX == go.startX && go.endY == go.startY) {
								if (go.findPath != null) {
									if (go.findPath.size() > 1) {
										Vector2f moveStart = go.findPath
												.get(0);
										Vector2f moveEnd = go.findPath
												.get(1);
										go.startX = field2D
												.tilesToWidthPixels(moveStart
														.x());
										go.startY = field2D
												.tilesToHeightPixels(moveStart
														.y());
										go.endX = moveEnd.x()
												* field2D.getTileWidth();
										go.endY = moveEnd.y()
												* field2D.getTileHeight();
										go.moveX = moveEnd.x() - moveStart.x();
										go.moveY = moveEnd.y() - moveStart.y();
										go.direction = Field2D.getDirection(
												(int) go.moveX, (int) go.moveY);
										go.findPath.remove(0);
									} else {
										go.findPath.clear();
									}
								}
							}
							switch (go.direction) {
							case Config.TUP:
								go.startY -= go.speed;
								if (go.startY < go.endY) {
									go.startY = go.endY;
								}
								break;
							case Config.TDOWN:
								go.startY += go.speed;
								if (go.startY > go.endY) {
									go.startY = go.endY;
								}
								break;
							case Config.TLEFT:
								go.startX -= go.speed;
								if (go.startX < go.endX) {
									go.startX = go.endX;
								}
								break;
							case Config.TRIGHT:
								go.startX += go.speed;
								if (go.startX > go.endX) {
									go.startX = go.endX;
								}
								break;
							case Config.UP:
								go.startX += go.speed;
								go.startY -= go.speed;
								if (go.startX > go.endX) {
									go.startX = go.endX;
								}
								if (go.startY < go.endY) {
									go.startY = go.endY;
								}
								break;
							case Config.DOWN:
								go.startX -= go.speed;
								go.startY += go.speed;
								if (go.startX < go.endX) {
									go.startX = go.endX;
								}
								if (go.startY > go.endY) {
									go.startY = go.endY;
								}
								break;
							case Config.LEFT:
								go.startX -= go.speed;
								go.startY -= go.speed;
								if (go.startX < go.endX) {
									go.startX = go.endX;
								}
								if (go.startY < go.endY) {
									go.startY = go.endY;
								}
								break;
							case Config.RIGHT:
								go.startX += go.speed;
								go.startY += go.speed;
								if (go.startX > go.endX) {
									go.startX = go.endX;
								}
								if (go.startY > go.endY) {
									go.startY = go.endY;
								}
								break;
							}
							go.update();
						}
					}
				}
			}
		}
	}

	@Override
	public final void alter(LTimerContext timer) {
		if (isOnLoadComplete()) {
			going();
			scripts.update();
			update(timer.getTimeSinceLastUpdate());
		}
	}

	public abstract void update(long elapsedTime);

	@Override
	public final void draw(GLEx g) {
		if (isOnLoadComplete()) {
			if (isPack) {
				if (isMap) {
					if (!tempMap.drawSub(tempPack)) {
						packSprites.draw(tempPack, elapsedTime);
						return;
					}
				}
				if (packSprites.size() > 0) {
					tempPack.glBegin();
					packSprites.draw(tempPack, elapsedTime);
					tempPack.glEnd();
				}
			}
			paint(g);
		}
	}

	public abstract void paint(GLEx g);

	public String getScriptFile() {
		return scriptFile;
	}

	public void setScriptFile(String script) {
		this.scriptFile = script;
	}

	public PackTileFactory getTileFactory() {
		return tileFactory;
	}

	public void setTileFactory(PackTileFactory tileFactory) {
		this.tileFactory = tileFactory;
		PackTileMap map = getMap(mapName);
		if (map != null) {
			PackTileFactory factory = map.getTileFactory();
			if (factory != null && !"simple".equals(factory.getName())) {
				map.setTileFactory(tileFactory);
			}
		}
	}

	public boolean isMap() {
		return isMap;
	}

	public boolean isPack() {
		return isPack;
	}

	public PackSprites getPackSprites() {
		return packSprites;
	}

	public ScriptFactory getScriptFactory() {
		return scripts;
	}

	@Override
	public void dispose() {
		this.isPack = false;
		this.isMap = false;
		if (paths != null) {
			paths.clear();
			paths = null;
		}
		if (scripts != null) {
			scripts.dispose();
			scripts = null;
		}
		if (packSprites != null) {
			packSprites.clear();
			packSprites = null;
		}
		if (paths != null) {
			paths.clear();
			paths = null;
		}
	}

}
