/*
 * 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;

import scone.util.*;
import scone.netobjects.*;
import scone.accesstracking.EventDecoder;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;
import java.util.Properties;
import java.util.Enumeration;
import java.util.ResourceBundle;
import java.util.Locale;

import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory;  
import javax.xml.parsers.FactoryConfigurationError;  
import javax.xml.parsers.ParserConfigurationException;
 
import org.xml.sax.SAXException;  
import org.xml.sax.SAXParseException;  

import java.io.File;
import java.io.IOException;

import org.w3c.dom.Document;
import org.w3c.dom.DOMException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;  
import java.awt.Insets;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import com.ibm.wbi.protocol.http.beans.FormHelper;

/**
 * This class controls the test
 
 * @author Torsten Hass, Harald Weinreich
 * @version 1.5, 04/15/2004
 */
public class UserTestControl extends Thread {

    protected static ResourceBundle labels = UserTestTool.labels;
    protected static PersistentProperties props = UserTestTool.props;

    private Document document;
    private Node userTestXml;
    private Task task;
    private String frameName;
//    private scone.Plugin plugin;
    
    private TaskWindow tw = null;
    private BrowserControl browserControl = null;
    private CurrentState currentState = null;
    private HashMap testComponents = null;          // components, that are valid for the whole test, e. g. like stopwatch
    
    private boolean testRunning = false;
    public boolean outputDebugInfo = false;
    
    private String xmlPath;
    private String destPath;
    private File destDir;
    private File xmlDir;
    
    private PersistentTestStateStore ptss;
    
    private long accessTime = 0;
    
//    public UserTestControl (scone.Plugin plugin) {
//        this.plugin = plugin;
//    }
    

    /**
     * This method starts the thread and opens the gui.
     */
    public void run() {
        // Create the task window that displays the task description to the test user
        tw = new TaskWindow(this);
//        tw.setVisible(false);
//        tw.toFront();

        
        // Start BrowserControl
        browserControl = new BrowserControl();

        ptss = PersistentTestStateStore.getInstance();

        if (props.get("Path for XML test descriptions") != null) {
            xmlPath = props.get("Path for XML test descriptions");
            xmlDir = new File(xmlPath);
            if (!xmlDir.isDirectory()) {
                JOptionPane.showMessageDialog(tw, "The directory:\n"+xmlDir.getAbsolutePath()+"\n with the user test XML files does not exist.\nPlease create a new directory for your tests and configure it with runscone -config\nfor the UserTestTool-Plugin", 
                    "Scone: UserTestTool", JOptionPane.ERROR_MESSAGE);
            } else {
                
                File[] fileArray = (xmlDir.listFiles());
                if ((fileArray == null) || (fileArray.length == 0)) {
                    JOptionPane.showMessageDialog(tw, "The directory:\n"+xmlDir.getAbsolutePath()+"\n does not contain any valid XML user test files.\n Please create the according files\n or copy them to that directory. You may also choose another directory for the user test files usind runscone -config\n", 
                        "Scone: UserTestTool", JOptionPane.ERROR_MESSAGE);
                } else {
                    if (props.get("Output path for test results") != null) {
                        destPath = props.get("Output path for test results");
                    } else {
                        destPath = "";
                    }
                    destDir = new File (destPath);
                    if (!destDir.isDirectory()) {
                        destDir.mkdirs();
                    }
                    ptss.setDirs(xmlPath, destPath);
                }
            }
        }
        if (props.get("Output debug information to text window") != null) {
            if (props.get("Output debug information to text window").equals("true")) {
                outputDebugInfo = true;
            }
        }
    }
    
    public Document parseXmlFile (String fileName) {
        // Parse the xml-File to a Document
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        Document document = null;
        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;
    }        


    /**
     * This method is called by UserTestToolMeg, everytime the Browser loads a 
     * page from host http://usertest.scone.de
     *
     * @param pageName The name of the page called
     */    
    public void pageCalled (String pageName) {
    }
    
