//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package de.ugoe.cs.autoquest.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.autoquest.CommandHelpers;
import de.ugoe.cs.autoquest.IReplayDecorator;
import de.ugoe.cs.autoquest.SequenceInstanceOf;
import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.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
                    Console.traceln(Level.FINEST, "no decorator used for " + filename);
                }
                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
     * @param encoding
     *            file encoding, empty string for platform default
     * @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++;
    }
}
