/**
 * <p>
 * Copyright © 2009-2010, Bruce-Robert Pocock
 * </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
 */
package com.tootsville.npc;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import org.json.JSONObject;
import org.starhope.appius.except.GameLogicException;
import org.starhope.appius.except.NotFoundException;
import org.starhope.appius.except.PrivilegeRequiredException;
import org.starhope.appius.except.UserDeadException;
import org.starhope.appius.game.AppiusClaudiusCaecus;
import org.starhope.appius.game.Zone;
import org.starhope.appius.game.inventory.Inventory;
import org.starhope.appius.physica.Geometry;
import org.starhope.appius.room.Room;
import org.starhope.appius.room.RoomListener;
import org.starhope.appius.user.AbstractNonPlayerCharacter;
import org.starhope.appius.user.AbstractUser;
import org.starhope.appius.user.GeneralUser;
import org.starhope.appius.util.AppiusConfig;
import org.starhope.util.LibMisc;

/**
 * <p>
 * Short-order cook for TootsBurger as a cheap Diner Dash clone.
 * </p>
 * <h2>The TootsBurger Game</h2>
 * <p>
 * The player triggers this game by putting on their apron.
 * </p>
 * <p>
 * The way this game works, is that the player walks up to a
 * {@link ChatterBot}, who will find their way to a seat (cleverly
 * tagged, one hopes, by a “$seat” space in the room) and demand
 * attention. The player will (one hopes) heed their calls and walk
 * over. When they approach one of the impatient pachyderms, they'll get
 * a demand for some food item or other. The helpful player will then
 * walk over to the {@link ShortOrderCook} and repeat the order. If,
 * instead, they walk over to the {@link DinerDishWasher}, he will
 * helpfully fill them in on the rules (at a whisper, no less!).
 * </p>
 * <p>
 * Once the intrepid server has repeated the order, s/he can idle for a
 * bit while the cook prepares the food. After a moment, the cook will
 * call back the order, and the player has a window of opportunity to
 * pick up the food from the cook.
 * </p>
 * <p>
 * Picking up the food on time costs the player a few peanuts (depending
 * on the order). The cook will inform them of that cost, but gives them
 * the food. They now have a limited time to run the food out to the
 * patient patrons, who will cheerfully announce their pleasure and
 * promptly pay their food, with a gratuity based upon the speed of
 * delivery. Note that patrons count time from the time that they
 * ordered, not when the food was ready, so a 15%-35% tip can result
 * depending on the player's alacrity.
 * </p>
 * <p>
 * Failure to pick up the order in time will cost the player — they'll
 * raise the ire of the cook, who will charge them for the spoiled food,
 * as well as the customers, who will ask for a different server, or
 * leave the diner altogether.
 * </p>
 * <h2>How The Cook Works</h2>
 * <ul>
 * <li>If the player walks up to the cook and does not have an apron,
 * the cook will ask if s/he would like a job, and explain the rules in
 * brief (in a whisper): <blockquote> “Hi, there, (PLAYER). I'm (COOK).
 * I'm the cook here at TootsBurger. Would you like to help us out by
 * waiting tables? If you have a TootsBurger apron, you can help me out.
 * Just find out what customers want, and tell me. I'll cook the order,
 * and you pay me for it. When it's ready, you can bring it to your
 * customers, and they'll pay you, and give you a tip. You can earn
 * money by bringing me their orders and bringing them their food as
 * fast as possible.” </blockquote></li>
 * <li>If the player just stands there, the cook will repeat:
 * <blockquote> “Would you like to wait tables? Just say ‘Apron’ and
 * I'll sell you one.” </blockquote></li>
 * <li>The player can then choose to buy the apron for 25 peanuts from
 * the cook by saying “apron” in their proximity. <blockquote> “Here's
 * your apron. You can get started right away! Good luck!” </blockquote>
 * </li>
 * <li>Once a {@link ChatterBot} places an order, the ShortOrderCook
 * “knows” it. The player comes up and is supposed to repeat (at least
 * the significant keyword(s) of) the order.</li>
 * <li>The chef will wait a short time (cooking the food, presumably)
 * and then announce: “Order up, (PLAYER). (FOOD)”.</li>
 * </ul>
 *
 * @author BRPocock@Star-Hope.org
 */
