//   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.plugin.html;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Map;

import org.xml.sax.SAXException;

import de.ugoe.cs.util.StringTools;
import de.ugoe.cs.util.console.Console;

/**
 * <p>
 * deletes queries from URLs belonging to GUI elements contained in an HTML log file. For this, it
 * parses a given file and dumps a replacement, in which all are removed.
 * </p>
 * 
 * @author Patrick Harms
 * @version 1.0
 * 
 */
public class HTMLLogQueryDeleter extends AbstractDefaultLogParser {
    
    /**
     * <p>
     * The output writer into which the new variant of the log file without queries is written
     * </p>
     */
    private PrintWriter outputWriter;

    /**
     * <p>
     * called to delete queries in the given log file. The method reuses 
     * {@link #deleteQueries(File)}.
     * </p>
     * 
     * @param file the log file in which the queries shall be deleted
     */
    public void deleteQueries(String file) {
        if (file == null) {
            throw new IllegalArgumentException("file must not be null");
        }

        deleteQueries(new File(file));
    }

    /**
     * <p>
     * called to delete the queries in the given log file. The given file is read
     * completely. All events are written to an output file as they are. All GUI elements are
     * written to an output file as they are, as well, as long as they do not have a query
     * parameter. If they have a query parameter, it is removed. Finally, the original log file is
     * deleted and replaced by the adapted variant. Log files, which do not contain query parameters
     * stay untouched.
     * </p>
     * 
     * @param file the log file in which the query parameters shall be removed
     */
    public void deleteQueries(File file) {
        if (file == null) {
            throw new IllegalArgumentException("file must not be null");
        }
        
        if (!file.exists()) {
            throw new IllegalArgumentException("file must denote an existing file");
        }
        
        if (!file.isFile()) {
            throw new IllegalArgumentException("file must denote a file");
        }
        
        File outFile = new File(file.getParentFile(), file.getName() + "_tmp");
        boolean parsingFailed = false;
        
        try {
            FileOutputStream fis = new FileOutputStream(outFile);
            outputWriter = new PrintWriter(new OutputStreamWriter(fis, "UTF-8"));
            outputWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            outputWriter.println("<session>");

            try {
                super.parseFile(file);
            }
            catch (SAXException e) {
                parsingFailed = true;
            }
            
            outputWriter.println("</session>");
            outputWriter.flush();
        }
        catch (FileNotFoundException e) {
            Console.printerrln("could not create adapted file " + outFile);
        }
        catch (UnsupportedEncodingException e) {
            // this should never happen
            e.printStackTrace();
        }
        finally {
            if (outputWriter != null) {
                outputWriter.close();
                outputWriter = null;
            }
        }
        
        if (!parsingFailed && outFile.exists()) {
            if (!file.delete()) {
                Console.printerrln("could not delete adapted file " + file);
            }
            else if (!outFile.renameTo(file)) {
                Console.printerrln
                    ("could not rename adapted file to original file name " + file);
            }            
            else {
                Console.println("removed queries from file " + file);
            }
        }
        else {
            if (!outFile.delete()) {
                Console.printerrln("could not delete temporary file " + outFile);
            }
        }
    }
    
    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#parseFile(java.lang.String)
     */
    @Override
    public void parseFile(String filename) {
        throw new IllegalStateException("this method must not be called externally");
    }

    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#parseFile(java.io.File)
     */
    @Override
    public void parseFile(File file) {
        throw new IllegalStateException("this method must not be called externally");
    }

    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleGUIElement(String, Map)
     */
    @Override
    protected boolean handleGUIElement(String id, Map<String, String> parameters)
        throws SAXException
    {
        outputWriter.print("<component id=\"");
        outputWriter.print(id);
        outputWriter.println("\">");
        
        for (Map.Entry<String, String> param : parameters.entrySet()) {
            if (!"query".equals(param.getKey())) {
                dumpParam(param.getKey(), param.getValue());
            }
        }
            
        outputWriter.println("</component>");
        
        return true;
    }

    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleEvent(String,Map)
     */
    @Override
    protected boolean handleEvent(String type, Map<String, String> parameters) throws SAXException {
        outputWriter.print("<event type=\"");
        outputWriter.print(type);
        outputWriter.println("\">");
        
        for (Map.Entry<String, String> param : parameters.entrySet()) {
            dumpParam(param.getKey(), param.getValue());
        }
            
        outputWriter.println("</event>");
        
        return true;
    }

    /**
     * <p>
     * dumps a parameter with the given name and value to the log file. The result is a
     * tag named param with a name attribute and a value attribute. The value is transformed
     * to a String if it is no String already. Furthermore, an XML entity replacement is performed
     * if required.
     * </p>
     *
     * @param name  the name of the parameter to be dumped
     * @param value the value of the parameter to be dumped
     */
    private void dumpParam(String name, Object value) {
        if (value == null) {
            return;
        }
        
        String val;
        
        if (value instanceof String) {
            val = (String) value;
        }
        else {
            val = String.valueOf(value);
        }
        
        outputWriter.print(" <param name=\"");
        outputWriter.print(name);
        outputWriter.print("\" value=\"");
        outputWriter.print(StringTools.xmlEntityReplacement(val));
        outputWriter.println("\"/>");
    }
}