    /**
     * This method is called by EventObserver, everytime the Browser loads a 
     * new page.
     *
     * @param access The Access object, containing information about the loaded page
     */    
    public void pageLoading (Access access) {  

        // To avoid mulitple calls because of multiple AccessEvents, start this method
        // only if the accessTime is new
        if (accessTime != access.getTime()) {
            accessTime = access.getTime();
            // Look for the usertest.scone.de page
            // if http://usertest.scone.de was called, start a new test
            if (access.getNode().toString().toLowerCase().equals("http://usertest.scone.de/") && !testRunning) {
                testRunning = true;
                testComponents = new HashMap();
                frameName = access.getParentFrameName() + access.getFrameName();
    
                // create list of existing xml test descriptions
    
                tw.displayTask(createTestChooser(ptss, this));
                tw.setTitle(labels.getString("testselecttitle"));
                tw.setVisible(true);
                // browserControl.blur(frameName); // Blur browser window
    
            
            }
            // web pages choosen by the test user
            if ((!access.getNode().toString().toLowerCase().startsWith("http://usertest.scone.de/")) && (testRunning)) {
                if ((access.getAction() & EventDecoder.LINK) > 0) {
                    currentState.addLinkEvent("click", access.getNode().toString(), access.getFrameName());
                }
                else if ((access.getAction() & EventDecoder.FRAGMENT) > 0) {
                    currentState.addLinkEvent("fragmentOnSamePage", access.getNode().toString(), access.getFrameName());
                }
                else if ((access.getAction() & EventDecoder.SELFLINK) > 0) {
                    currentState.addLinkEvent("samePage", access.getNode().toString(), access.getFrameName());
                }
                else if ((access.getAction() & EventDecoder.FORMSUBMIT) > 0) {
                    currentState.addLinkEvent("formSubmit", access.getNode().toString(), access.getFrameName());
                    Properties p = FormHelper.interpretFormData(unEscape(access.getPost()));
                    for (Enumeration e = p.keys() ; e.hasMoreElements() ;) {
                        String key = (String)e.nextElement();
                        currentState.addFormData(key+"="+p.get(key), access.getNode().toString(), access.getFrameName());
                    }
                    p = FormHelper.interpretFormData(access.getQuery());
                    for (Enumeration e = p.keys() ; e.hasMoreElements() ;) {
                        String key = (String)e.nextElement();
                        currentState.addFormData(key+"="+p.get(key), access.getNode().toString(), access.getFrameName());
                    }
    
                }
                else if ((access.getAction() & EventDecoder.RELOAD) > 0) {
                    currentState.addBrowserEvent("reload", access.getNode().toString(), access.getFrameName());
                }
                else if (((access.getAction() & EventDecoder.SKIPPED) > 0) && ((access.getAction() & EventDecoder.BACK) > 0)) {
                    currentState.addBrowserEvent("backSeveralPages", access.getNode().toString(), access.getFrameName());
                }
                else if (((access.getAction() & EventDecoder.SKIPPED) > 0) && ((access.getAction() & EventDecoder.NEXT) > 0)) {
                    currentState.addBrowserEvent("nextSeveralPages", access.getNode().toString(), access.getFrameName());
                }
                else if ((access.getAction() & EventDecoder.BACK) > 0) {
                    currentState.addBrowserEvent("back", access.getNode().toString(), access.getFrameName());
                }
                else if ((access.getAction() & EventDecoder.NEXT) > 0) {
                    currentState.addBrowserEvent("next", access.getNode().toString(), access.getFrameName());
                }
                else if ((access.getAction() & EventDecoder.NEWURL) > 0) {
                    currentState.addBrowserEvent("newUrl", access.getNode().toString(), access.getFrameName());
                }
            }            
        }
    }
    
    
    /**
     * This method returns the effect of the JavaScript escape() method. The
     * %-Character, followed by two hexadecimal digits will be converted to 
     * one character
     *
     * @param escaped The escaped string
     * @return The unescaped string
     */
    public String unEscape (String escaped) {
        
        String unescaped = "";
        int i = 0;
        while (i<escaped.length()) {
            if ((escaped.charAt(i) == '%') && ((i+2)<escaped.length())) {
                byte[] charValue = {37};
                try {
                    charValue[0] = (byte) Integer.parseInt(escaped.substring(i+1, i+3), 16);
                    i++;
                    i++;
                } catch (NumberFormatException e) {
                }
                unescaped += new String (charValue, 0,1);
            } else {
                unescaped += escaped.charAt(i);
            }
            i++;
        }
        return unescaped;
    }
    
                                
    
    
    
