package de.ugoe.cs.eventbench;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Collection;
import java.util.List;

import de.ugoe.cs.eventbench.data.IReplayable;
import de.ugoe.cs.eventbench.data.ReplayableEvent;
import de.ugoe.cs.util.StringTools;
import de.ugoe.cs.util.console.Console;

/**
 * <p>
 * This class provides the functionality to generate replay files from sequences
 * if {@link ReplayableEvent}s.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class ReplayGenerator {

	/**
	 * <p>
	 * {@link IReplayDecorator} to be used. If this field is {@code null}, no
	 * decorator is used. Default: {@code null}
	 * </p>
	 */
	private IReplayDecorator decorator = null;

	/**
	 * <p>
	 * Id of the current session. The starting id is 1.
	 * </p>
	 */
	int sessionId = 1;

	/**
	 * <p>
	 * Creates a replay file that contains multiple event sequences.
	 * </p>
	 * 
	 * @param sequences
	 *            collection of event sequences from which the sessions are
	 *            generated
	 * @param filename
	 *            name and path of the replay file
	 */
	public void createLogfileMultipleSessions(
			Collection<List<ReplayableEvent<?>>> sequences, String filename) {
		OutputStreamWriter writer = openReplayFile(filename);
		if (writer != null) {
			try {
				decorator = sequences.iterator().next().get(0).getReplayDecorator();
				if (decorator != null) {
					writer.write(decorator.getHeader());
				}
				for (List<ReplayableEvent<?>> actions : sequences) {
					writeSession(actions, writer);
				}
				if (decorator != null) {
					writer.write(decorator.getFooter());
				}
				decorator = null;
				writer.close();
			} catch (IOException e) {
				Console.printerrln("Unable to write replay file " + filename);
			}
		}
	}

	/**
	 * <p>
	 * Creates a replay file that from a single event sequence.
	 * </p>
	 * 
	 * @param actions
	 *            event sequence from which the sessions are generated
	 * @param filename
	 *            name and path of the replay file
	 */
	public void createLogfileSingleSession(List<ReplayableEvent<?>> actions,
			String filename) {
		OutputStreamWriter writer = openReplayFile(filename);
		if (writer != null) {
			try {
				actions.get(0).getReplayDecorator();
				if (decorator != null) {
					writer.write(decorator.getHeader());
				}
				writeSession(actions, writer);
				if (decorator != null) {
					writer.write(decorator.getFooter());
				}
				decorator = null;
				writer.close();
			} catch (IOException e) {
				Console.printerrln("Unable to write replay file " + filename);
			}
		}
	}

	/**
	 * <p>
	 * Helper function that opens the replay file for writing.
	 * </p>
	 * 
	 * @param filename
	 *            name and path of the replay file
	 * @return {@link OutputStreamWriter} that writes to the replay file
	 */
	private OutputStreamWriter openReplayFile(String filename) {
		File file = new File(filename);
		boolean fileCreated;
		try {
			fileCreated = file.createNewFile();
			if (!fileCreated) {
				Console.traceln("Created logfile " + filename);
			} else {
				Console.traceln("Overwrote existing logfile " + filename);
			}
		} catch (IOException e) {
			Console.printerrln("Unable to create file " + filename);
			Console.logException(e);
		}
		OutputStreamWriter writer = null;
		try {
			writer = new OutputStreamWriter(new FileOutputStream(file),
					"UTF-16");
		} catch (IOException e) {
			Console.printerrln("Unable to open file for writing (read-only file):"
					+ filename);
			Console.logException(e);
		}
		return writer;
	}

	/**
	 * <p>
	 * Helper function that adds an event sequence to the replay.
	 * </p>
	 * 
	 * @param actions
	 *            event sequences to be added
	 * @param writer
	 *            {@link OutputStreamWriter} to which the replay is added
	 * @throws IOException
	 *             thrown if there is a problem writing to writer
	 */
	private void writeSession(List<ReplayableEvent<?>> actions,
			OutputStreamWriter writer) throws IOException {
		if (decorator != null) {
			writer.write(decorator.getSessionHeader(sessionId));
		}
		for (ReplayableEvent<?> currentAction : actions) {

			List<? extends IReplayable> replayables = currentAction
					.getReplayMessages();
			for (IReplayable replayble : replayables) {
				writer.write(replayble.getReplay() + StringTools.ENDLINE);
				writer.flush();
			}
		}
		if (decorator != null) {
			writer.write(decorator.getSessionFooter(sessionId));
		}
		sessionId++;
	}

}
