/
maps/
package mapmaker;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import mapmaker.mapcmd.*;
import util.*;

/** the Controller of mapmaker's MVC pattern
 */
public class SimpleHandler extends MapEventHandler {  

  public static final Object
    STATE_DRAG = "STATE_DRAG",
    STATE_LINK = "STATE_LINK";

  AreaMap map;
  NodeCollector collector;
  StateController 
    dragState = new ConstStateController(new Boolean(true)),
    linkState = dragState;

  /** @param map the map to be controlled
   */
  public SimpleHandler(AreaMap map) {
    this.map = map;
    collector = new NodeCollector();
  } // Simplehandler

  /** @param pos the map position of the room that was clicked upon
   */
  public void roomClicked(MouseEvent e, Point pos) {
    if (!e.isMetaDown()) { // left click
      if (!e.isControlDown()) {
	if (!e.isShiftDown())
	  map.execute(new CmdNewRoom(pos, MapFactory.type_DescRoom));
	else {
	  // mark/unmark the room clicked on
	  Room room = map.getRoom(pos);
	  if (room == null)
	    return;
	  map.execute(new RCmdSetMarked(room.getPos(), !room.getMarked()));
	}
      }
    }
    else {
      if (e.isControlDown()) {
	// copy room description
	Room source = map.getSelected();
	Room target = map.getRoom(pos);
	if (target != null && source != target &&
	    target instanceof DescObj) {
	  if (source == null || !(source instanceof DescObj))
	    ((DescObj)target).setDesc(null);
	  else
	    ((DescObj)target).setDesc(((DescObj)source).getDesc());
	  map.notifyOfChange(MapEvent.ChangeDesc);
	}
	return;
      }
      // change select state
      Room room = map.getRoom(pos);
      if (room == null || room == map.getSelected())
	map.execute(new CmdUnselectRoom());
      else
	map.execute(new CmdSelectRoom(pos));
    }
  } // roomClicked

  /** @param pressed the map position of the room where the
   * mousekey was pressed down
   * @param and where it was released
   */
  public void roomDragged(MouseEvent e, Point pressed, Point released) {
    if (!e.isMetaDown()) { // left click
      if (e.isControlDown()) {
	// move rooms
	Room root = map.getRoom(pressed);
	if (root == null)
	  return;
	Point dir = new Point(released.x - pressed.x, 
			      released.y - pressed.y);
	Set rooms;
	if (root.getMarked()) {
	  // move all marked rooms
	  Room[] roomArr = MapUtil.getMarkedRooms(map);
	  rooms = new HashSet();
	  for (int i = 0; i < roomArr.length; i++)
	    rooms.add(roomArr[i]);
	}
	else {
	  // move all rooms connected
	  rooms = collector.getNodeCollection(root, dragState.state());
	}
	if (MapUtil.isMoveable(map, rooms, dir))
	  MapUtil.moveRooms(map, rooms, dir);
	return;
      }
      if (e.isShiftDown()) {
	// mark all rooms in drag rectangel
	// determin drag rectangel
	int minX = Math.min(pressed.x, released.x);
	int maxX = Math.max(pressed.x, released.x);
	int minY = Math.min(pressed.y, released.y);
	int maxY = Math.max(pressed.y, released.y);
	// create and execute mapcommand to mark all rooms
	CompositeCommand markCmd = new CompositeCommand();
	for (int x = minX; x <= maxX; x++)
	  for (int y = minY; y <= maxY; y++)
	    markCmd.add(new RCmdSetMarked(new Point(x, y), true));
	map.execute(markCmd);
	return;
      }
      // only swap if released Point is empty
      if (map.getRoom(released) == null)
	map.execute(new CmdSwapRooms(pressed, released));
    }
    else { 
      // link rooms
      if (((Boolean)linkState.state()).booleanValue()) {
	CompositeCommand cc = new CompositeCommand();
	Point[] meanRooms = MapUtil.meanRooms(map, pressed, released);
	for (int i = 1; i < meanRooms.length; i++)
	  cc.add(new CmdLinkRooms(meanRooms[i - 1], meanRooms[i]));
	map.execute(cc);
      }
      else
	map.execute(new CmdLinkRooms(pressed, released));
    }
  } // roomDragged

  /** @param room the Room that contains the exit that was clicked upon
   * @param exit the direction of the exit that was clicked upon
   */
  public void linkClicked(MouseEvent e, Point room, int exit) {
    if (!e.isMetaDown()) { // left clicked
      boolean blockState = map.getRoom(room).exitBlocked(exit);
      map.execute(new RCmdSetExitBlocked(room, exit, !blockState));
    }
  } // linkClicked

  /** @param room the room containing the exits that were dragged
   * @param pressed the direction of the exit where the dragging started
   * @param released dito
   */
  public void linkDragged(MouseEvent e, Point room, 
			  int pressed, int released) {
    if (!e.isMetaDown()) // left click
      map.execute(new RCmdSwapExits(room, pressed, released));
  } // linkDragged

  /** @param pos the map position of the room that was below the
   * mouse pointer when the key was typed
   */
  public void keyTypedOnRoom(KeyEvent e, Point pos) {
    if (e.getKeyCode() == KeyEvent.VK_DELETE &&
	!(map.getSelected() != null &&
	  map.getSelected().getPos().equals(pos))) {
      map.execute(new CmdKillRoom(pos));
      return;
    }
    if (e.getKeyCode() == KeyEvent.VK_V) {
      map.execute(new CmdNewRoom(pos, MapFactory.type_VirtualRoom));
      return;
    }
  } // keyTypedOnRoom

  /** @param room the Room that contains the exit that was below the
   * mouse pointer when the key was typed
   * @param exit the direction of the exit below the mouse
   */
  public void keyTypedOnLink(KeyEvent e, Point room, int exit) {
    if (e.getKeyCode() == KeyEvent.VK_DELETE)
      map.execute(new RCmdKillLink(room, exit));
  } // keyTypedOnLink
  
  /** adds a new StateController that the SimpleHandler can use
   * to determin its state;
   * @param index = STATE_DRAG: state will be used to determin 
   * wether up/down connections should be dragged or not
   * @param index = STATE_LINK: state will be used to determin
   * if mean rooms should be connected too when linking rooms
   * @param state must be of type Boolean
   */
  public void addStateController(StateController state,
					  Object index) {
    if (index == STATE_DRAG)
      dragState = state;
    else if (index == STATE_LINK)
      linkState = state;
    else
      throw new IllegalArgumentException("unknown state index");
  } // addStateController

} // SimpleHandler