/*
 * Scone - The Web Enhancement Framework
 * Copyright (c) 2004 Harald Weinreich, Volkert Buchmann, Frank Wollenweber, Torsten Haß
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package scone.usertesttool;

/**
 * This object stores the current state of a specific user test
 *
 * @author Torsten Hass
 * @version 1.0, 08/06/2003
 */
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

import scone.util.ErrorLog;


public class CurrentState {
    
    private int numberOfTasks = 0;
    private int currentTask = 0;
    private int completedTasks = 0;
//    private String xmlFileName; // Name of the xml file that drives the User test
    private String parentFrameAndFrameName; // Name of the parent frame and the current frame
    private String testPerson;
    private String xmlFile;
    private String resultPath;
    private String resultFilename;
    private int testNumber; // one Number for each Test
    
    private Document resultDoc;
    
    public CurrentState (String testPerson, String xmlFile, int testNumber, int numberOfTasks, String resultPath) {

        this.numberOfTasks = numberOfTasks;
        this.testPerson = testPerson;
        this.xmlFile = xmlFile;
        this.testNumber = testNumber;
        this.resultPath = resultPath;
        
        // make result filename 
        String leadingZeros = "";
        if (testNumber < 1000) { leadingZeros += "0";}
        if (testNumber < 100) { leadingZeros += "0";}
        if (testNumber < 10) { leadingZeros += "0";}
        File file = new File(xmlFile);
        resultFilename = forceEndingSlash(resultPath) + file.getName().substring(0, file.getName().length()-4) + "." + leadingZeros + Integer.toString(testNumber) + "." + testPerson + ".xml";

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        try {

            DocumentBuilder builder = factory.newDocumentBuilder();
            resultDoc = builder.newDocument();

            Element userTestResult = resultDoc.createElement("userTestResult"); 
            resultDoc.appendChild(userTestResult);
            userTestResult.setAttribute("timeStamp", new Date(System.currentTimeMillis()).toString());
            userTestResult.setAttribute("testPerson", testPerson);
            userTestResult.setAttribute("xmlFile", xmlFile);
            userTestResult.setAttribute("numberOfTasks", Integer.toString(numberOfTasks));
            userTestResult.setAttribute("completedTasks", "0");
            Text textNode = resultDoc.createTextNode("\n");
            userTestResult.appendChild(textNode);
            

        } catch (ParserConfigurationException pce) {
            // Parser with specified options can't be built
            pce.printStackTrace();
        }
    }

    public CurrentState (String resultFile, String resultPath, int startTask) {
        resultFilename = resultFile;
        this.resultPath = resultPath; 
        this.currentTask = startTask;
        
        resultDoc = parseXmlFile(resultFilename);
        
        NodeList nList = resultDoc.getElementsByTagName("task");
        while (nList.getLength() >= startTask) {
            nList.item(0).getParentNode().removeChild(nList.item(nList.getLength()-1)); // remove the last task node
            nList = resultDoc.getElementsByTagName("task");
        }
        this.completedTasks = nList.getLength();
        nList = resultDoc.getElementsByTagName("userTestResult");
        try {
            this.numberOfTasks = Integer.parseInt(getAttribByName("numberOfTasks", nList.item(0)));
        } catch (NumberFormatException e) {
        }
        this.testPerson = getAttribByName("testPerson", nList.item(0));
        this.xmlFile = getAttribByName("xmlFile", nList.item(0));
        ((Element)nList.item(0)).setAttribute("completedTasks", Integer.toString(completedTasks));
        currentTask--;
        startNextTask();
    }
    
