package evaluator.util;


import java.awt.Component;
import java.awt.Dimension;
import java.util.Date;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.Vector;

import javax.swing.Box;

import scone.Plugin;
import scone.accesstracking.AppletConnector;
import scone.accesstracking.FrameAccess;
import scone.netobjects.AccessCache;
import scone.netobjects.AccessEvent;
import scone.netobjects.HtmlNode;
import scone.netobjects.HtmlNodeCache;
import scone.util.AlertBox;


/**
 * ExperimentController class
 * <p>Description: This class is used to store the data for an experiment,
 * that is, the subject number, the tasks and questions, and the results.
 * It also computes the correct task sequence.</p>
 * <p>Copyright: Copyright (C) 2003</p>
 * <p>Company: University of Hamburg, Germany</p>
 * @author Hartmut Obendorf, http://asi-www.informatik.uni-hamburg.de/personen/obendorf/
 @author Harald Weinreich, http://vsis-www.informatik.uni-hamburg.de/~weinreic/
 * @version 1.5.0.0
 */

public class ExperimentController implements Observer {
    private EvaluatorFrame         _evaluatorFrame;
    private Plugin                 _plugin;
    private Long                   _participantNumber;
    private Long                   _experimentIndex;
    private TaskGroupList          _taskGroupList;
    private int                    _currentTask;
    private int                    _currentTaskGroup;
    // There can be only one!
    private static ExperimentOutputWriter _experimentOutputWriter;

    public ExperimentController(EvaluatorFrame evaluatorFrame, Plugin plugin) {
        _evaluatorFrame = evaluatorFrame;
        _plugin = plugin;

        _currentTask = 999999;
        _experimentIndex = new Long(0);
        _participantNumber = new Long(evaluator.util.ExperimentOutputWriter.getNextParticipantNumber(_experimentIndex.longValue()));
        _taskGroupList = new TaskGroupList();
    }

    public void addTaskGroup(TaskGroup taskGroup) {
        _taskGroupList.addTaskGroup(taskGroup);
    }

    public void run() {
        _currentTaskGroup = 0;

        _experimentOutputWriter = new ExperimentOutputWriter(_participantNumber.longValue(),
                _experimentIndex.longValue());
        // _experimentOutputWriter.write("Test: " + System.currentTimeMillis());

        startTask();
    }

    public void nextTask() {
        _currentTask++;

        if (isDone()) {
            _experimentOutputWriter.close();
            _evaluatorFrame.close();
        } else if (taskIsDone()) {
            stopTask();
            _currentTask = -1;
            _currentTaskGroup++;
            nextTask();
        } else {
            TaskGroup taskGroup = _taskGroupList.getTaskGroup(_currentTaskGroup);
            Component task = taskGroup.getTask(_currentTask);
            Box       box = Box.createVerticalBox();

            // page counter
            Box countBox = Box.createHorizontalBox();

            countBox.add(Box.createRigidArea(new Dimension(40, 0)));
            // javax.swing.JLabel label = new javax.swing.JLabel("Screen " + (calculateCurrentPage()+1) +
            // "/" + calculateLength());
            javax.swing.JLabel label;

            if (_currentTaskGroup > 0) {
                label = new javax.swing.JLabel("Aufgabe " + (_currentTaskGroup) + " von " + (_taskGroupList.size() - 1));
            } else {                                                    
                label = new javax.swing.JLabel(" ");
            }
            label.setForeground(java.awt.Color.black);
            countBox.add(label);
            countBox.add(Box.createHorizontalGlue());
            box.add(countBox);

            box.add(Box.createRigidArea(new Dimension(0, 30)));

            // the text and the buttons
            Box itemBox = Box.createHorizontalBox();

            itemBox.add(Box.createRigidArea(new Dimension(40, 0)));
            itemBox.add(task);
            itemBox.add(Box.createHorizontalGlue());
            box.add(itemBox);

            box.add(Box.createRigidArea(new Dimension(0, 30)));

            // codefield
            Box codeBox = Box.createHorizontalBox();

            codeBox.add(Box.createRigidArea(new Dimension(40, 0)));
            javax.swing.JLabel codelabel = new javax.swing.JLabel("pagecode: " + calculateCurrentPage() + "." // + lastNumberOfVisits_ + "."+lastDuration_
                    + ".3 ");// +"gr:"+ConfigMain.getCurrentConfig().CURRENT_STUDY_GROUP+" meet:"+ConfigMain.getCurrentConfig().MEETING);

            codelabel.setForeground(java.awt.Color.gray);
            codelabel.setFont(evaluator.util.UIToolbox.SMALLFONT);
            codeBox.add(codelabel);
            codeBox.add(Box.createHorizontalGlue());
            box.add(codeBox);

            javax.swing.JPanel boxcapsule = new javax.swing.JPanel();

            boxcapsule.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(153, 153, 204), 1));
            boxcapsule.setAlignmentX(Component.CENTER_ALIGNMENT);