    public void prepareFirstTask (String testPerson, String xmlFile, int testNumber) {
        document = parseXmlFile(xmlFile);
//        userTestXml = document.getFirstChild();
    	
        // Look for the userTest element
        NodeList nList = document.getChildNodes();
        int i=0;        
        boolean found=false;
        while ((i < nList.getLength()) && (!found)) {
            if (nList.item(i).getNodeName().equals("userTest")) {
                found = true;
                userTestXml = nList.item(i);
            } else {
                i++;
            }
        }

        // create a new CurrentState object
        int numberOfTasks = document.getElementsByTagName("task").getLength();
        currentState = new CurrentState(testPerson, xmlFile, testNumber, numberOfTasks, ptss.getTestResultPath());
        currentState.setParentFrameAndFrameName(frameName);   // frameName is needed to choose the right browser window

        // add 
        File file = new File(currentState.getResultFilename());
        ptss.addToUnfinishedTests(file.getName().substring(0, file.getName().length()-4), numberOfTasks, 0);

        startNextTask();
    }        

    public void startNextTask () {

        if (currentState.hasMoreTasks()) {
            currentState.startNextTask();
            task = new Task(userTestXml, currentState.getCurrentTask(), this);
            currentState.setTaskName(task.getTaskName());
            if (tw != null) {
                tw.setTitle(task.getTitle());
                tw.setVisible(true);
                tw.displayTask(task.getTaskPanel());
            }
        } else {
            browserControl.openUri(currentState.getParentFrameAndFrameName(), "http://usertest.scone.de/testended"); 
            File file = new File(currentState.getResultFilename());
            ptss.removeFromUnfinishedTests(file.getName().substring(0, file.getName().length()-4));
            testRunning = false;
            tw.setVisible (false);
        }
        
    }        

    public void restartTest (String resultFile, int taskNumber) {
        currentState = new CurrentState(resultFile, ptss.getTestResultPath(), taskNumber);
        currentState.setParentFrameAndFrameName(frameName);   // frameName is needed to choose the right browser window
        
        document = parseXmlFile(currentState.getXmlFilename());
//        userTestXml = document.getFirstChild();

        // Look for the userTest element
        NodeList nList = document.getChildNodes();
        int i=0;        
        boolean found=false;
        while ((i < nList.getLength()) && (!found)) {
            if (nList.item(i).getNodeName().equals("userTest")) {
                found = true;
                userTestXml = nList.item(i);
            } else {
                i++;
            }
        }


        task = new Task(userTestXml, currentState.getCurrentTask(), this);
        currentState.setTaskName(task.getTaskName());
        if (tw != null) {
            tw.setTitle(task.getTitle());
            tw.setVisible(true);
            tw.displayTask(task.getTaskPanel());
        }
        
    }
    
    
    public void pageUnloading (Access access) {  
    }

    /**
     * This method is involked when a button inside the TaskWindow is clicked
     */    
    public void buttonClick(UTButton button) {
        currentState.addButtonClick(button.getName()); 
        performActions (button.getActionList());
           
    }
      
    /**
     * This method is involked when a TextEntryBox inside the TaskWindow is left with the return button 
     */    
    public void textFieldReturn(UTTextField textField) {
        // addToLog(timestamp, buttonName)
        // PerformActions (textField.getActionMap())
    }
 