public class ShortOrderCook extends AbstractNonPlayerCharacter {

	/**
	 * <p>
	 * The names of some of my (BRP) personal favourite short-order
	 * diner cooks.
	 * </p>
	 */
	private static String [] cooks = { "Mrs.Paul", "Jo.An",
		"Sweet.Sage", "Big.Mary", "Bernie", "Gretchen.Findle" };

	/**
	 * <p>
	 * Provide an unique ID counter for multiple instances of the same
	 * cook character in different Zones at the same time.
	 * </p>
	 */
	private static AtomicInteger nextID = new AtomicInteger (0);

	/**
	 * WRITEME: Document this brpocock@star-hope.org
	 */
	private static final long serialVersionUID = 6712696516747032143L;

	/**
	 * The apron which has to be worn to be an employee.
	 */
	private final int apronItem = AppiusConfig.getIntOrDefault (
			"com.tootsville.tootsBurger.apronItem", 1902);

	/**
	 * List of foods. Accessed by DinerDiner also. Names match emotes.
	 */
	public final String [] foods = { "hotdog", "soda", "fries",
			"hamburger", "cupcake" };

	/**
	 * <p>
	 * Create a short-order cook. The cook's name is pseudo-randomly
	 * taken from a pool of available names, on the principle that there
	 * should definitely be more than one chef at a fine 24-hour diner
	 * like TootsBurger. The names of the characters are homages to some
	 * of my personal favourite 24-hour diner short-order cooks. (BRP)
	 * </p>
	 *
	 * @param diner the TootsBurger room
	 * @throws NotFoundException WRITEME
	 * @throws GameLogicException WRITEME
	 */
	public ShortOrderCook (final Room diner) throws NotFoundException,
	GameLogicException {
		super (ShortOrderCook.cooks [AppiusConfig.getRandomInt (0,
				ShortOrderCook.cooks.length)]);
		final Inventory inv = getInventory ();
		if ( !inv.hasItem (apronItem)) {
			inv.add (apronItem);
		}
		inv.findItem (apronItem).setActive (true);
		diner.join (this, "tootSquare");
		diner.goTo (this, "$cookStation", "Walk");
		final long now = System.currentTimeMillis ();
		final long arrival = now + Geometry.getTimeToTarget (this, now);
		AppiusClaudiusCaecus.getKalendor ().schedule (arrival,
				new Runnable () {
			@Override
			public void run () {
				setFacing ("S");
				final String txt = LibMisc.getTextOrDefault (
						"ShortOrderCook.startShift",
				"OK, ready to cook something!");
				speak (getRoom (), txt);
			}
		});

	}

	/**
	 * @see org.starhope.appius.room.RoomListener#acceptObjectJoinRoom(org.starhope.appius.room.Room,
	 *      org.starhope.appius.room.RoomListener)
	 */
	@Override
	public void acceptObjectJoinRoom (final Room room,
			final RoomListener object) {
		if (object instanceof AbstractNonPlayerCharacter) {
			if (getWaiters ().size () > 0) return;
			speakCasually ("welcomeUser.seatYourself");
			((AbstractNonPlayerCharacter) object).acceptPrivateMessage (
					this, "$seatYourself");
			return;
		}
		if (object instanceof GeneralUser) {
			if (getWaiters ().size () > 0) return;
			speakCasually ("welcomeUser.seatYourself");
			speakCasually ("inviteWaiter");
			return;
		}
	}

	/**
	 * @see org.starhope.appius.room.RoomListener#acceptObjectPartRoom(org.starhope.appius.room.Room,
	 *      org.starhope.appius.room.RoomListener)
	 */
	@Override
	public void acceptObjectPartRoom (final Room room,
			final RoomListener thing) {
		// 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) {
		// TODO Auto-generated method stub brpocock@star-hope.org
		AppiusClaudiusCaecus
		.reportBug ("unimplemented RoomListener::acceptOutOfBandMessage (brpocock@star-hope.org, Jul 7, 2010)");

	}