            javax.swing.JPanel capsule = new javax.swing.JPanel();

            java.awt.Container contentPane = _evaluatorFrame.getContentPane();

            contentPane.removeAll();
            contentPane.setLayout(new javax.swing.BoxLayout(contentPane, javax.swing.BoxLayout.Y_AXIS));
            contentPane.add(Box.createHorizontalGlue());
            capsule.setAlignmentX(Component.CENTER_ALIGNMENT);
            boxcapsule.add(box);
            capsule.add(Box.createHorizontalGlue());
            capsule.add(boxcapsule);
            capsule.add(Box.createHorizontalGlue());
            contentPane.add(capsule);
            contentPane.add(Box.createHorizontalGlue());

            contentPane.validate();
            // contentPane.repaint();
        }
    }

    public void startTask() {
        // _experimentOutputWriter.write("taskGroup " + _currentTaskGroup + " started: " + time);

        _currentTask = -1;

        nextTask();
    }

    /**
     * Stops the current taskGroup and advances to the next.
     * All DataSources of the current taskGroup are acquired in a
     * list and written to disk.
     * @param time the time in ms when taskGroup stopped
     */

    public void stopTask() {
        // _experimentOutputWriter.write("taskGroup " + _currentTaskGroup + " ended: " + time);

        TaskGroup taskGroup = _taskGroupList.getTaskGroup(_currentTaskGroup);

        for (int task = 0; task < taskGroup.size(); task++) {
            DataSourceInterface[] sources = getDataSources(taskGroup.getTask(task));

            _experimentOutputWriter.write("STEP;" + _currentTaskGroup + "," + task);
            if (sources != null) {
                for (int i = 0; i < sources.length; i++) {
                    _experimentOutputWriter.write(sources[i]);
                }
            }
            
        }
    }
  
    /**
     * This method is used to control the browser. The following commands are being accepted:
     * <UL>
     * <LI>OpenURL<BR>
     * Opens a new URL in the open Browser-Window.
     *
     * <LI>BringToFront<BR>
     * With second parameter: Load URL and bring browser window to front. <BR>
     * Attention: <BR> allowStealingFocus.reg has to be installed and Windows to be restarted!
     *
     * <LI>BringToFront<BR>
     * Second parameter is empty String: Bring browser window to front. 
     * Attention: <BR> allowStealingFocus.reg has to be installed and Windows to be restarted!
     *
     * <LI>BlankPage<BR>
     * Show blank page in Browser with AccessTracking still enabled. Use this instead of "about:blank" !
     *
     * <LI>Maximize<BR>
     * Maximize Browser Window.
     *
     * <LI>CloseBrowser<BR>
     * Closes all windows of Browser.
     *
     *</UL>
     *
     * @param command The command to invoce in the browser: OpenURL, BringToFront, BlankPage
     * @param parameter An optional second parameter.
     */
    public void browserControl(String command, String parameter) {
        // get the FrameAccess-Instance
        FrameAccess fa = FrameAccess.getInstance();
        // get valid frame Names
        Set frameNames = fa.getSetOfContents();
        Iterator myIterator;
        String firstFrameName = "";
        boolean hasNext = false;
        int i = 0;

        do {
            myIterator = frameNames.iterator();
            hasNext = myIterator.hasNext();
            if (hasNext || command.equalsIgnoreCase("CloseBrowser")) {
                firstFrameName = (String) myIterator.next();
            } else {
                AlertBox a = new AlertBox(_evaluatorFrame, new String[] {"Achtung!", "Bitte starten Sie einen Browser, laden Sie eine beliebige Seite und klicken danach OK."});
            }
            i++;
        } while (hasNext == false && i <= 2);  // Twice is enough!
        // get a reference of AppletConnector by sending the frameName
        AppletConnector ac = fa.get(firstFrameName);

        // if ac is still active, call sendToApplet
        if (ac != null) {
            // use OpenUrl like this:
            // ac.sendToApplet("OpenURL", "http://www.yahoo.de", "_self");
            // ac.sendToApplet("BringToFront", "", "");   // first bring to front
            if (command.equalsIgnoreCase("OpenURL")) {
                ac.sendToApplet("OpenURL", parameter, "_top");       // change URL in TOP frame
            } else if (command.equalsIgnoreCase("BringToFront")
                    && parameter.length() != 0) {
                ac.sendToApplet("BringToFront", "", "");              // First bring to Front...
                ac.sendToApplet("OpenURLDelayed", parameter, "");     // then change the URL
            } else if (command.equalsIgnoreCase("BringToFront")) {
                ac.sendToApplet("BringToFront", "", "");
            } else if (command.equalsIgnoreCase("BringToFront")) {
                ac.sendToApplet("BringToFront", "", "");
            } else if (command.equalsIgnoreCase("BlurBrowser")) {
                ac.sendToApplet("BlurBrowser", "", "");
            } else if (command.equalsIgnoreCase("Maximize")) {
                ac.sendToApplet("anyFunction", "window.moveTo(0,0); window.resizeTo(screen.width-1,screen.height-30);", "");
            } else if (command.equalsIgnoreCase("BlankPage")) {
                ac.sendToApplet("OpenURL", "http://blank.scone.de", "_top");    // change the URL in top frame!
            } else if (command.equalsIgnoreCase("CloseBrowser")) {
                ac.sendToApplet("anyFunction", "history.go(-999); window.close();", "");    // then change the URL
            } else {
                System.out.println("ExperimentController.browserControl() -> Unknown command: " + command);
            }
        
        }
    }

    /**
     * Close this expoeriment and stop Scone.
     */
    public void close() {
        _evaluatorFrame.close();
    }

    private DataSourceInterface[] getDataSources(java.awt.Component component) {
        Vector dataSources = new Vector();

        recursivelyCheckComponents(component, dataSources);

        if (dataSources.size() > 0) {
            return (DataSourceInterface[]) dataSources.toArray(new DataSourceInterface[1]);
        }

        return null;
    }

    private void recursivelyCheckComponents(java.awt.Component parentComponent, Vector dataSources) {
        if (parentComponent instanceof java.awt.Container) {
            java.awt.Container container = (java.awt.Container) parentComponent;

            for (int c = 0; c < container.getComponentCount(); c++) {
                java.awt.Component component = container.getComponent(c);

                testForDataSourceInterface(component, dataSources);
                recursivelyCheckComponents(component, dataSources);
            }
        }
    }

    private void testForDataSourceInterface(java.awt.Component component, Vector dataSources) {
        Class cls = component.getClass();
        Class[] interfaces = cls.getInterfaces();

        // System.err.println();
        // System.err.println(cls.getName());
        for (int i = 0; i < interfaces.length; i++) {
            // System.err.println(interfaces[i].getName());
            if (interfaces[i].getName().equals(evaluator.util.DataSourceInterface.class.getName())) {
                dataSources.add(component);
            }
        }
    }

    private boolean isDone() {
        return (_currentTaskGroup >= _taskGroupList.size());
    }

    private boolean taskIsDone() {
        if (isDone()) {
            return true;
        }

        return (_currentTask
                >= _taskGroupList.getTaskGroup(_currentTaskGroup).size());
    }

    public long getSubjectNumber() {
        return _participantNumber.longValue();
    }

    protected void setSubjectNumber(long subjectNumber) {
        _participantNumber = new Long(subjectNumber);
    }

    private int calculateCurrentPage() {
        TaskGroup taskGroup;
        int  currentPage;

        currentPage = 0;
        for (int t = 0; t < _currentTaskGroup; t++) {
            taskGroup = _taskGroupList.getTaskGroup(t);
            currentPage += taskGroup.size();
        }
        currentPage += _currentTask;

        return currentPage;
    }

    private int calculateLength() {
        TaskGroup taskGroup;
        int  length;

        length = 0;
        for (int t = 0; t < _taskGroupList.size(); t++) {
            taskGroup = _taskGroupList.getTaskGroup(t);
            length += taskGroup.size();
        }

        return length;
    }
  
    /**
     * Log the browser events.<P>
     * @param o It can be assumed, that o is an instance of a scone.netobjects.XXXCache-Class is.
     * @param arg is the accessevent.
     */
    public void update(Observable o, Object arg) {
        String action = null;

        if (o instanceof AccessCache) {
            AccessEvent e = (AccessEvent) arg;

            if (e.getNode() == null) {
                System.out.println("OT: Unknown NetNode!");
            } else {
                HtmlNode hNode = HtmlNodeCache.check(e.getNode());

                if (hNode == null) {
                    System.out.println("OT: AccessEvent to unknown or non-HTML-page:" + e.getNode().toString());
                } else {
                    if (e.getAccess().getAction() == 0) {  // First Event - Action unknown
                        // System.out.print  ("\nOT: "+e.getUser().getUserName());
                        // System.out.println(" accessed "+e.getNode().toString());
                        // System.out.println("  Title : "+hNode.getTitle());
                    } else {
                        if (e.getAccess().getStayTime() == 0) {
                            action = "SWWW;" + e.getAccess().getTime() + ";"
                                    + e.getAccess().getFrameName() + ";"
                                    + e.getNode().toString() + ";"
                                    + hNode.getTitle() + ";";
                            if ((e.getAccess().getAction() & 1 << 0) > 0) {
                                action += "link,";
                            }
                            if ((e.getAccess().getAction() & 1 << 1) > 0) {
                                action += "reference,";
                            }
                            if ((e.getAccess().getAction() & 1 << 2) > 0) {
                                action += "samePage,";
                            }
                            if ((e.getAccess().getAction() & 1 << 3) > 0) {
                                action += "submit,";
                            }
                            if ((e.getAccess().getAction() & 1 << 4) > 0) {
                                action += "reloaded,";
                            }
                            if ((e.getAccess().getAction() & 1 << 5) > 0) {
                                action += "back,";
                            }
                            if ((e.getAccess().getAction() & 1 << 6) > 0) {
                                action += "next,";
                            }
                            if ((e.getAccess().getAction() & 1 << 7) > 0) {
                                action += "multi,";
                            }
                            if ((e.getAccess().getAction() & 1 << 8) > 0) {
                                action += "new window,";
                            }
                            if ((e.getAccess().getAction() & 1 << 9) > 0) {
                                action += "bookmark";
                            }
                            // System.out.println(action);
                            _experimentOutputWriter.write(action);
                        } else {
                            // System.out.println("  Stay  : "+e.getAccess().getStayTime()/1000+"s");  // liefert immer 0!?
                            action = "FWWW;"
                                    + (e.getAccess().getTime()
                                            + e.getAccess().getStayTime())
                                    + ";"
                                    + e.getAccess().getFrameName()
                                    + ";"
                                    + e.getNode().toString()
                                    + ";"
                                    + e.getAccess().getStayTime()
                                    + "ms";
                            _experimentOutputWriter.write(action);
                        }
                      
                    }
                
                }
            }
        }
    }

    /**
     * Log user actions...<P>
     * @see experiment.util.ExperimentController#logMessage
     * @param parameter
     */
    public static void logMessage(String param) {
        String action = null;

        action = "ACTION;" + (new Date()).getTime() + ";" + param;  // Other parameters are useless.
        // System.out.println(action);
        _experimentOutputWriter.write(action);
    }

}