    /**
     * This method reads out the actions of a clicked button and performs them
     *
     * @param actionList The List of actions of the clicked button 
     */    
    public void performActions(ArrayList actionList) {
        Action action = null;
        String parameter;
        
        for(int i=0; i<actionList.size(); i++) {
            action = (Action)actionList.get(i);
            parameter = "";
            try {
                // enable
                if (action.getCommand().equals("enable")) {
                    parameter = action.getParameter().trim();                  // get the name of the component that will be enabled
                    ((AllComponents)getComponentByName(parameter)).setComponentEnabled(true); // get component object and disable it
                }
                // disable
                if (action.getCommand().equals("disable")) {
                    parameter = action.getParameter().trim();                  // get the name of the component that will be disabled
                    ((AllComponents)getComponentByName(parameter)).setComponentEnabled(false); // get component object and disable it
                }
                // highlight
                if (action.getCommand().equals("highlight")) {
                    parameter = action.getParameter().trim();                  // get the name of the component that will be disabled
                    ((AllComponents)getComponentByName(parameter)).setComponentHighlighted(true); // get component object and disable it
                }
                // unhighlight
                if (action.getCommand().equals("unhighlight")) {
                    parameter = action.getParameter().trim();                  // get the name of the component that will be disabled
                    ((AllComponents)getComponentByName(parameter)).setComponentHighlighted(false); // get component object and disable it
                }
                // requestFocus
                if (action.getCommand().equals("requestFocus")) {
                    parameter = action.getParameter().trim();                  // get the name of the component that will be disabled
                    ((AllComponents)getComponentByName(parameter)).componentRequestFocus(); // get component object and disable it
                }
            } catch (NullPointerException e) {
                System.out.println("");
                System.out.println("Scone: UserTestTool");
                System.out.println("");
                System.out.println("The XML file that defines the current user test has an error.");
                System.out.println("Component: \""+parameter+"\", Command: \""+action.getCommand()+"\"");
                System.out.println("This component does not exist.");
                System.out.println("");
            } catch (Exception e) {
                ErrorLog.log(this, "performActions", "Error while performing actions of a component\n", e);
            }                    


            // openUri
            if (action.getCommand().equals("openUri")) {
                parameter = action.getParameter().trim();      
                if (parameter.toLowerCase().equals("_blank")) {      // to show a blank web page, let the browser call usertest.scone.de/blank
                    parameter = "http://usertest.scone.de/blank";
                }
                if  (parameter.indexOf("://") < 0) {                // add http:// if no protocol is selected. Without a protocol string the page would not load
                    parameter = "http://" + parameter;  
                }
                currentState.addBrowserControlEvent("openUri", parameter, currentState.getParentFrameAndFrameName());   // create Entry in result file
                browserControl.openUri(currentState.getParentFrameAndFrameName(), parameter); // open a new Url in browser
            }
            // javaScriptCommand
            if (action.getCommand().equals("javaScriptCommand")) {
                parameter = action.getParameter().trim();      
                currentState.addBrowserControlEvent("javaScriptCommand", parameter, currentState.getParentFrameAndFrameName());   // create Entry in result file
                browserControl.javaScriptCommand(currentState.getParentFrameAndFrameName(), parameter); // execute javaScript command
            }
            // browserToFront: Bring browser Window to front (in focus)
            if (action.getCommand().equals("browserToFront")) {
                currentState.addBrowserControlEvent("browserToFront", "", currentState.getParentFrameAndFrameName());   // create Entry in result file
                browserControl.bringToFront(currentState.getParentFrameAndFrameName()); // open a new Url in browser
            }
            // browserResize: Resize browser Window
            if (action.getCommand().equals("browserResize")) {
                parameter = action.getParameter().trim();      
                int width = 640;
                int height = 480;
                try {
                    width = Integer.parseInt(parameter.substring(0, parameter.indexOf("x")));
                } catch (Exception e) {
                }
                try {
                    height = Integer.parseInt(parameter.substring(parameter.indexOf("x")+1, parameter.length()));
                } catch (Exception e) {
                }
                currentState.addBrowserControlEvent("browserResize", parameter, currentState.getParentFrameAndFrameName());   // create Entry in result file
                browserControl.resize(currentState.getParentFrameAndFrameName(), width, height); // open a new Url in browser
            }
            // moveBrowserWindow
            if (action.getCommand().equals("moveBrowserWindow")) {
                parameter = action.getParameter().trim();      
                int x = 0;
                int y = 0;
                try {
                    x = Integer.parseInt(parameter.substring(0, parameter.indexOf("x")));
                } catch (Exception e) {
                }
                try {
                    y = Integer.parseInt(parameter.substring(parameter.indexOf("x")+1, parameter.length()));
                } catch (Exception e) {
                }
                currentState.addBrowserControlEvent("moveBrowserWindow", parameter, currentState.getParentFrameAndFrameName());   // create Entry in result file
                browserControl.moveBrowserWindow(currentState.getParentFrameAndFrameName(), x, y); // move browser window
            }
            // startNextTask
            if (action.getCommand().equals("startNextTask")) {
                
                // Read out all components the user could create text (UTTextField or UTLickertScale)
                Object comp = null;
                String compName = null;
                ArrayList compList = task.getComponents();
                // test if all required fields (like text fields, comboBoxes...) were changed by the User

                boolean userUsedRequiredComponents = true;
                for (int j = 0; j < compList.size(); j++) {
                    comp = compList.get(j);
                    compName = ((AllComponents)comp).getComponentName();

                    if (isInputComponent(comp)) {
                        if (!((InputComponent)comp).changedByUser()) { // input component (like textField) was not changed by the user, but should have
                            userUsedRequiredComponents = false;
                            JOptionPane.showMessageDialog(tw, labels.getString("enterallfields"), 
                                "Scone: UserTestTool", JOptionPane.PLAIN_MESSAGE);
                        }
                    }
                }

                if (userUsedRequiredComponents) {
                    // after all required field were changed by the user, ask every Input comonent for the UserInput
                    for (int j = 0; j < compList.size(); j++) {
                        comp = compList.get(j);
                        compName = ((AllComponents)comp).getComponentName();
    
                        if (isInputComponent(comp)) {
                            ((InputComponent)comp).startDataQuery();
                            while (((InputComponent)comp).hasMoreData()) {
                                String tagname = ((InputComponent)comp).getTagName();
                                String actionData = ((InputComponent)comp).getData();
                                String timeStamp = ((InputComponent)comp).getTimeStamp();
                                currentState.addData(tagname, actionData, compName, timeStamp);
                                ((InputComponent)comp).getNextData();
                            }
                        }
                        // Insert other data components here
                    }
                    
                    // Daten aus Aufgabenfenster auslesen und in die xml-datei Schreiben
                    currentState.endTask();   
                    currentState.writeXmlResults();
                    currentState.writeTabDevidedResults();
                    File file = new File (currentState.getResultFilename());
                    ptss.addToUnfinishedTests(file.getName().substring(0, file.getName().length()-4), currentState.getNumberOfTasks(), currentState.getCompletedTasks());
                    startNextTask();
                }
                    
                return;
            }
            // cancelTest
            if (action.getCommand().equals("cancelTest")) {
                cancelTest();               
            }
            // stopWatchStart
            if (action.getCommand().equals("stopWatchStart")) {
                parameter = action.getParameter().trim();
                testComponents.put(parameter, new StopWatch());                
            }
            // stopWatchElapsed
            if (action.getCommand().equals("stopWatchElapsed")) {
                parameter = action.getParameter().trim();
                if (testComponents.get(parameter) != null) {
                     currentState.addStopWatch("elapsedTime", parameter, ((StopWatch)testComponents.get(parameter)).getElapsedTime());
                }
            }
            // stopWatchLapTime 
            if (action.getCommand().equals("stopWatchLapTime")) {
                parameter = action.getParameter().trim();
                if (testComponents.get(parameter) != null) {
                     currentState.addStopWatch("lapTime", parameter, ((StopWatch)testComponents.get(parameter)).getLapTime());
                }
            }
        }
    }
    