	/**
	 * @see org.starhope.appius.room.RoomListener#acceptPublicMessage(org.starhope.appius.user.AbstractUser,
	 *      org.starhope.appius.room.Room, java.lang.String)
	 */
	@Override
	public void acceptPublicMessage (final AbstractUser sender,
			final Room room, final String message) {
		// TODO Auto-generated method stub brpocock@star-hope.org
		AppiusClaudiusCaecus
		.reportBug ("unimplemented RoomListener::acceptPublicMessage (brpocock@star-hope.org, Jul 7, 2010)");

	}

	/**
	 * @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) {
		// TODO Auto-generated method stub brpocock@star-hope.org
		AppiusClaudiusCaecus
		.reportBug ("unimplemented RoomListener::acceptPublicMessage (brpocock@star-hope.org, Jul 7, 2010)");

	}

	/**
	 * @see org.starhope.appius.room.RoomListener#acceptUserVariableUpdate(org.starhope.appius.user.AbstractUser,
	 *      java.lang.String, java.lang.String)
	 */
	@Override
	public void acceptUserVariableUpdate (final AbstractUser user,
			final String varName, final String varValue) {
		// TODO Auto-generated method stub brpocock@star-hope.org
		AppiusClaudiusCaecus
		.reportBug ("unimplemented RoomListener::acceptUserVariableUpdate (brpocock@star-hope.org, Jul 7, 2010)");

	}

	/**
	 * @see org.starhope.appius.user.AbstractUser#ban(org.starhope.appius.user.AbstractUser,
	 *      java.lang.String)
	 */
	@Override
	public void ban (final AbstractUser u, final String banReason)
	throws PrivilegeRequiredException {
		// TODO Auto-generated method stub brpocock@star-hope.org
		AppiusClaudiusCaecus
		.reportBug ("unimplemented AbstractUser::ban (brpocock@star-hope.org, Jul 7, 2010)");

	}

	/**
	 * @see org.starhope.appius.user.AbstractNonPlayerCharacter#getInstanceID()
	 */
	@Override
	protected int getInstanceID () {
		return ShortOrderCook.nextID.incrementAndGet ();
	}

	/**
	 *
	 * @return The number of waiters in the room
	 */
	private Set <AbstractUser> getWaiters () {
		final Set <AbstractUser> waiters = new HashSet<AbstractUser> ();
		if (null == getRoom ()) return waiters;
		for (final AbstractUser u : getRoom ().getAllUsers ()) {
			if (u.getInventory ().hasEquipped (apronItem)) {
				waiters.add(u);
			}
		}
		return waiters;
	}

	/**
	 * @see org.starhope.appius.user.AbstractUser#sendEarnings(org.starhope.appius.room.Room,
	 *      java.lang.String)
	 */
	@Override
	public void sendEarnings (final Room room,
			final String string) {
		// TODO Auto-generated method stub brpocock@star-hope.org
		AppiusClaudiusCaecus
		.reportBug ("unimplemented AbstractUser::sendEarnings (brpocock@star-hope.org, Jul 7, 2010)");

	}

	/**
	 * @see org.starhope.appius.user.AbstractUser#sendMigrate(org.starhope.appius.types.AbstractZone)
	 */
	@Override
	public void sendMigrate (final Zone refugeeZone)
	throws UserDeadException {
		// TODO Auto-generated method stub brpocock@star-hope.org
		AppiusClaudiusCaecus
		.reportBug ("unimplemented AbstractUser::sendMigrate (brpocock@star-hope.org, Jul 7, 2010)");

	}

	/**
	 * @see org.starhope.appius.user.AbstractUser#sendWardrobe()
	 */
	@Override
	public void sendWardrobe () {
		// TODO Auto-generated method stub brpocock@star-hope.org
		AppiusClaudiusCaecus
		.reportBug ("unimplemented AbstractUser::sendWardrobe (brpocock@star-hope.org, Jul 7, 2010)");

	}

	/**
	 * @see org.starhope.appius.util.AcceptsMetronomeTicks#tick(long,
	 *      long)
	 */
	@Override
	public void tick (final long currentTime, final long deltaTime)
	throws UserDeadException {
		// TODO Auto-generated method stub brpocock@star-hope.org
		AppiusClaudiusCaecus
		.reportBug ("unimplemented AcceptsMetronomeTicks::tick (brpocock@star-hope.org, Jul 7, 2010)");

	}
}
