/**
 * Copyright © 2010, Res Interactive, LLC. All Rights Reserved.
 */
package com.tootsville.npc;

import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicInteger;

import org.starhope.appius.except.AlreadyExistsException;
import org.starhope.appius.except.NotFoundException;
import org.starhope.appius.game.AppiusClaudiusCaecus;
import org.starhope.appius.game.npc.Ejecta;
import org.starhope.appius.geometry.Coord2D;
import org.starhope.appius.geometry.Coord3D;
import org.starhope.appius.geometry.Polygon;
import org.starhope.appius.physica.Geometry;
import org.starhope.appius.room.Room;
import org.starhope.appius.user.AbstractUser;
import org.starhope.appius.user.User;
import org.starhope.appius.user.events.ActionHandler;
import org.starhope.appius.user.events.ActionMethod;
import org.starhope.appius.user.events.Quaestor;
import org.starhope.util.LibMisc;

/**
 * WRITEME: Document this type.
 *
 * @author brpocock@star-hope.org
 */
public class SmudgeStain {

	/**
	 * WRITEME: Document this type.
	 *
	 * @author brpocock@star-hope.org
	 */
	static final class StepOnMe implements ActionMethod {
		/**
		 * WRITEME: Document this type.
		 *
		 * @author brpocock@star-hope.org
		 */
		final static class StepOnMeMulligan implements Runnable {
			/**
			 * WRITEME: Document this brpocock@star-hope.org
			 */
			private final String indirectObject;
			/**
			 * WRITEME: Document this brpocock@star-hope.org
			 */
			private final StepOnMe stepOnMe;
			/**
			 * WRITEME: Document this brpocock@star-hope.org
			 */
			private final AbstractUser subject;
			/**
			 * WRITEME: Document this brpocock@star-hope.org
			 */
			private final Room where;
			/**
			 * WRITEME: Document this brpocock@star-hope.org
			 */
			private final String verb;
			/**
			 * WRITEME: Document this brpocock@star-hope.org
			 */
			private final AbstractUser object;
			/**
			 * WRITEME: Document this brpocock@star-hope.org
			 */
			private final Object [] trailer;
			
			/**
			 * WRITEME: Document this constructor brpocock@star-hope.org
			 * 
			 * @param redoIndirectObject WRITEME
			 * @param redoStepOnMe WRITEME
			 * @param redoSubject WRITEME
			 * @param redoWhere WRITEME
			 * @param redoVerb WRITEME
			 * @param redoObject WRITEME
			 * @param redoTrailer WRITEME
			 */
			StepOnMeMulligan (final String redoIndirectObject,
					final StepOnMe redoStepOnMe,
					final AbstractUser redoSubject,
					final Room redoWhere, final String redoVerb,
					final AbstractUser redoObject,
					final Object [] redoTrailer) {
				indirectObject = redoIndirectObject;
				stepOnMe = redoStepOnMe;
				subject = redoSubject;
				where = redoWhere;
				verb = redoVerb;
				object = redoObject;
				trailer = redoTrailer;
			}

			@Override
			public void run () {
				stepOnMe.acceptAction (where, subject, verb, object,
						indirectObject, trailer);
			}
		}

		/**
		 * WRITEME: Document this brpocock@star-hope.org
		 */
		private final SmudgeStain stain;

		/**
		 * Who already was annoyed with a popup from this stain?
		 */
		private final Set <AbstractUser> annoyed = new ConcurrentSkipListSet <AbstractUser> ();

		/**
		 * WRITEME: Document this constructor brpocock@star-hope.org
		 *
		 * @param theStainWhoLovedMe who got stepped-on
		 */
		StepOnMe (final SmudgeStain theStainWhoLovedMe) {
			stain = theStainWhoLovedMe;
		}

		/**
		 * @see org.starhope.appius.user.events.ActionMethod#acceptAction(org.starhope.appius.room.Room,
		 *      org.starhope.appius.user.AbstractUser, java.lang.String,
		 *      org.starhope.appius.user.AbstractUser, java.lang.String,
		 *      java.lang.Object[])
		 */
		@Override
		public boolean acceptAction (final Room where,
				final AbstractUser subject, final String verb,
				final AbstractUser object, final String indirectObject,
				final Object... trailer) {

			if (subject instanceof Ejecta) {
				stain.cleanUp (subject);
			}

			if ( ! (subject instanceof User)) {
				return false;
			}

			final StepOnMe stepOnMe = this;

			if (annoyed.contains (subject)) {
				return true;
			}

			// Not quite there yet? do-over later.
			if ( !stain.getPolygon ().contains (
					subject.getLocation ().toCoord2D ())) {
				if (stain.getPolygon ().contains (
						subject.getTarget ().toCoord2D ())) {
					AppiusClaudiusCaecus.getKalendor ().schedule (
							Geometry.getTimeToTarget (subject, System
									.currentTimeMillis ())
									+ System.currentTimeMillis (),
							new StepOnMeMulligan (indirectObject,
									stepOnMe, subject, where, verb,
									object, trailer));
				}
				// why were you here, then?
				return false;
			}

			if (subject.getInventory ().contains (
					Integer.valueOf (Harmony.CLEANUP_ITEM_ID))) {
				stain.cleanUp (subject);
				return true;
			}

			stain.ewwSticky (subject);
			annoyed.add (subject);
			return false;
		}
	}

	/**
	 * WRITEME
	 */
	private static final int CIRCLING_STEPS = 5;
	/**
	 * WRITEME
	 */
	private static final double DIAMETER = 20;
	/**
	 * WRITEME
	 */
	private static final double TRIG_SCALAR = Math.PI * 2
			/ SmudgeStain.CIRCLING_STEPS;