    protected boolean isInputComponent (Object comp) { 
        Class[] classes = comp.getClass().getInterfaces();
        for (int y=0; y<classes.length; y++) {
            if (classes[y].getName().equals("scone.usertesttool.InputComponent")) {
                return true;
            }
        }
        return false;
    }    

    protected Object getComponentByName (String compName) {
            
        ArrayList comps = task.getComponents();
        int i = 0;
        while (i < comps.size()) {
            if (((AllComponents)comps.get(i)).getComponentName().equals(compName)) {
                return comps.get(i);
            }
            i++;
        }
        return null;
    }
    
    /**
     * cancelTest() is involked by TaskWindow when the user closed the task window
     */
    public void cancelTest() {
        if (currentState != null) {
            browserControl.openUri(currentState.getParentFrameAndFrameName(), "http://usertest.scone.de/testcanceled"); 
        }
        testRunning = false;
        tw.setVisible (false);
    }
                

    /**
     * This method creates a TaskPanel that contains the test chooser and the
     * chooser for unfinished Tests
     *
     * @param ptss PersistentTestStateStore object that contains the states of finished 
     *              unfinished tests and a list of valid test description xml
     *              files.
     * @param utc The UserTestControl object that receives the button clicks
     *              of this Panel
     * @return The TaskPanel containing the test chooser
     */
    public TaskPanel createTestChooser (PersistentTestStateStore ptss, UserTestControl utc) {
        
        final UserTestControl _utc = utc;
        final PersistentTestStateStore _ptss = ptss; 

        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        
        final TaskPanel panel = new TaskPanel();
        panel.setLayout(gridbag);
        
        c.insets = new Insets(2,5,2,5);  //top padding

        JLabel label = new JLabel("Scone User Test Tool");
        label.setFont(UIStyle.VERYBIGBOLDFONT);
        c.gridx = 0;
        c.gridy = 0;
        c.gridwidth = 2;
        c.ipady = 40;      //make this component tall
//        c.weightx = 0.0;
        c.anchor = GridBagConstraints.CENTER;
        c.fill = GridBagConstraints.NONE;
        gridbag.setConstraints(label, c);
        panel.add(label);

        label = new JLabel(labels.getString("newtest"));
        label.setFont(UIStyle.BIGBOLDFONT);
        c.gridx = 0;
        c.gridy = 1;
        c.gridwidth = 2;
        c.ipady = 40;
//        c.weightx = 0.0;
        c.anchor = GridBagConstraints.WEST;
        gridbag.setConstraints(label, c);
        panel.add(label);

        label = new JLabel(labels.getString("testdescription"));
        label.setFont(UIStyle.PLAINFONT);
        c.gridx = 0;
        c.gridy = 2;
        c.gridwidth = 1;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.anchor = GridBagConstraints.WEST;
        c.fill = GridBagConstraints.NONE;
        gridbag.setConstraints(label, c);
        panel.add(label);

        final JComboBox descriptionList = new JComboBox(_ptss.getTestDescriptions());
        c.gridx = 1;
        c.gridy = 2;
        c.gridwidth = 1;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.fill = GridBagConstraints.HORIZONTAL;
        gridbag.setConstraints(descriptionList, c);
        panel.add(descriptionList);

        label = new JLabel(labels.getString("testno"));
        label.setFont(UIStyle.PLAINFONT);
        c.gridx = 0;
        c.gridy = 3;
        c.gridwidth = 1;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.anchor = GridBagConstraints.WEST;
        c.fill = GridBagConstraints.NONE;
        gridbag.setConstraints(label, c);
        panel.add(label);

        final JTextField testNumber = new JTextField(Integer.toString(_ptss.getLastTestNumberFor((String)descriptionList.getSelectedItem())+1));
        c.gridx = 1;
        c.gridy = 3;
        c.gridwidth = 1;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.fill = GridBagConstraints.HORIZONTAL;
        gridbag.setConstraints(testNumber, c);
        panel.add(testNumber);

        descriptionList.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JComboBox cb = (JComboBox)e.getSource();
                testNumber.setText(Integer.toString(_ptss.getLastTestNumberFor((String)cb.getSelectedItem())+1));
            }
        });

        label = new JLabel(labels.getString("participantname"));
        label.setFont(UIStyle.PLAINFONT);
        c.gridx = 0;
        c.gridy = 4;
        c.gridwidth = 1;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.anchor = GridBagConstraints.WEST;
        c.fill = GridBagConstraints.NONE;
        gridbag.setConstraints(label, c);
        panel.add(label);

        final JTextField testPerson = new JTextField();
        c.gridx = 1;
        c.gridy = 4;
        c.gridwidth = 1;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.fill = GridBagConstraints.HORIZONTAL;
        gridbag.setConstraints(testPerson, c);
        panel.add(testPerson);

        JButton button = new JButton(labels.getString("startnewtest"));
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent actionEvent) {
                int numberOfTest=0;
                boolean isNumber = true;
                testPerson.setText(testPerson.getText().trim());
                try {
                    numberOfTest = Integer.parseInt(testNumber.getText());
                } catch (NumberFormatException e) {
                    isNumber = false;
                }
                if (testPerson.getText().equals("")) {
                    JOptionPane.showMessageDialog(panel, labels.getString("entername"), 
                        "Scone: UserTestTool", JOptionPane.PLAIN_MESSAGE);
                    testPerson.requestFocus();                   
                } else if (!isNumber) {
                    JOptionPane.showMessageDialog(panel, labels.getString("numbererror"), 
                        "Scone: UserTestTool", JOptionPane.PLAIN_MESSAGE);
                    testNumber.requestFocus();                   
                } else {
                    String filename = _ptss.getTestResultPath()+(String)descriptionList.getSelectedItem()+".";
                    if (numberOfTest < 1000) { filename += "0";}
                    if (numberOfTest < 100) { filename += "0";}
                    if (numberOfTest < 10) { filename += "0";}
                    filename += Integer.toString(numberOfTest)+"."+testPerson.getText()+".xml";
                    File file = new File (filename);
                    if (file.exists()) {
                        String backupEnding = ".001";
                        int endNum = 1;
                        File renamedFile = new File (filename+backupEnding);
                        while (renamedFile.exists() && endNum < 1000) {
                            endNum++;
                            if (endNum < 10) { backupEnding = ".00"+Integer.toString(endNum);}
                            else if (endNum < 100) { backupEnding += ".0"+Integer.toString(endNum);}
                            renamedFile = new File (filename+backupEnding);
                        }
                        Object[] options = {labels.getString("renamecontinue"),labels.getString("alterentry")};
                        int n = JOptionPane.showOptionDialog(panel, labels.getString("outputfileexists1")+"\n"+
                                "\n"+filename+labels.getString("outputfileexists2")+"\n"+
                                "\n"+filename+backupEnding+labels.getString("outputfileexists3")+"\n"+
                                labels.getString("outputfileexists4"),
                                "Scone: UserTestTool", JOptionPane.YES_NO_OPTION, 
                                JOptionPane.QUESTION_MESSAGE, null, options, options[0]);   
                        if (n == JOptionPane.YES_OPTION) {
                            file.renameTo(renamedFile);    
                            if (_ptss.getLastTestNumberFor((String)descriptionList.getSelectedItem()) < numberOfTest) {
                                _ptss.setLastTestNumberFor((String)descriptionList.getSelectedItem(), numberOfTest);
                            }
                            _utc.prepareFirstTask(testPerson.getText(), _ptss.getTestDescriptionPath()+(String)descriptionList.getSelectedItem()+".xml", numberOfTest);
                        }
                    } else {
                        if (_ptss.getLastTestNumberFor((String)descriptionList.getSelectedItem()) < numberOfTest) {
                            _ptss.setLastTestNumberFor((String)descriptionList.getSelectedItem(), numberOfTest);
                        }
                        _utc.prepareFirstTask(testPerson.getText(), _ptss.getTestDescriptionPath()+(String)descriptionList.getSelectedItem()+".xml", numberOfTest);
                    }
                }
                
            }
        }
        );
        c.gridx = 1;
        c.gridy = 5;
        c.gridwidth = 1;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.anchor = GridBagConstraints.EAST;
        c.fill = GridBagConstraints.NONE;
        gridbag.setConstraints(button, c);
        panel.add(button);
        
        label = new JLabel(labels.getString("continueaborted"));
        label.setFont(UIStyle.BIGBOLDFONT);
        c.gridx = 0;
        c.gridy = 6;
        c.gridwidth = 2;
        c.ipady = 40;
