/**
 * Copyright © 2009-2010, Bruce-Robert Pocock & Res Interactive, LLC.
 * All Rights Reserved. Licensed for perpetual use, modification, and/or
 * distribution by either party.
 * 
 * @author brpocock@star-hope.org
 */
package com.tootsville.game;

import java.math.BigInteger;

import org.json.JSONObject;
import org.starhope.appius.except.NotFoundException;
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.game.laserTag.LaserTagGame;
import org.starhope.appius.room.Room;
import org.starhope.appius.room.RoomListener;
import org.starhope.appius.user.AbstractUser;
import org.starhope.appius.util.AppiusConfig;
import org.starhope.util.LibMisc;

/**
 * @author brpocock@star-hope.org, twheys@gmail.com, rdawson
 */
public final class ZapAttack extends LaserTagGame {

	/**
	 * The POJO serialization ID
	 */
	private static final long serialVersionUID = -3405419666711244842L;

	/**
	 * @param z the zone in which the game is being played
	 */
	public ZapAttack (final Zone z) {
		super (z, 'Z');
		gameState = GameStateFlag.GAME_SOLO;

		for (final String moniker : new String [] { "zapArena1",
				"zapArena2", "zapArena3", "zapArena4" }) {
			Room r;
			try {
				r = z.getRoom (moniker);
			} catch (NotFoundException e) {
				throw AppiusClaudiusCaecus
						.fatalBug ("Can't find room by moniker "
								+ moniker + " in zone " + z.getName ());
			}
			rooms.add (r);
		}

		try {
			scoreWatchRooms.add (z.getRoom ("zapLobby"));
		} catch (NotFoundException e) {
			throw new RuntimeException (e);
		}
		for (final Room r : rooms) {
			r.add (this);
		}
		AppiusClaudiusCaecus.add (this);
		acceptGameStateChange (this, GameStateFlag.GAME_SOLO);
	}

	/**
	 * Accept notification of game state change. Set up practice mode,
	 * if necessary; clear remaining targets upon leaving practice mode.
	 * 
	 * @param game This game, hopefully.
	 * @param newGameState The state into which we are entering.
	 */
	@Override
	public void acceptGameStateChange (final GameEvent game,
			final GameStateFlag newGameState) {
		if (newGameState == GameStateFlag.GAME_SOLO
				|| AppiusConfig
						.getConfigBoolOrFalse ("com.tootsville.game.ZapAttack.combatRocks")) {
			// establish targets for target practice
			final int left = AppiusConfig.getIntOrDefault (
					"com.tootsville.game.ZapAttack.left", 100);
			final int right = AppiusConfig.getIntOrDefault (
					"com.tootsville.game.ZapAttack.right", 700);
			final int top = AppiusConfig.getIntOrDefault (
					"com.tootsville.game.ZapAttack.top", 100);
			final int bottom = AppiusConfig.getIntOrDefault (
					"com.tootsville.game.ZapAttack.bottom", 400);
			final int rockRatio = AppiusConfig.getIntOrDefault (
					"com.tootsville.game.ZapAttack.rockRatio", 3);
			final int minTargets = AppiusConfig.getIntOrDefault (
					"com.tootsville.game.ZapAttack.minTargets", 4);
			final int maxTargets = AppiusConfig.getIntOrDefault (
					"com.tootsville.game.ZapAttack.maxTargets", 8);
			for (final Room r : getRooms ()) {
				final int numTargets = AppiusConfig.getRandomInt (
						minTargets, maxTargets);
				for (int i = 0; i < numTargets; ++i) {
					final String varExists = r.getVariable ("t~" + i);
					if (null == varExists || "".equals (varExists)) {
						final int x = AppiusConfig.getRandomInt (left,
								right);
						final int y = AppiusConfig.getRandomInt (top,
								bottom);
						final String type;
						final int subType;
						if (AppiusConfig.getRandomInt (0, rockRatio) == 0) {
							type = "itm";
							subType = AppiusConfig.getRandomInt (0, 2);
						} else {
							type = "obs";
							subType = 0;
						}

						r.setVariable ("t~" + i, type + "~" + subType
								+ "~" + x + "~" + y);
					}
				}
			}
		} else if ( !AppiusConfig
				.getConfigBoolOrFalse ("com.tootsville.game.ZapAttack.combatRocks")) {
			// eliminate any remaining targets on leaving solo mode
			for (final Room r : getRooms ()) {
				for (final String roomVarName : r.getVariables ()
						.keySet ()) {
					if (roomVarName.startsWith ("t~")) {
						r.deleteVariable (roomVarName);
					}
				}
			}
		}
		super.acceptGameStateChange (game, newGameState);
	}