    public Document parseXmlFile (String fileName) {
        // Parse the xml-File to a Document
        Document document = null;
        
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            document = builder.parse( new File(fileName));
        } catch (SAXException sxe) {
           // Error generated during parsing)
           Exception  x = sxe;
           if (sxe.getException() != null)
               x = sxe.getException();
           x.printStackTrace();
        } catch (ParserConfigurationException pce) {
            // Parser with specified options can't be built
            pce.printStackTrace();
        } catch (IOException ioe) {
           // I/O error
           ioe.printStackTrace();
        }
        return document;
    }        


    public boolean hasMoreTasks() {
        return !(currentTask == numberOfTasks);
    }
        
    
    public void startNextTask () {
        currentTask++;
        NodeList nList = resultDoc.getElementsByTagName("userTestResult");
        Node utr = nList.item(0);
        
        Text textNode = resultDoc.createTextNode("  ");
        utr.appendChild(textNode);

        Element newTaskNode = resultDoc.createElement("task");
        utr.appendChild(newTaskNode);
        newTaskNode.setAttribute("timeStamp", new Long(System.currentTimeMillis()).toString());

        textNode = resultDoc.createTextNode("\n");
        newTaskNode.appendChild(textNode);

    }
    
    public void setTaskName (String name) {
        NodeList nList = resultDoc.getElementsByTagName("task");
        Node taskNode = nList.item(currentTask-1);

        ((Element)taskNode).setAttribute("name", name);
    }
    
    public void addButtonClick (String name) {
        NodeList nList = resultDoc.getElementsByTagName("task");
        Node taskNode = nList.item(currentTask-1);
        
        Text textNode = resultDoc.createTextNode("    ");
        taskNode.appendChild(textNode);

        Element buttonClickNode = resultDoc.createElement("buttonClick");
        taskNode.appendChild(buttonClickNode);
        buttonClickNode.setAttribute("action", "click");
        buttonClickNode.setAttribute("timeStamp", new Long(System.currentTimeMillis()).toString());
        buttonClickNode.setAttribute("name", name);

        textNode = resultDoc.createTextNode("\n");
        taskNode.appendChild(textNode);
    }
    
    public void addLinkEvent (String action, String uri, String frameName) {
        NodeList nList = resultDoc.getElementsByTagName("task");
        Node taskNode = nList.item(currentTask-1);
        
        Text textNode = resultDoc.createTextNode("    ");
        taskNode.appendChild(textNode);

        Element linkClickNode = resultDoc.createElement("link");
        taskNode.appendChild(linkClickNode);
        linkClickNode.setAttribute("action", action);
        linkClickNode.setAttribute("timeStamp", new Long(System.currentTimeMillis()).toString());
        linkClickNode.setAttribute("uri", uri);
        linkClickNode.setAttribute("frameName", frameName);

        textNode = resultDoc.createTextNode("\n");
        taskNode.appendChild(textNode);
    }
    
    public void addFormData (String action, String uri, String frameName) {
        NodeList nList = resultDoc.getElementsByTagName("task");
        Node taskNode = nList.item(currentTask-1);
        
        Text textNode = resultDoc.createTextNode("    ");
        taskNode.appendChild(textNode);

        Element linkClickNode = resultDoc.createElement("formData");
        taskNode.appendChild(linkClickNode);
        linkClickNode.setAttribute("action", action);
        linkClickNode.setAttribute("timeStamp", new Long(System.currentTimeMillis()).toString());
        linkClickNode.setAttribute("uri", uri);
        linkClickNode.setAttribute("frameName", frameName);

        textNode = resultDoc.createTextNode("\n");
        taskNode.appendChild(textNode);
    }
    
    public void addBrowserEvent (String action, String uri, String frameName) {
        NodeList nList = resultDoc.getElementsByTagName("task");
        Node taskNode = nList.item(currentTask-1);
        
        Text textNode = resultDoc.createTextNode("    ");
        taskNode.appendChild(textNode);

        Element BrowserButtonNode = resultDoc.createElement("browser");
        taskNode.appendChild(BrowserButtonNode);
        BrowserButtonNode.setAttribute("action", action);
        BrowserButtonNode.setAttribute("timeStamp", new Long(System.currentTimeMillis()).toString());
        BrowserButtonNode.setAttribute("uri", uri);
        BrowserButtonNode.setAttribute("frameName", frameName);

        textNode = resultDoc.createTextNode("\n");
        taskNode.appendChild(textNode);
    }
    
    public void addBrowserControlEvent (String action, String uri, String frameName) {
        NodeList nList = resultDoc.getElementsByTagName("task");
        Node taskNode = nList.item(currentTask-1);
        
        Text textNode = resultDoc.createTextNode("    ");
        taskNode.appendChild(textNode);

        Element BrowserButtonNode = resultDoc.createElement("browserControl");
        taskNode.appendChild(BrowserButtonNode);
        BrowserButtonNode.setAttribute("action", action);
        BrowserButtonNode.setAttribute("timeStamp", new Long(System.currentTimeMillis()).toString());
        BrowserButtonNode.setAttribute("uri", uri);
        BrowserButtonNode.setAttribute("frameName", frameName);

        textNode = resultDoc.createTextNode("\n");
        taskNode.appendChild(textNode);
    }
    
    public void addData (String tagname, String actionData, String componentName, String timeStamp) {
        NodeList nList = resultDoc.getElementsByTagName("task");
        Node taskNode = nList.item(currentTask-1);
        
        Text textNode = resultDoc.createTextNode("    ");
        taskNode.appendChild(textNode);

        Element dataNode = resultDoc.createElement(tagname);
        taskNode.appendChild(dataNode);
        dataNode.setAttribute("action", actionData);
        dataNode.setAttribute("name", componentName);
        dataNode.setAttribute("timeStamp", timeStamp);

        textNode = resultDoc.createTextNode("\n");
        taskNode.appendChild(textNode);
    }
    
    public void addStopWatch (String action, String name, long time) {
        NodeList nList = resultDoc.getElementsByTagName("task");
        Node taskNode = nList.item(currentTask-1);
        
        Text textNode = resultDoc.createTextNode("    ");
        taskNode.appendChild(textNode);

        Element StopWatchNode = resultDoc.createElement("stopWatch");
        taskNode.appendChild(StopWatchNode);
        StopWatchNode.setAttribute("action", action);
        StopWatchNode.setAttribute("name", name);
        StopWatchNode.setAttribute("periodOfTime", new Long(time).toString());

        textNode = resultDoc.createTextNode("\n");
        taskNode.appendChild(textNode);
    }
    
    public void endTask () {
        NodeList nList = resultDoc.getElementsByTagName("task");
        Node taskNode = nList.item(currentTask-1);
        
        Text textNode = resultDoc.createTextNode("  ");
        taskNode.appendChild(textNode);

        nList = resultDoc.getElementsByTagName("userTestResult");
        Element userTestResult = (Element)nList.item(0);

        textNode = resultDoc.createTextNode("\n");
        userTestResult.appendChild(textNode);

        if (taskNode.hasChildNodes()) {       // then there were events 
            completedTasks = currentTask;
            userTestResult.setAttribute("completedTasks", Integer.toString(completedTasks));
        }
    }
    
    public void writeXmlResults () {
        // write resultDoc to xml file
        try {
            TransformerFactory tFactory = TransformerFactory.newInstance();
            Transformer transformer = tFactory.newTransformer();

            File outputFile = new File(resultFilename);
            
            DOMSource source = new DOMSource(resultDoc);
            StreamResult result = new StreamResult(outputFile);
            transformer.transform(source, result);
        } catch (TransformerConfigurationException tce) {
            // Error generated by the parser
            ErrorLog.log(this, "writeXmlResults", "Transformer Factory error", tce);
            
            // use the contained exception, if any
            Throwable x = tce;
            if (tce.getException() != null) {
                x = tce.getException();
                //ErrorLog.log(this, "writeXmlResults", "Error contained in Transformer Factory error", x);
            }
        } catch (TransformerException te) {
            //Error generated by the parser
            ErrorLog.log(this, "writeXmlResults", "Transformation error", te);
            
            // use the contained exception, if any
            Throwable x = te;
            if (te.getException() != null) {
                x = te.getException();
                //ErrorLog.log(this, "writeXmlResults", "Error contained in Transformation error", x);
            }
        }
    }                    


    public void writeTabDevidedResults () {
        
        String textFilename = resultFilename.substring(0, resultFilename.length()-4) + ".txt";
        File outFile = new File(textFilename);
        try {
            FileWriter out = new FileWriter(outFile);
            NodeList nodeList = resultDoc.getChildNodes();
            for (int i = 0; i < nodeList.getLength(); i++) {
                tabDevidedOutput( nodeList.item(i), out);
            }
            out.close();   
        } catch (IOException e) {
            ErrorLog.log(this, "writeTabDevidedResults", "Error while writing to textfile", e);
        }
    }
        
    public void tabDevidedOutput (Node node, FileWriter out) {
        
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            try {
                if (node.getNodeName().equals("userTestResult")) {
                    out.write(node.getNodeName()+"\t"+getAttribByName("testPerson", node)+"\t"+
                              getAttribByName("xmlFile", node)+"\t"+getAttribByName("timeStamp", node)+"\t"+
                              getAttribByName("completedTasks", node)+" / "+getAttribByName("numberOfTasks", node)+"\t"+"\r\n");
                } else if (node.getNodeName().equals("stopWatch")) {                    
                    out.write(node.getNodeName()+"\t"+getAttribByName("name", node)+"\t"+
                              getAttribByName("action", node)+"\t"+getAttribByName("timeStamp", node)+"\t"+
                              getAttribByName("periodOfTime", node)+"\t"+"\r\n");
                } else if (node.getNodeName().equals("browser") ||
                           node.getNodeName().equals("browserControl") ||
                           node.getNodeName().equals("link")) {                    
                    out.write(node.getNodeName()+"\t"+getAttribByName("frameName", node)+"\t"+
                              getAttribByName("action", node)+"\t"+getAttribByName("timeStamp", node)+"\t"+
                              getAttribByName("uri", node)+"\t"+"\r\n");
                } else {
                    out.write(node.getNodeName()+"\t"+getAttribByName("name", node)+"\t"+
                              getAttribByName("action", node)+"\t"+getAttribByName("timeStamp", node)+"\t"+
                              getAttribByName("uri", node)+"\t"+"\r\n");
                }
            } catch (IOException e) {
                ErrorLog.log(this, "writeTabDevidedResults", "Error while writing to textfile", e);
            }
        }        
        NodeList nodeList = node.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            tabDevidedOutput( nodeList.item(i), out);
        }
    }
    
            
    public int getCurrentTask () {
        return currentTask;
    }
    
    public String getXmlFilename () {
        return xmlFile;
    }
    
    public String getResultFilename () {
        return resultFilename;
    }
    
    public void setParentFrameAndFrameName (String parentFrameAndFrameName) {
        this.parentFrameAndFrameName = parentFrameAndFrameName;
    }
    
    public String getParentFrameAndFrameName () {
        return parentFrameAndFrameName;
    }
    
    public int getNumberOfTasks () {
        return numberOfTasks;
    }
    
    public int getCompletedTasks() {
        return completedTasks;
    }
    
    
    protected String forceEndingSlash(String path) {
        if (!(path.endsWith("/") || path.endsWith("\\"))) {
            path += "/";
        }
        return path;
    }


    private String getText (Node node) {
        String textString = "";
        org.w3c.dom.NodeList nList = node.getChildNodes();
        for (int i = 0; i<nList.getLength(); i++) {
            if (nList.item(i).getNodeType() == org.w3c.dom. Node.TEXT_NODE) {
                textString += nList.item(i).getNodeValue();    
            }
        }
        return textString;
    }
    

    private String getAttribByName (String attrName, Node node) {
        org.w3c.dom.NamedNodeMap nMap = node.getAttributes();
        String result = "";
        int i = 0;
        while (i < nMap.getLength()) {
            if (nMap.item(i).getNodeName().toLowerCase().equals(attrName.toLowerCase())) {
                result = getText(nMap.item(i));
            }
            i++;
        }
        return result;
    }



}