//        c.weightx = 0.0;
        c.anchor = GridBagConstraints.WEST;
        c.fill = GridBagConstraints.NONE;
        gridbag.setConstraints(label, c);
        panel.add(label);
        
        String[] unfinished = ptss.getUnfinishedTests();
        for (int i=0; i<unfinished.length;i++) {
            unfinished[i] += " (" +ptss.getCompletedTasksFromUnfinishedTest(unfinished[i])+ " " + labels.getString("of") +
                            ptss.getNumberOfTasksFromUnfinishedTest(unfinished[i]) + labels.getString("taskssolved") + ")";
        }
        final JComboBox comboBox = new JComboBox(unfinished);
        if (unfinished.length == 0) {
            comboBox.addItem(labels.getString("nocancelled"));        
        }
        c.gridx = 0;
        c.gridy = 7;
        c.gridwidth = 2;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.fill = GridBagConstraints.HORIZONTAL;
        gridbag.setConstraints(comboBox, c);
        panel.add(comboBox);

        label = new JLabel(labels.getString("starttask"));
        label.setFont(UIStyle.PLAINFONT);
        c.gridx = 0;
        c.gridy = 8;
        c.gridwidth = 1;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.anchor = GridBagConstraints.WEST;
        c.fill = GridBagConstraints.NONE;
        gridbag.setConstraints(label, c);
        panel.add(label);

        final JComboBox taskNames = new JComboBox();
        if (unfinished.length > 0) {
            taskNames.removeAllItems();
            Object[] nameArray;
            nameArray = _ptss.getTaskNamesForResumableTest(0);
            for (int i=0; i<nameArray.length; i++) {
                taskNames.addItem(nameArray[i]);
            }
        }            
        c.gridx = 1;
        c.gridy = 8;
        c.gridwidth = 1;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.fill = GridBagConstraints.HORIZONTAL;
        gridbag.setConstraints(taskNames, c);
        panel.add(taskNames);

        if (unfinished.length > 0) {
            comboBox.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    JComboBox cb = (JComboBox)e.getSource();
                    taskNames.removeAllItems();
                    Object[] nameArray;
                    nameArray = _ptss.getTaskNamesForResumableTest(cb.getSelectedIndex());
                    for (int i=0; i<nameArray.length; i++) {
                        taskNames.addItem(nameArray[i]);
                    }
                }
            });
        }

        button = new JButton(labels.getString("continuetest"));
        if (unfinished.length > 0) {
            button.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent actionEvent) {
                    int iTaskNumber = 0;
                    boolean isNumber = false;
                    try {
                        iTaskNumber = taskNames.getSelectedIndex()+1;
                        isNumber = true;
                    } catch (NumberFormatException e) {
                    }
                    if (!isNumber) {
                        JOptionPane.showMessageDialog(panel, labels.getString("enterwholeno"), 
                            "Scone: UserTestTool", JOptionPane.PLAIN_MESSAGE);
                        taskNames.requestFocus();                   
                    } else if ((iTaskNumber > _ptss.getCompletedTasksFromUnfinishedTest(comboBox.getSelectedIndex())+1) || (iTaskNumber < 1)) {
                        JOptionPane.showMessageDialog(panel, labels.getString("enter1toN1")+" "+
                            _ptss.getCompletedTasksFromUnfinishedTest(comboBox.getSelectedIndex()+1)+
                            " "+labels.getString("enter1toN2"), 
                            "Scone: UserTestTool", JOptionPane.PLAIN_MESSAGE);
                        taskNames.requestFocus();                   
                    } else {
                        _utc.restartTest(_ptss.getTestResultPath()+_ptss.getUnfinishedTests()[comboBox.getSelectedIndex()]+".xml", iTaskNumber);
                    }
                }
            }
            );
        }            
        c.gridx = 1;
        c.gridy = 9;
        c.gridwidth = 1;
        c.ipady = 0;
//        c.weightx = 0.0;
        c.anchor = GridBagConstraints.EAST;
        c.fill = GridBagConstraints.NONE;
        gridbag.setConstraints(button, c);
        panel.add(button);

        return panel;
    }


    private String getText (Node node) {
        String textString = "";
        org.w3c.dom.NodeList nList = node.getChildNodes();
        for (int i = 0; i<nList.getLength(); i++) {
            if (((org.w3c.dom.Node)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;
    }


    public TaskWindow getTaskWindow() {
        return tw;
    }
    

}