	/**
	 * @see org.starhope.appius.room.RoomListener#acceptObjectJoinRoom(Room,
	 *      org.starhope.appius.room.RoomListener)
	 */
	@Override
	public void acceptObjectJoinRoom (final Room room,
			final RoomListener object) {
		super.acceptObjectJoinRoom (room, object);
		if (GameStateFlag.GAME_RUNNING == gameState) {
			sendStartEvents ("ZapAttack.game");
		}
	}

	/**
	 * @see org.starhope.appius.room.RoomListener#acceptOutOfBandMessage(AbstractUser,
	 *      Room, JSONObject)
	 */
	@Override
	public void acceptOutOfBandMessage (final AbstractUser sender,
			final Room room, final JSONObject body) {
		noOp ();
	}

	/**
	 * This is an overriding method.
	 * 
	 * @see org.starhope.appius.room.RoomListener#acceptPublicMessage(AbstractUser,
	 *      Room, String)
	 */
	@Override
	public void acceptPublicMessage (final AbstractUser sender,
			final Room room, final String message) {
		noOp ();
	}

	/**
	 * This is an overriding method.
	 * 
	 * @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) {
		noOp ();
	}

	/**
	 * @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) {
		//no op
	}

	/**
	 * This is an overriding method.
	 *
	 * @see org.starhope.appius.game.GameEvent#equals(java.lang.Object)
	 */
	@Override
	public boolean equals (final Object other) {
		if (null == other) {
			return false;
		}
		if (other instanceof ZapAttack) {
			return equals ((ZapAttack) other);
		}
		return false;
	}


	/**
	 * Determine if two instance pointers to Zap Attack are, in fact,
	 * the same instance
	 *
	 * @param other Another ZapAttack instance against which to compare
	 *            this one
	 * @return true, if the two objects refer to the same game instance
	 */
	public boolean equals (final ZapAttack other) {
		if (null == other) {
			return false;
		}
		return toString ().equals (other.toString ());
	}

	/**
	 * This is an overriding method.
	 *
	 * @see org.starhope.appius.game.laserTag.LaserTagGame#getGameEventPrefix()
	 */
	@Override
	public String getGameEventPrefix () {
		return "ZapAttack";
	}

	/**
	 * This is an overriding method.
	 *
	 * @see org.starhope.appius.util.HasName#getName()
	 */
	@Override
	public String getName () {
		if (null != rooms && null != rooms.first ()
				&& null != rooms.first ().getZone ()
				&& null != rooms.first ().getZone ().getName ()) {
			return "ZapAttack in "
					+ rooms.first ().getZone ().getName ();
		}
		return "ZapAttack (noplace)";
	}

	/**
	 * @see org.starhope.appius.game.laserTag.LaserTagGame#getScoreForObject(String,
	 *      int)
	 */
	@Override
	protected BigInteger getScoreForObject (final String type,
			final int subtype) {
		return BigInteger.valueOf (AppiusConfig.getIntOrDefault (
				"com.tootsville.games.ZapAttack." + type + "."
						+ subtype + ".score", (1 + subtype)));
	}

	/**
	 * @see org.starhope.appius.game.GameEvent#hashCode()
	 */
	@Override
	public int hashCode () {
		return LibMisc.makeHashCode (toString ());
	}

	/**
	 * Guess what this doesn't do? Everything!
	 */
	public void noOp () {
		// No op.
	}

}
