package com.christophdietze.jack.shared.pgn;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.christophdietze.jack.shared.board.Game;
import com.christophdietze.jack.shared.board.GameMetaInfo;
import com.christophdietze.jack.shared.board.Move;
import com.christophdietze.jack.shared.board.MoveNode;
import com.christophdietze.jack.shared.board.Position;
import com.christophdietze.jack.shared.board.PositionUtils;
import com.christophdietze.jack.shared.board.SanWriter;
import com.christophdietze.jack.shared.board.SanWritingException;
import com.christophdietze.jack.shared.board.GameMetaInfo.TagPair;
public class PgnWriter {
private static final int MAX_LINE_LENGTH = 80;
private Game game;
private Position currentPosition;
private StringBuilder sb;
int curLineLength = 0;
public String write(Game game) throws PgnWritingException {
assert this.game == null;
this.game = game;
try {
sb = new StringBuilder();
writeTagPairSection();
writeMoveTextSection();
return sb.toString();
} finally {
this.game = null;
currentPosition = null;
sb = null;
}
}
private void writeTagPairSection() {
GameMetaInfo metaInfo = game.getMetaInfo();
writeTagPair(PgnStandardTagName.Event, getTag(PgnStandardTagName.Event), "?");
writeTagPair(PgnStandardTagName.Site, getTag(PgnStandardTagName.Site), "?");
writeTagPair(PgnStandardTagName.Date, getTag(PgnStandardTagName.Date), "????.??.??");
writeTagPair(PgnStandardTagName.Round, getTag(PgnStandardTagName.Round), "?");
String whiteName = metaInfo.getWhitePlayerName() == null ? "?" : metaInfo.getWhitePlayerName();
writeTagPair(PgnStandardTagName.White, whiteName);
String blackName = metaInfo.getBlackPlayerName() == null ? "?" : metaInfo.getBlackPlayerName();
writeTagPair(PgnStandardTagName.Black, blackName);
writeTagPair(PgnStandardTagName.Result, game.getGameResult().getSymbol());
List<GameMetaInfo.TagPair> additionalTagPairs = getAdditionalNonStandardTagPairs();
Position initialPosition = game.getInitialPosition();
if (initialPosition != null && !initialPosition.equals(Position.STARTING_POSITION)) {
FenWriter fenWriter = new FenWriter();
String fenString = fenWriter.write(initialPosition);
additionalTagPairs.add(new TagPair("FEN", fenString));
additionalTagPairs.add(new TagPair("SetUp", "1"));
currentPosition = initialPosition;
} else {
currentPosition = Position.STARTING_POSITION;
}
Collections.sort(additionalTagPairs, new Comparator<TagPair>() {
public int compare(TagPair o1, TagPair o2) {
return o1.getName().compareTo(o2.getName());
}
});
for (TagPair tagPair : additionalTagPairs) {
writeTagPair(tagPair.getName(), tagPair.getValue());
}
sb.append("\n");
}
private void writeTagPair(PgnStandardTagName tagName, String tagValue, String defaultValue) {
if (tagValue != null) {
writeTagPair(tagName, tagValue);
} else {
writeTagPair(tagName, defaultValue);
}
}
private void writeTagPair(PgnStandardTagName tagName, String tagValue) {
writeTagPair(tagName.name(), tagValue);
}
private void writeTagPair(String tagName, String tagValue) {
sb.append("[");
sb.append(tagName);
sb.append(" \"");
sb.append(tagValue);
sb.append("\"]\n");
}
private void writeMoveTextSection() throws PgnWritingException {
if (!currentPosition.isWhiteToMove()) {
StringBuilder sb2 = new StringBuilder();
sb2.append(currentPosition.getFullmoveNumber());
sb2.append("...");
appendWord(sb2.toString());
}
// TODO handle variations
List<Move> moves = new ArrayList<Move>();
MoveNode node = game.getInitialMoveNode();
while (node.hasNext()) {
node = node.getNext();
moves.add(node.getMove());
}
int moveCounter = -1;
for (Move move : moves) {
StringBuilder sb2 = new StringBuilder();
++moveCounter;
// if (moveCounter != 0) {
// sb.append(" ");
// }
if (currentPosition.isWhiteToMove()) {
sb2.append(currentPosition.getFullmoveNumber());
sb2.append(". ");
}
sb2.append(getMoveString(move));
appendWord(sb2.toString());
}
appendWord(game.getGameResult().getSymbol());
}
private String getMoveString(Move move) throws PgnWritingException {
try {
SanWriter sanWriter = new SanWriter();
String moveString = sanWriter.write(currentPosition, move);
currentPosition = PositionUtils.makeMove(currentPosition, move);
return moveString;
} catch (SanWritingException ex) {
throw new PgnWritingException("Error writing SAN for " + move + " of position " + currentPosition);
}
}
private void appendWord(String text) {
if (curLineLength + text.length() + 1 > MAX_LINE_LENGTH) {
sb.append("\n");
sb.append(text);
curLineLength = text.length();
} else {
if (curLineLength != 0) {
sb.append(" ");
curLineLength += 1;
}
sb.append(text);
curLineLength += text.length();
}
}
private String getTag(PgnStandardTagName tagName) {
return game.getMetaInfo().getTag(tagName.name());
}
/**
* @return An unsorted list of all non-standard tag pairs.
*/
private List<TagPair> getAdditionalNonStandardTagPairs() {
List<TagPair> result = new ArrayList<TagPair>();
Set<String> standardTags = new HashSet<String>();
for (PgnStandardTagName tagName : PgnStandardTagName.values()) {
standardTags.add(tagName.name());
}
for (Map.Entry<String, String> entry : game.getMetaInfo().getTags().entrySet()) {
if (standardTags.contains(entry.getKey())) {
continue;
}
result.add(new TagPair(entry));
}
return result;
}
}