/*
* $Id$
*
* Copyright (c) 2004 by Rodney Kinney
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.build.module.map;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import VASSAL.build.GameModule;
import VASSAL.build.module.Chatter;
import VASSAL.build.module.GlobalOptions;
import VASSAL.build.module.Map;
import VASSAL.command.AddPiece;
import VASSAL.command.ChangeTracker;
import VASSAL.command.Command;
import VASSAL.command.MovePiece;
import VASSAL.command.NullCommand;
import VASSAL.counters.GamePiece;
import VASSAL.counters.MovementMarkable;
import VASSAL.counters.PieceAccess;
import VASSAL.counters.Properties;
import VASSAL.counters.Stack;
import VASSAL.tools.FormattedString;
/**
* Builds an auto-report message for a collection of Move Commands
*/
public class MovementReporter {
protected FormattedString format = new FormattedString();
protected List<MoveSummary> movesToReport = new ArrayList<MoveSummary>();
protected List<MoveSummary> movesToMark = new ArrayList<MoveSummary>();
public MovementReporter(Command moveCommand) {
extractMoveCommands(moveCommand);
}
protected void extractMoveCommands(Command c) {
MoveSummary summary = null;
if (c instanceof AddPiece) {
AddPiece addPiece = ((AddPiece) c);
if (shouldReport(addPiece)) {
summary = createMoveSummary(addPiece);
}
}
else if (c instanceof MovePiece) {
MovePiece movePiece = (MovePiece) c;
if (shouldReport(movePiece)) {
summary = createMoveSummary(movePiece);
}
}
if (summary != null) {
// Do we already have an instance that represents movement
// between the same two map positions on the same two maps?
int index = movesToReport.indexOf(summary);
if (index >= 0
&& c instanceof MovePiece
&& shouldReport((MovePiece) c)) {
MoveSummary existing = movesToReport.get(index);
existing.append((MovePiece) c);
}
else {
movesToReport.add(summary);
}
if (shouldMarkMoved(summary)) {
movesToMark.add(summary);
}
}
Command[] sub = c.getSubCommands();
for (int i = 0; i < sub.length; i++) {
extractMoveCommands(sub[i]);
}
}
protected MoveSummary createMoveSummary(AddPiece c) {
return new MoveSummary(c);
}
protected MoveSummary createMoveSummary(MovePiece c) {
return new MoveSummary(c);
}
/**
* Mark all pieces with the {@link MovementMarkable} trait
* @return the equivalent Command
*/
public Command markMovedPieces() {
Command c = null;
if (!movesToMark.isEmpty()) {
c = new NullCommand();
for (MoveSummary ms : movesToMark) {
for (GamePiece p : ms.pieces) {
c.append(markMoved(p));
}
}
}
return c;
}
public Command markMoved(GamePiece p) {
Command c = null;
if (p instanceof Stack) {
c = new NullCommand();
for (Iterator<GamePiece> i = ((Stack) p).getPiecesIterator();
i.hasNext();) {
c.append(markMoved(i.next()));
}
}
else if (p.getProperty(Properties.MOVED) != null) {
if (p.getId() != null) {
ChangeTracker comm = new ChangeTracker(p);
p.setProperty(Properties.MOVED, Boolean.TRUE);
c = comm.getChangeCommand();
}
}
return c;
}
protected boolean shouldMarkMoved(MoveSummary summary) {
String option = Map.getMapById(summary.getNewMapId()).getAttributeValueString(Map.MARK_MOVED);
if (option == null) {
option = GlobalOptions.getInstance().getAttributeValueString(GlobalOptions.MARK_MOVED);
}
if (GlobalOptions.ALWAYS.equals(option)) {
return true;
}
else if (GlobalOptions.NEVER.equals(option)) {
return false;
}
else {
return Boolean.TRUE.equals(GameModule.getGameModule().getPrefs().getValue(Map.MARK_MOVED));
}
}
protected boolean shouldReport(AddPiece addPiece) {
GamePiece target = addPiece.getTarget();
if (target != null
&& !(target instanceof Stack)
&& !Boolean.TRUE.equals(target.getProperty(Properties.INVISIBLE_TO_ME))
&& !Boolean.TRUE.equals(target.getProperty(Properties.INVISIBLE_TO_OTHERS))) {
return true;
}
else {
return false;
}
}
protected boolean shouldReport(MovePiece movePiece) {
GamePiece target = GameModule.getGameModule().getGameState().getPieceForId(movePiece.getId());
if (target == null) {
return false;
}
if (target instanceof Stack) {
GamePiece top = ((Stack) target).topPiece(null);
return top != null;
}
else {
return !Boolean.TRUE.equals(target.getProperty(Properties.INVISIBLE_TO_ME))
&& !Boolean.TRUE.equals(target.getProperty(Properties.INVISIBLE_TO_OTHERS));
}
}
public Command getReportCommand() {
PieceAccess.GlobalAccess.hideAll();
Command c = new NullCommand();
for (MoveSummary ms : movesToReport) {
Map fromMap = Map.getMapById(ms.getOldMapId());
Map toMap = Map.getMapById(ms.getNewMapId());
format.clearProperties();
if (fromMap == null) {
format.setFormat(toMap.getCreateFormat());
}
else if (fromMap != toMap) {
format.setFormat(toMap.getMoveToFormat());
}
else {
format.setFormat(toMap.getMoveWithinFormat());
}
if (format.getFormat().length() == 0) {
break;
}
format.setProperty(Map.PIECE_NAME, ms.getPieceName());
format.setProperty(Map.LOCATION, getLocation(toMap, ms.getNewPosition()));
if (fromMap != null) {
format.setProperty(Map.OLD_MAP, fromMap.getLocalizedConfigureName());
format.setProperty(Map.OLD_LOCATION, getLocation(fromMap, ms.getOldPosition()));
}
format.setProperty(Map.MAP_NAME, toMap.getLocalizedConfigureName());
String moveText = format.getLocalizedText();
if (moveText.length() > 0) {
c = c.append(new Chatter.DisplayText(GameModule.getGameModule().getChatter(), "* " + moveText));
}
}
PieceAccess.GlobalAccess.revertAll();
return c;
}
protected String getLocation(Map map, Point p) {
return map.localizedLocationName(p);
}
/**
* A version of the MovementReporter for reporting the movement of
* Invisible pieces. Replace the locations with '?'
*/
public static class HiddenMovementReporter extends MovementReporter {
public HiddenMovementReporter(Command moveCommand) {
super(moveCommand);
}
protected MoveSummary createMoveSummary(AddPiece c) {
return new HiddenMoveSummary(c);
}
protected MoveSummary createMoveSummary(MovePiece c) {
return new HiddenMoveSummary(c);
}
protected boolean shouldReport(AddPiece addPiece) {
GamePiece target = addPiece.getTarget();
if (target != null
&& !(target instanceof Stack)
&& (Boolean.TRUE.equals(target.getProperty(Properties.INVISIBLE_TO_ME))
|| Boolean.TRUE.equals(target.getProperty(Properties.INVISIBLE_TO_OTHERS)))) {
return true;
}
else {
return false;
}
}
protected boolean shouldReport(MovePiece movePiece) {
GamePiece target = GameModule.getGameModule().getGameState().getPieceForId(movePiece.getId());
if (target == null) {
return false;
}
if (target instanceof Stack) {
for (Iterator<GamePiece> i = ((Stack) target).getPiecesIterator(); i.hasNext() ;) {
GamePiece piece = i.next();
if (Boolean.TRUE.equals(piece.getProperty(Properties.INVISIBLE_TO_ME))
|| Boolean.TRUE.equals(piece.getProperty(Properties.INVISIBLE_TO_OTHERS))) {
return true;
}
}
return false;
}
else {
return Boolean.TRUE.equals(target.getProperty(Properties.INVISIBLE_TO_ME))
|| Boolean.TRUE.equals(target.getProperty(Properties.INVISIBLE_TO_OTHERS));
}
}
protected String getLocation(Map map, Point p) {
return "?";
}
}
public static class MoveSummary {
protected String oldMapId, newMapId;
protected Point oldPosition, newPosition;
protected List<GamePiece> pieces = new ArrayList<GamePiece>();
public MoveSummary(AddPiece c) {
GamePiece target = c.getTarget();
newMapId = target.getMap().getIdentifier();
newPosition = target.getPosition();
pieces.add(target);
}
public MoveSummary(MovePiece c) {
GamePiece target = GameModule.getGameModule()
.getGameState().getPieceForId(c.getId());
oldMapId = c.getOldMapId();
newMapId = c.getNewMapId();
oldPosition = c.getOldPosition();
newPosition = c.getNewPosition();
if (target != null) {
pieces.add(target);
}
}
public String getNewMapId() {
return newMapId;
}
public Point getNewPosition() {
return newPosition;
}
public String getOldMapId() {
return oldMapId;
}
public Point getOldPosition() {
return oldPosition;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MoveSummary)) return false;
final MoveSummary moveSummary = (MoveSummary) o;
if (!newPosition.equals(moveSummary.newPosition)) return false;
if (!newMapId.equals(moveSummary.newMapId)) return false;
if (oldMapId != null ? !oldMapId.equals(moveSummary.oldMapId) : moveSummary.oldMapId != null) return false;
if (oldMapId != null) {
// If there is no old map, then ignore the old position for equals() purposes.
if (oldPosition != null ? !oldPosition.equals(moveSummary.oldPosition) : moveSummary.oldPosition != null) return false;
}
return true;
}
public int hashCode() {
int result;
result = (oldMapId != null ? oldMapId.hashCode() : 0);
result = 29 * result + newMapId.hashCode();
result = 29 * result + newPosition.hashCode();
if (oldMapId != null) {
result = 29 * result + (oldPosition != null ? oldPosition.hashCode() : 0);
}
return result;
}
public void append(MovePiece movePiece) {
GamePiece target = GameModule.getGameModule()
.getGameState()
.getPieceForId(movePiece.getId());
if (target != null && !pieces.contains(target)) {
pieces.add(target);
}
}
public String getPieceName() {
final StringBuilder names = new StringBuilder();
for (Iterator<GamePiece> i = pieces.iterator(); i.hasNext(); ) {
names.append(i.next().getLocalizedName());
if (i.hasNext()) {
names.append(", ");
}
}
return names.toString();
}
}
public static class HiddenMoveSummary extends MoveSummary {
public HiddenMoveSummary(AddPiece c) {
super(c);
}
public HiddenMoveSummary(MovePiece c) {
super(c);
}
public String getPieceName() {
final StringBuilder names = new StringBuilder();
boolean first = true;
for (Iterator<GamePiece> i = pieces.iterator(); i.hasNext(); ) {
GamePiece piece = i.next();
if (piece instanceof Stack) {
for (Iterator<GamePiece> j = ((Stack) piece).getPiecesIterator(); j.hasNext(); ) {
GamePiece p = j.next();
if (isInvisible(p)) {
if (!first) {
names.append(", ");
}
names.append("?");
first = false;
}
}
}
else {
if (isInvisible(piece)) {
if (!first) {
names.append(", ");
}
names.append("?");
first = false;
}
}
}
return names.toString();
}
protected boolean isInvisible(GamePiece piece) {
return Boolean.TRUE.equals(piece.getProperty(Properties.INVISIBLE_TO_ME))
|| Boolean.TRUE.equals(piece.getProperty(Properties.INVISIBLE_TO_OTHERS));
}
}
}