	/**
	 * WRITEME: Document this brpocock@star-hope.org
	 */
	protected static final AtomicInteger idAssigner = new AtomicInteger ();
	/**
	 * unique ID
	 */
	private final int id;
	/**
	 * WRITEME: Document this brpocock@star-hope.org
	 */
	private final ActionHandler myHandler;

	/**
	 * WRITEME: Document this brpocock@star-hope.org
	 */
	private final Room myRoom;

	/**
	 * the zone ID assigned for the event area containing this oil slick
	 */
	private final String eventZone;

	/**
	 * the item ID (in the room variables) used to place this oil
	 * slick's graphic
	 */
	private final String itemString;

	/**
	 * The polygonal area surrounding this stain
	 */
	private final Polygon polygon;

	/**
	 * WRITEME: Document this constructor brpocock@star-hope.org
	 *
	 * @param room WRITEME
	 * @param position WRITEME
	 */
	public SmudgeStain (final Room room, final Coord3D position) {

		id = SmudgeStain.idAssigner.incrementAndGet ();

		List <Coord2D> inscribed = new LinkedList <Coord2D> ();
		final double y = position.getY ();
		final double x = position.getX ();
		for (int i = 0; i < SmudgeStain.CIRCLING_STEPS; i++ ) {
			inscribed.add (new Coord2D (Math.cos (i
					* SmudgeStain.TRIG_SCALAR)
					* SmudgeStain.DIAMETER + x, Math.sin (i
					* SmudgeStain.TRIG_SCALAR)
					* SmudgeStain.DIAMETER + y));
		}

		final String eventIdentifier = "evt_$SmudgeStain/" + getID ();
		eventZone = room.getPlaceZoneNumber ();
		polygon = new Polygon (inscribed);
		room.setVariable (eventZone, eventIdentifier + ":"
				+ polygon.toRoomVar ());

		itemString = room.getPlaceItemNumber ();
        final String rndSmudge = randomSmudge ();
        room.setVariable (getItemString (), rndSmudge + "~" + x
				+ "~" + y + "~");
        room.setVariable ("mc~" + getItemString (), "item_mc,1");

		myRoom = room;

		final SmudgeStain stain = this;

		myHandler = new ActionHandler (room, null, "event.srv.enter/"
				+ eventIdentifier, null, new StepOnMe (stain));
		Quaestor.listen (myHandler);
		// note that the reference is held by Quaestor, so after
		// dispatching the cleanup action, we deallocate magically.

		AppiusClaudiusCaecus.getKalendor ().schedule (
				System.currentTimeMillis () + 3000, new Runnable () {

					@Override
					public void run () {
						room
.deleteVariable ("mc~"
										+ getItemString ());
					}
				});

	}

	/**
	 * WRITEME: Document this method brpocock@star-hope.org
	 *
	 * @param cleaner user who cleaned up the mess
	 */
	protected void cleanUp (final AbstractUser cleaner) {
		Quaestor.ignore (myHandler);
		getRoom ().deleteVariable (eventZone);
		getRoom ().setVariable ("mc~" + getItemString (), "item_mc,2");

		if (cleaner instanceof User) {

			// AppiusClaudiusCaecus.getKalendor ().schedule (
			// System.currentTimeMillis () + 3000, new Runnable () {
			//
			// @Override
			// public void run () {
			getRoom ().deleteVariable ("mc~" + getItemString ());
			getRoom ().deleteVariable (getItemString ());
			// }
			// });
			try {
				Quaestor.startEvent (cleaner, Harmony.EVENT_MONIKER);
			} catch (AlreadyExistsException e) {
				AppiusClaudiusCaecus
						.reportBug (
								"Caught a AlreadyExistsException in SmudgeStain.cleanUp ",
								e);
			} catch (NotFoundException e) {
				AppiusClaudiusCaecus
						.reportBug (
								"Caught a NotFoundException in SmudgeStain.cleanUp ",
								e);
			}
			cleaner.speak (getRoom (), LibMisc
					.getText ("game.oilSlick.clean.say"));
			int cleaned = Quaestor.getStartedEventsByType (cleaner,
					Harmony.EVENT_MONIKER).size ();
			if (cleaned == 1) {
				cleaner.acceptMessage ("Harmony", "Harmony", LibMisc
						.getText ("game.oilSlick.clean.first"));
			} else {
				cleaner.acceptMessage ("Harmony", "Harmony", String
						.format (LibMisc
								.getText ("game.oilSlick.clean.later"),
								Integer.valueOf (cleaned)));
			}
		}
	}

	/**
	 * WRITEME: Document this method brpocock@star-hope.org
	 *
	 * @param steppedInIt who stepped in the icky spot
	 */
	protected void ewwSticky (final AbstractUser steppedInIt) {
		steppedInIt.speak (steppedInIt.getRoom (), LibMisc
				.getText ("game.oilSlick.goop.say"));
		steppedInIt.acceptMessage ("Smudge", "Smudge", LibMisc
				.getText ("game.oilSlick.goop.adm"));
	}

	/**
	 * @return this stain's particular ID
	 */
	private int getID () {
		return id;
	}

	/**
	 * @return the itemString
	 */
	public String getItemString () {
		return itemString;
	}

	/**
	 * @return the event region for this stain
	 */
	public Polygon getPolygon () {
		return polygon;
	}

	/**
	 * @return the myRoom
	 */
	public Room getRoom () {
		return myRoom;
	}

	/**
	 * @return an oily slick item number to be placed in the room
	 */
	private String randomSmudge () {
		return "2419";
	}

}
