package de.ugoe.cs.quest.commands.sequences;

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 java.util.logging.Level;

import de.ugoe.cs.quest.CommandHelpers;
import de.ugoe.cs.quest.IReplayDecorator;
import de.ugoe.cs.quest.SequenceInstanceOf;
import de.ugoe.cs.quest.eventcore.Event;
import de.ugoe.cs.quest.eventcore.IReplayable;
import de.ugoe.cs.util.StringTools;
import de.ugoe.cs.util.console.Command;
import de.ugoe.cs.util.console.Console;
import de.ugoe.cs.util.console.GlobalDataContainer;

/**
 * <p>
 * Command to create a replay file from stored sessions.
 * </p>
 * 
 * TODO: Add appropriate checks if Events are replayable
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class CMDgenerateReplayfile implements Command {

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.ugoe.cs.util.console.Command#help()
	 */
	@Override
	public String help() {
		return "generateReplayfile <filename> <sequences>";
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void run(List<Object> parameters) {
		String filename;
		String sequencesName;
		try {
			filename = (String) parameters.get(0);
			sequencesName = (String) parameters.get(1);
		} catch (Exception e) {
			throw new IllegalArgumentException();
		}

		Collection<List<Event>> sequences = null;
		Object dataObject = GlobalDataContainer.getInstance().getData(
				sequencesName);
		if (dataObject == null) {
			CommandHelpers.objectNotFoundMessage(sequencesName);
			return;
		}
		if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
			CommandHelpers.objectNotType(sequencesName,
					"Collection<List<Event<?>>>");
			return;
		}

		sequences = (Collection<List<Event>>) dataObject;
		createLogfileMultipleSessions(sequences, filename);
	}
	
	    /**
	     * <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
	     */
	    private void createLogfileMultipleSessions(Collection<List<Event>> sequences, String filename) {
	        OutputStreamWriter writer = openReplayFile(filename);
	        if (writer != null) {
	            try {
	                try {
	                    decorator =
	                        sequences.iterator().next().get(0).getReplayables().get(0).getDecorator();
	                }
	                catch (Exception e) {
	                    // in the above line, many things can go wrong: emtpy sequences, null
	                    // references, etc. However, all failures just indicate that no replay decorator
	                    // should be used, hence, we ignore the exception
	                }
	                if (decorator != null) {
	                    writer.write(decorator.getHeader());
	                }
	                for (List<Event> 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>
	     * 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(Level.INFO, "Created logfile " + filename);
	            }
	            else {
	                Console.traceln(Level.INFO, "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<Event> actions, OutputStreamWriter writer) throws IOException {
	        if (decorator != null) {
	            writer.write(decorator.getSessionHeader(sessionId));
	        }
	        for (Event currentAction : actions) {

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