/**
 * <p>
 * Copyright © 2010, Bruce-Robert Pocock &amp; Edward Winkelman &amp;
 * Res Interactive, LLC
 * </p>
 * <p>
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * </p>
 * <p>
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 * </p>
 * <p>
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <a
 * href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.
 * </p>
 * 
 * @author brpocock@star-hope.org
 * @author edward.winkelman@gmail.com
 */
package com.tootsville.game;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.json.JSONObject;
import org.starhope.appius.except.GameLogicException;
import org.starhope.appius.except.NotFoundException;
import org.starhope.appius.except.UserDeadException;
import org.starhope.appius.game.AppiusClaudiusCaecus;
import org.starhope.appius.game.GameEvent;
import org.starhope.appius.game.GameStateFlag;
import org.starhope.appius.game.Zone;
import org.starhope.appius.geometry.Coord3D;
import org.starhope.appius.geometry.TriangleMesh;
import org.starhope.appius.physica.Geometry;
import org.starhope.appius.physica.Kalendor;
import org.starhope.appius.room.Room;
import org.starhope.appius.user.AbstractUser;
import org.starhope.appius.util.AppiusConfig;

/**
 * @author ewinkelman
 */
public class ShadowTree extends GameEvent {

	/**
	 * Special area on the branch to cause it to break
	 */
	private transient final TriangleMesh branchArea;

	/**
	 * If the branch is in the down position
	 */
	private transient int branchState = 1;

	/**
	 * Special area for when the branch is broken
	 */
	private transient final TriangleMesh brokenBranchArea;

	/**
	 * Tree branch hit points
	 */
	private transient double hitpoints = 100;

	/**
	 * Landing area for falling toots
	 */
	private transient final TriangleMesh landing;

	/**
	 * The last time that the branch was shaken
	 */
	private transient long lastShake = System.currentTimeMillis ();

	/**
	 * Soccer field room object
	 */
	private transient final Room room;

	/**
	 * User hits so that one user can't just spam jump
	 */
	private transient final ConcurrentHashMap <AbstractUser, Double> userHits = new ConcurrentHashMap <AbstractUser, Double> ();

	/**
	 * WRITEME brpocock@star-hope.org Sep 28, 2010
	 * 
	 * @param z Zone in which tree is placed
	 * @throws NotFoundException WRITEME
	 * @throws GameLogicException WRITEME
	 */
	public ShadowTree (final Zone z) throws NotFoundException,
	GameLogicException {
		super (z, 'S');

		gameState = GameStateFlag.GAME_SOLO;
		room = z.getRoom ("shadowLand1");
		if (null == room) {
			AppiusClaudiusCaecus
			.reportBug ("can't find the shadow tree");
			throw new GameLogicException ("Can't find the shadow tree",
					this, z);
		}
		rooms.add (room);
		room.add (this);

		branchArea = Geometry.stringToNewTriangleMesh (room
				.getPlaceStringByName ("evt_$branch"));
		brokenBranchArea = Geometry.stringToNewTriangleMesh (room
				.getPlaceStringByName ("evt_$branchBroken"));
		landing = Geometry.stringToNewTriangleMesh (
				room.getPlaceStringByName ("evt_$landing"))
				;
		room.setVariable ("zoneBranch", "obs_:"
				+ brokenBranchArea.toRoomVar ());

		AppiusClaudiusCaecus.add (this);
	}

	/**
	 * @see org.starhope.appius.room.RoomListener#acceptGameAction(org.starhope.appius.user.AbstractUser,
	 *      org.json.JSONObject)
	 */
	@Override
	public void acceptGameAction (final AbstractUser u,
			final JSONObject action) {
		// No op

	}

	/**
	 * @see org.starhope.appius.room.RoomListener#acceptOutOfBandMessage(org.starhope.appius.user.AbstractUser,
	 *      org.starhope.appius.room.Room, org.json.JSONObject)
	 */
	@Override
	public void acceptOutOfBandMessage (final AbstractUser sender,
			final Room room, final JSONObject body) {
		// No op

	}

	/**
	 * @see org.starhope.appius.room.RoomListener#acceptPublicMessage(org.starhope.appius.user.AbstractUser,
	 *      java.lang.String)
	 */
	@Override
	public void acceptPublicMessage (final AbstractUser from,
			final String message) {
		// No op

	}

	/**
	 * @see org.starhope.appius.room.RoomListener#acceptUserAction(org.starhope.appius.room.Room,
	 *      org.starhope.appius.user.AbstractUser)
	 */
	@Override
	public void acceptUserAction (final Room r, final AbstractUser u) {
		final boolean onArea = branchArea.contains (u.getLocation ()
		);
		if ("Jumping".equals (u.getCurrentAction ()) && onArea) {
			shakeLimb ();
			final double userHit = userHits.containsKey (u) ? userHits
					.get (u).doubleValue () / 2d : 10d;
					userHits.put (u, Double.valueOf (userHit));
					hitpoints -= userHit;
					if (hitpoints <= 0 && branchState != 2) {
						branchState = 2;
						room.setVariable ("anim~treeBranch", "2");
						room.setVariable ("zoneBranch", "obs_:"
								+ branchArea.toRoomVar ());
						final long futureNow = System.currentTimeMillis () + 750;
						for (final AbstractUser user : room.getAllUsers ()) {
							if (branchArea.contains (user.getLocation ()
							)) {
								final double randX = landing.getWidth ()
								* AppiusConfig.rnd.nextDouble ()
								+ landing.getOrigin ().getX ();
								final double randY = landing.getHeight ()
								* AppiusConfig.rnd.nextDouble ()
								+ landing.getOrigin ().getY ();
						Kalendor.getGlobal ().schedule (
										futureNow, new Runnable () {
											@Override
											public void run () {
												getRoom ().putHere (
														user,
														new Coord3D (randX,
																randY, 0));
											}
										});
							}
						}
					}
		}
	}

	/**
	 * @see org.starhope.appius.game.GameEvent#destroySelf()
	 */
	@Override
	public void destroySelf () {
		super.destroySelf ();
		AppiusClaudiusCaecus.remove (this);
	}

	/**
	 * @see org.starhope.appius.game.GameEvent#getGameEventPrefix()
	 */
	@Override
	public String getGameEventPrefix () {
		return "shadowTree";
	}

	/**
	 * @see org.starhope.appius.util.HasName#getName()
	 */
	@Override
	public String getName () {
		return "Shadow Tree in " + rooms.first ().getZone ().getName ();
	}

	/**
	 * Shakes the limb every two seconds max
	 */
	private void shakeLimb () {
		if (branchState != 2
				&& System.currentTimeMillis () > lastShake + 2000) {
			lastShake = System.currentTimeMillis ();
			branchState = branchState == 1 ? 3 : 1;
			room.setVariable ("anim~treeBranch", Integer
					.toString (branchState));
		}
	}

	/**
	 * @see org.starhope.appius.game.GameEvent#tick(long, long)
	 */
	@Override
	public void tick (final long currentTime, final long deltaTime)
	throws UserDeadException {
		final double regen = Math.max ( (100d - hitpoints) / 20d, 0.1d)
		* deltaTime / 1000d;

		if (regen + hitpoints >= 100 && hitpoints != 100
				&& branchState == 2) {
			branchState = 1;
			room.setVariable ("anim~treeBranch", "1");
			room.setVariable ("zoneBranch", "obs_:"
					+ brokenBranchArea.toRoomVar ());
			for (final AbstractUser user : room.getAllUsers ()) {
				if (brokenBranchArea.contains (user.getLocation ()
						.toCoord2D ())) {
					final double randX = landing.getWidth ()
					* AppiusConfig.rnd.nextDouble ()
					+ landing.getOrigin ().getX ();
					final double randY = landing.getHeight ()
					* AppiusConfig.rnd.nextDouble ()
					+ landing.getOrigin ().getY ();
					room.putHere (user, new Coord3D (randX, randY, 0));
				}
			}
		}
		hitpoints = Math.min (100, hitpoints + regen);

		for (final Map.Entry <AbstractUser, Double> entry : userHits
				.entrySet ()) {
			final Double hit = Double.valueOf (entry.getValue ()
					.intValue ()
					+ deltaTime / 1000d);
			if (hit.doubleValue () >= 20) {
				userHits.remove (entry.getKey ());
			} else {
				entry.setValue (hit);
			}

		}
	}

}
