/*
 * Scone - The Web Enhancement Framework
 * Copyright (C) 2009 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.accesstracking;


import java.lang.reflect.Method;
import java.util.Date;

import scone.netobjects.Access;
import scone.netobjects.AccessCache;
import scone.netobjects.AccessEvent;
import scone.netobjects.Link;
import scone.netobjects.LinkCache;
import scone.netobjects.NetNode;
import scone.netobjects.NetNodeCache;
import scone.util.ErrorLog;


/**
 * The methods of this class are called by AppletConnector. Every
 * message of the applet has its own method to evaluate the received
 * information and to create and change Access events.
 *
 * @author Torsten Hass
 * @author Harald Weinreich
 * @version 1.1, 22-Feb-2002
 */

/* Removed Bugs and build in features
 11/5/2001   Removed bug: Reload is now working with frames
 11/6/2001   Removed bug: Applet is invisible now
 11/6/2001   added feature: FrameAccessTestGui can be disabled in
 Plugin configuration now
 11/7/2001   added feature: BROWSER_NS or BROWSER_IE are added to Action
 11/7/2001   Accesstracking speedup by disable creation of TextArea
 22.2.2002   logMessage added. Used e.g. for scone.Evaluator.
 */

public class EventDecoder {
    // constants for bit array action, indicating the action
    // the user performed to get to this page
    public final static int LINK = 1 << 0;          // user clicked a link
    public final static int FRAGMENT = 1 << 1;      // link was a reference link
    public final static int SELFLINK = 1 << 2;      // link pointed to itself
    public final static int FORMSUBMIT = 1 << 3;    // user submited a form
    public final static int RELOAD = 1 << 4;        // page was reloaded by button or by typing url
    public final static int BACK = 1 << 5;          // user clicked the back button
    public final static int NEXT = 1 << 6;          // user clicked the next button
    public final static int SKIPPED = 1 << 7;    // user jumped more than one step back or next
    public final static int NEWWINDOW = 1 << 8;     // new browser window
    public final static int BOOKMARK = 1 << 9;      // user clicked a bookmark (netscape only)
    public final static int NEWURL = 1 << 15;       // user typed in a new url or selected from bookmarks (IE)
    public final static int BROWSER_NS = 1 << 27;   // browser is Netscape
    public final static int BROWSER_IE = 1 << 28;   // browser is Internet Explorer
    public final static int BROWSER_GECKO = 1 << 29;   // browser is Mozilla or Firefox
    public final static int HEURISTIC = 1 << 31;    // a heuristic was used to get this value
    // bookmark was used with IE or 
    // unknown way of calling a page

    // constants for type of browser
    private final int OTHER = 0;  // unknown browser type
    private final int EXPLORER = 1;  // Microsoft Internet Explorer
    private final int NETSCAPE = 2;  // Netscape

    private BrowserHistory history;
    private int browserName;
    private boolean accessEventExisted;
    private boolean showMessages;

    public EventDecoder() {
        // get the BrowserHistory object
        history = BrowserHistory.getInstance();
        accessEventExisted = false;
        browserName = OTHER;
    }

    /**
     * This method is called when LogApplet was started and sent it's
     * first information. With that information an AccessEvent is
     * created and the BrowserHistory is updated.
     *
     * @param appletStartTime The time the applet was started
     * @param userId
     * @param nodeId
     * @param startTime
     * @param frameName
     * @param fragment Fragment part of the URL
     * @param query Query part of the URL
     * @param parentNodeName URL of the parent frame
     * @param parentFrameName Name of the parent frame
     * @param referrer Last URL
     * @param isIE Test if browser is MS Internet Explorer
     * @param isNS Test if browser is Netscape
     * @param frameCount Number of Frames in page
     * @param megTime The time the javascript was created
     * @param post The posted form data
     * @param appletConnector
     */
    void appletStarted(long appletStartTime,
            String userId,
            String nodeId,
            long startTime,
            String frameName,
            String fragment,
            String query,
            String post,
            String parentNodeName,
            String parentFrameName,
            String referrer,
            String isIE,
            String isNS,
            int frameCount,
            String megTime,
            boolean showMessages,
            AppletConnector appletConnector) {

        FrameAccess frameAccess = FrameAccess.getInstance();
        String key;
        NetNode referrerNode;
        int distance;
        FrameHistory fh;
        Object fhEntry;
        Object prevEntry;
        Access a;

        // System.out.println("--- Methode appletStarted gestartet");
        // System.out.println("EventDecoder:appletStarted " + this.toString());
        // System.out.println("megTime " + megTime);
        this.showMessages = showMessages;
        // Create a new Access event, test if the page comes from browser's
        // cache, store all data of the applet in the access event.
        a = AccessCache.get(userId, nodeId, startTime, frameName);
        if (a.getAction() > 0) {
            // Accessevent already exists... these are old params from browser cache
            accessEventExisted = true;
            a = AccessCache.get(userId, nodeId, appletStartTime, frameName);
            // System.out.println("---- AccessEventExisted ----");
        } else {
            // System.out.println("---- New AccessEvent ----");
            a.setAction(NEWURL);
        }
        // Fragment
        if (fragment.startsWith("#")) {     // cut off leading "#"
            fragment = fragment.substring(1);
        }
        a.setFragment(fragment);
        // query
        a.setQuery(query);
        // post data
        a.setPost(post);
        // ParentNodeName
        parentNodeName = new UriStripper(parentNodeName).getUri();
        if (!parentNodeName.equals("") && !parentNodeName.equals(":"))
            a.setParentFrameNodeId(NetNodeCache.get(parentNodeName).getNodeId());
        // parentFrameName
        a.setParentFrameName(parentFrameName);
        // referrer
        referrer = new UriStripper(referrer).getUri();
        if (!referrer.equals("") && !referrer.equals(":"))
            a.setReferrerNodeId(NetNodeCache.get(referrer).getNodeId());
        a.store();
      
        // Add the Access object to list of open frames in FrameAccess
        frameAccess.addAccessObject(parentFrameName, frameName, a); // Add the access object to FrameAccess, so that plugins can access the AccessObject

        // Which browser was used?
        browserName = OTHER;
        if (isIE.equals("[object]")) {
            browserName = EXPLORER;
            a.setAction(a.getAction() | BROWSER_IE);
            // System.out.println("Browser: Internet Explorer");
        }
        if (isNS.startsWith("{length:")) {
            browserName = NETSCAPE;
            a.setAction(a.getAction() | BROWSER_NS);
            // System.out.println("Browser: Netscape");
        }

        /*// Send collected infos to Applet to save status there
         statusSaver = "AccessEventExisted=";
         if (accessEventExisted) {
         statusSaver += "true";
         } else {
         statusSaver += "false";
         }
         statusSaver += "\nBrowserName=" + browserName + "\n";
         appletConnector.writeToApplet(statusSaver);
         */
        // Evaluate read information
        key = history.createKey(userId, parentFrameName, frameName);

        // If the user clicks on a link in a frame set that opens
        // the same page as before, IE does not load that page new,
        // but gets that page from cache
        // System.out.println("Started: getLastAction: " + history.getLastAction(key));
        if ((browserName == EXPLORER) && (frameCount > 0)
                && (history.getLastAction(key) == (LINK | SELFLINK))) {
            history.replaceLastEvent(key, frameName, megTime, a);
            history.setLastAction(key, NEWURL);
            printMsg("AccessTracking: Link to same URL clicked (Explorer)");
        } else


        // *** Page comes from browser's cache
        if (history.contains(key, megTime)) { // JavaScript was not generated new but comes from browser cache
            // System.out.println("MegTime existiert in History: Seite kommt aus dem Cache");
            distance = history.getDistance(key, megTime);
            // System.out.println("Distance = " + distance);
            if (distance == 0) {
                printMsg("AccessTracking: Seitengröße verändert");
            }
            if (distance != 0) {
                // Browser's referrer is buggy when a page comes from cache.
                // The frame History is used to correct it.
                if (history.getLastEvent(key, frameName) != null) {
                    referrerNode = NetNodeCache.getById(history.getLastEvent(key, frameName).getNodeId());
                    // System.out.println("Corrected referrer: " + referrerNode.getUri());
                    a.setReferrerNodeId(referrerNode.getNodeId());
                } else {
                    referrer = "";
                }
                history.setPositionTo(key, megTime);
                if (distance < 0) {        // user jumped back
                    // set action to a new value, but keep the browser type bits untouched
                    a.setAction(BACK | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                }
                if (distance > 0) {        // user jumped forward
                    // set action to a new value, but keep the browser type bits untouched
                    a.setAction(NEXT | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                }
                if ((distance < -1) | (distance > 1)) {   // user jumped more than one step
                    a.setAction(a.getAction() | SKIPPED);
                }
            }
            if (distance < 0) {
                printMsg("AccessTracking: Backtaste, " + (-distance) + " steps");
                a.setStepsInHistory(-distance);
            }
            if (distance > 0) {
                printMsg("AccessTracking: Nexttaste, " + distance + " steps");
                a.setStepsInHistory(distance);
            }
        } else {// This is a new page, not from cache

            // *** Page is new, not from cache

            // System.out.println("MegTime existiert nicht in History: Seite wurde neu geladen");

            // If the same page is requestet more than once, IE replaces the first
            // copy in it's cache by the new page. To prevent errors with "page from cache"
            // recognition all copies in all frameHistories have to be replaced by the new page
            if (browserName == EXPLORER) {
                history.replaceAllPagesLike(a, megTime);
            }
            if (frameCount > 0) { // the page is part of a frame set

                // *** Page contains FrameSet

                // Test if this page was opened in a new window
                if (!history.containsKey(key)) {
                    // new browser window
                    // set action to a new value, but keep the browser type bits untouched
                    a.setAction(NEWWINDOW | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                    printMsg("AccessTracking: new window");
                }
                // get the FrameHistory object for key, create it if
                // it does not exist
                fh = history.getFrameHistory(key);
                synchronized (fh) {
                    // System.out.println("Kritischen Abschnitt betreten");

                    // What did the user do to get to this Page
                    // If user clicked a link or submited a form,
                    // it was saved in lastAction
                    // System.out.println("LastAction: " + history.getLastAction(key));
                    switch (history.getLastAction(key)) {
                    case LINK | SELFLINK:
                        // set action to a new value, but keep the browser type bits untouched
                        a.setAction(history.getLastAction(key) | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                        a.store();
                        history.replaceLastEvent(key, frameName, megTime, a);
                        history.setLastAction(key, NEWURL);
                        printMsg("AccessTracking: Link to same URL clicked (Netscape)");
                        break;

                    case FORMSUBMIT:
                    case LINK:
                    case NEWURL:
                        // set action to a new value, but keep the browser type bits untouched
                        a.setAction(history.getLastAction(key) | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                        a.store();
                        // Store this Frameset to History
                        fhEntry = history.getLastEntry(key);
                        if (fhEntry == null) {
                            // no entry in frame history
                            // This is the browser's first page and first frame
                            // create new FrameSet object
                            FrameSet fs = new FrameSet();

                            // add this access object to frameset and
                            fs.addToFrameSet(megTime, a);
                            // System.out.println("No entry in FrameHistory");
                            // fs.showFrameSetContents();
                            // The first time a FrameSet is inserted into the
                            // FrameHistory, we have to create a "parent" Access
                            // object because parent frames don't have applets
                            // and can not connect to RAS like other frames.

                            Access pa = AccessCache.get(userId, 
                                        a.getParentFrameNodeId(),
                                        new Date().getTime(), 
                                        parentFrameName);

                            pa.setAction(a.getAction() & (BROWSER_NS | BROWSER_IE));
                            pa.store();
                            a.setParentAccessId(pa.getAccessId());
                            // all frames in this FrameSet get the AccessId
                            // of the parent Access object as their
                            // parentAccessId
                            fs.setParentAccessIds(pa.getAccessId());
                            // if FrameSet is filled completely,
                            // set lastAction to normal value
                            if (frameCount == fs.getFrameListSize()) {
                                history.setLastAction(key, NEWURL);
                                // System.out.println(frameCount + " von " + fs.getFrameListSize() + " Frames eingetragen");
                                // System.out.println("LastAction wurde auf NEW URL gesetzt");
                            }
                            // add frameset to FrameHistory
                            history.add(key, megTime, fs);
                            switch (a.getAction() & ~(BROWSER_NS | BROWSER_IE)) {
                            case FORMSUBMIT:
                                printMsg("AccessTracking: Form submited");
                                break;

                            case LINK:
                                printMsg("AccessTracking: Link clicked");
                                break;

                            case NEWURL:
                                printMsg("AccessTracking: New URL or bookmark used");
                                break;
                            }
                        } else { // fhEntry points to the last history entry
                            if (fhEntry.getClass().getName().endsWith("FrameSet")) {
                                // Last Entry in FH is a FrameSet
                                // Test if it is the current frameset, generated by
                                // another thread (test if there is no access event
                                // with staytime > 0)
                                if (((FrameSet) fhEntry).noStoppedEvents()) {
                                    // System.out.println("-- Keine gestoppent events: Aktuelles Frameset");
                                    // No stopped Access objects in FrameSet
                                    // seems to be the _actual_ FrameSet
                                    // Test if reload button was used
                                    if ((a.getAction() & NEWURL) == NEWURL) {
                                        // System.out.println("GetAction = NewURL");
                                        // Test if this page is already in the frameset, copied by an
                                        // other frame thread from the previous FrameSet
                                        // Test if the new page is the same as the page before but with
                                        // different start time. The user either pressed the reload button
                                        // or typed in the url of the last page. It's not possible to
                                        // make a difference between reload button and typed url.
                                        prevEntry = history.getPrevEntry(key);
                                        // history.showFrameContents(key);
                                        if (prevEntry == null) {// System.out.println("prevEntry ist null!");
                                        } else if (prevEntry.getClass().getName().endsWith("Access")) {// System.out.println("prevEntry ist ein Access object");                                 
                                        } else if (((FrameSet) prevEntry).containsPageLike(a)) {
                                            // System.out.println("--- Page was reloaded ---");
                                            a.setAction(RELOAD | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                                            // Replace old Access event with the reloaded one
                                            // history.replaceLastEvent(key, megTime, a);
                                        }
                                    }
                                    // Copy the parent Access Id from
                                    // one of the other Access events
                                    // of the Frameset
                                    a.setParentAccessId(((FrameSet) fhEntry).getFirstEvent().getParentAccessId());
                                    a.store();// add this access object to frameset
                                    // System.out.println("Diese AE hinzufuegen");
                                    // ((FrameSet)fhEntry).showFrameSetContents();
                                    ((FrameSet) fhEntry).addToFrameSet(megTime, a);
                                    // System.out.println("fhEntry is the actual FrameSet");
                                    // ((FrameSet)fhEntry).showFrameSetContents();
                                    if (frameCount
                                            == ((FrameSet) fhEntry).getFrameListSize()) {
                                        // if FrameSet is filled completely,
                                        // set lastAction to normal value
                                        history.setLastAction(key, NEWURL);
                                        // System.out.println(frameCount + " von " + ((FrameSet)fhEntry).getFrameListSize() + " Frames eingetragen");
                                        // System.out.println("LastAction wurde auf NEW URL gesetzt");
                                        // if this page was reloaded and loaded completely
                                        // this history entry should replace the one before
                                        // so we'll delete that one
                                        if ((a.getAction() & RELOAD) == RELOAD) {
                                            history.delPrevEntry(key);
                                        }
                                    }
                                    switch (a.getAction()
                                            & ~(BROWSER_NS | BROWSER_IE)) {
                                    case FORMSUBMIT:
                                        printMsg("AccessTracking: Form submited");
                                        break;

                                    case LINK:
                                        printMsg("AccessTracking: Link clicked");
                                        break;

                                    case RELOAD:
                                        printMsg("AccessTracking: Page was reloaded");
                                        break;

                                    case NEWURL:
                                        printMsg("AccessTracking: New URL or bookmark used");
                                        break;
                                    }
                                } else {
                                    // System.out.println("-- Gestoppent events: Voriges Frameset");
                                    // There are stopped Access objects in FrameSet
                                    // it is the frameset of the last event
                                    // Test if reload button was used
                                    if ((a.getAction() & NEWURL) == NEWURL) {
                                        // Test if this page is already in the frameset, copied by an
                                        // other frame thread from the previous FrameSet
                                        // Test if the new page is the same as the page before but with
                                        // different start time. The user either pressed the reload button
                                        // or typed in the url of the last page. It's not possible to
                                        // make a difference between reload button and typed url.
                                        if (((FrameSet) fhEntry).containsPageLike(a)) {
                                            // System.out.println("--- Page was reloaded ---");
                                            a.setAction(RELOAD | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                                            // Replace old Access event with the reloaded one
                                            // history.replaceLastEvent(key, megTime, a);
                                            if (browserName == EXPLORER) {// history.replaceAllPagesLike(key, megTime, a);
                                            }
                                        }
                                    }
                                    // create new FrameSet object
                                    // System.out.println("Erzeuge NEUES Frameset");
                                    FrameSet fs = new FrameSet();

                                    // System.out.println("fhEntry is the previous FrameSet");
                                    // fs.showFrameSetContents();
                                    // System.out.println("Kopiere alle ungestoppten");
                                    // copy all unstopped events
                                    fs.copyUnstoppedEvents((FrameSet) fhEntry);
                                    // add this access object to frameset and
                                    // fs.showFrameSetContents();
                                    // System.out.println("Fuege diese AE hinzu");
                                    fs.addToFrameSet(megTime, a);
                                    // fs.showFrameSetContents();
                                    // Has the parent frame changed?
                                    // Test if the "parentFrameNodeId" of this
                                    // Access object equals that one of an
                                    // Access object from previous FrameSet
                                    if (((FrameSet) fhEntry).getFirstEvent()
                                            != null) {
                                        Access prevAccess = ((FrameSet) fhEntry).getFirstEvent();

                                        if (a.getParentFrameNodeId().equals(prevAccess.getParentFrameNodeId())) {
                                            // System.out.println("ParentNodeId did not change");
                                            a.setParentAccessId(prevAccess.getParentAccessId());
                                        } else {
                                            // System.out.println("ParentNodeId has changed");
                                            // The parentNodeId has changed, so we
                                            // create a new "parent" Access object
                                            // because top frames don't have
                                            // applets and can not create their
                                            // Access object like other frames.

                                            Access pa = AccessCache.get(userId, 
                                                        a.getParentFrameNodeId(),
                                                        new Date().getTime(), 
                                                        parentFrameName);
                                            pa.setAction(a.getAction() & (BROWSER_NS | BROWSER_IE));
                                            pa.store();
                                            a.setParentAccessId(pa.getAccessId());
                                        }
                                    }
                                    // update all objects in FrameSet with
                                    // parentAccessId
                                    fs.setParentAccessIds(a.getParentAccessId());
                                    // if FrameSet is filled completely,
                                    // set lastAction to normal value
                                    if (frameCount == fs.getFrameListSize()) {
                                        history.setLastAction(key, NEWURL);
                                        // System.out.println(frameCount + " von " + fs.getFrameListSize() + " Frames eingetragen");
                                        // System.out.println("LastAction wurde auf NEW URL gesetzt");
                                    }
                                    // Referrer korrigieren
                                    a.setReferrerNodeId(((FrameSet) fhEntry).getLastEvent(frameName).getNodeId());
                                    a.store();
                                    // System.out.println("Corrected referrer: " + NetNodeCache.getById(a.getReferrerNodeId()).getUri());
                                    // add frameset to FrameHistory
                                    // System.out.println("Als neuen Historyeintrag speichern");
                                    if ((a.getAction() & RELOAD) == RELOAD) {
                                        // At RELOAD we insert the new FrameSet into
                                        // FrameHistory and delete the last one...
                                        history.insert(key, megTime, fs);
                                    } else {
                                        history.add(key, megTime, fs);
                                    }
                                    switch (a.getAction()
                                            & ~(BROWSER_NS | BROWSER_IE)) {
                                    case FORMSUBMIT:
                                        printMsg("AccessTracking: Form submited");
                                        break;

                                    case LINK:
                                        printMsg("AccessTracking: Link clicked");
                                        break;

                                    case RELOAD:
                                        printMsg("AccessTracking: Page was reloaded");
                                        break;

                                    case NEWURL:
                                        printMsg("AccessTracking: New URL or bookmark used");
                                        break;
                                    }
                                }
                            } else {
                                // System.out.println("Letzter Eintrag ist ein Access Event");
                                // last entry is an access object
                                // create new FrameSet object
                                // System.out.println("Erzeuge neues Frameset");
                                FrameSet fs = new FrameSet();

                                // add this access object to frameset and
                                fs.addToFrameSet(megTime, a);
                                // System.out.println("fhEntry is an Access object");
                                // fs.showFrameSetContents();
                                // The first time a FrameSet is inserted into the
                                // FrameHistory, we have to create a "parent" Access
                                // object because parent frames don't have applets
                                // and can not connect to RAS like other frames.
                                Access pa = AccessCache.get(userId, 
                                            a.getParentFrameNodeId(),
                                            new Date().getTime(), 
                                            parentFrameName);

                                pa.setAction(a.getAction() & (BROWSER_NS | BROWSER_IE));
                                pa.store();
                                a.setParentAccessId(pa.getAccessId());
                                // all frames in this FrameSet get the AccessId
                                // of the parent Access object as their
                                // parentAccessId
                                fs.setParentAccessIds(pa.getAccessId());
                                if (frameCount == fs.getFrameListSize()) {
                                    // if FrameSet is filled completely,
                                    // set lastAction to normal value
                                    history.setLastAction(key, NEWURL);
                                    // System.out.println(frameCount + " von " + fs.getFrameListSize() + " Frames eingetragen");
                                    // System.out.println("LastAction wurde auf NEW URL gesetzt");
                                }
                                // add frameset to FrameHistory
                                history.add(key, megTime, fs);
                                switch (a.getAction()
                                        & ~(BROWSER_NS | BROWSER_IE)) {
                                case FORMSUBMIT:
                                    printMsg("AccessTracking: Form submited");
                                    break;

                                case LINK:
                                    printMsg("AccessTracking: Link clicked");
                                    break;

                                case NEWURL:
                                    printMsg("AccessTracking: New URL or bookmark used");
                                    break;
                                }
                            }
                            // history.showFrameContents(key);
                        }
                        break;
                    }

                    // System.out.println("Kritischen Abschnitt verlassen");
                }

                /*// Test if a bookmark was used to get to this page
                 // This works with Netscape only
                 if (referrer.equals("[unknown origin]")) {
                 a.setAction(BOOKMARK);
                 System.out.println("---- Bookmark used ----");
                 }
                 */


                // *** No Frameset
            } else { // this page is not part of a frame set
                // Test if this page was opened in a new window
                if (!history.containsKey(key)) {
                    // new browser window
                    a.setAction(NEWWINDOW | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                    printMsg("AccessTracking: new window");
                    // Create first entry in frame history
                    history.add(key, megTime, a);
                }
                // What did the user do to get to this Page
                // If user clicked a link or submited a form, it was saved in lastAction
                // System.out.println("LastAction: " + history.getLastAction(key));
                switch (history.getLastAction(key)) {
                case LINK | SELFLINK:
                    a.setAction(history.getLastAction(key) | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                    history.replaceLastEvent(key, megTime, a);
                    printMsg("AccessTracking: Link to same URL clicked");
                    break;

                case FORMSUBMIT:
                case LINK:
                    a.setAction(history.getLastAction(key) | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                    if (history.getLastAction(key) == FORMSUBMIT) {
                        printMsg("AccessTracking: Form submited");
                    } else {
                        printMsg("AccessTracking: Link clicked");
                    }
                    // insert new event into history
                    history.add(key, megTime, a);
                    break;
                }
                // Test if a bookmark was used to get to this page
                // This works with Netscape only
                if (referrer.equals("[unknown origin]")) {
                    a.setAction(BOOKMARK | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                    printMsg("AccessTracking: Bookmark used to call page");
                    // insert new event into history
                    history.add(key, megTime, a);
                }
                // Test if the new page is the same as the page before but with
                // different start time. The user either pressed the reload button
                // or typed in the url of the last page. It's not possible to
                // make a difference between reload button and typed url.
                if ((a.getAction() & NEWURL) == NEWURL) {
                    if (history.containsPageLike(key, a)) {
                        distance = history.getDistanceOfPageLike(key, a);
                        // is it the same url as before?
                        if (distance == 0) {
                            printMsg("AccessTracking: Page was reloaded");
                            a.setAction(RELOAD | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                            // Replace old Access event with the reloaded one
                            history.replaceLastEvent(key, megTime, a);
                        }
                    }
                }
                // if getAction still is NEWURL the URL was either typed
                // in by the user or he used a bookmark or the Home button
                if ((a.getAction() & NEWURL) == NEWURL) {
                    printMsg("AccessTracking: URL typed in or bookmark or home used");
                    // insert new event into history
                    history.add(key, megTime, a);
                }
                history.setLastAction(key, NEWURL);
                a.store();  // Save changes to database
                // System.out.println("--- Action = " + a.getAction() + " ---");
            }
            // history.setLastMegTime(key, megTime);
        }
        // System.out.println("key: " + key);
        // Send 1st AccessEvent
        AccessCache.broadcastEvent(new AccessEvent(a));   // Now broadcast event!
    }

    /**
     * If the page is loaded completely, the applet sends a message
     * containing the exact time and all key values to get the right
     * access event. Then this method calls the access event and stores
     * the calculated load time
     *
     * @param appletStartTime
     * @param userId
     * @param nodeId
     * @param startTime
     * @param frameName
     * @param loadedTime The time the page was loaded completly
     */
    void pageLoaded(long appletStartTime,
            String userId,
            String nodeId,
            long startTime,
            String frameName,
            long loadedTime) {

        long loadTime = 0;
        Access a;

        // System.out.println("EventDecoder:pageLoaded " + this.toString());
        // System.out.println("--- Methode pageLoaded gestartet");

        if (accessEventExisted) {
            loadTime = loadedTime - appletStartTime;
            a = AccessCache.get(userId, nodeId, appletStartTime, frameName);
        } else {
            loadTime = loadedTime - startTime;
            a = AccessCache.get(userId, nodeId, startTime, frameName);
        }
        a.setLoadTime(loadTime);
        a.store();  // Save changes to database
        
        printMsg("AccessTracking: Load time: " + loadTime);

        // Send 2nd AccessEvent 
        if (AccessTracking.props.get("Create extra event after page is loaded").equals("true"))
            AccessCache.broadcastEvent(new AccessEvent(a));   // broadcast event if loadtime has to be read...
    }

    /**
     * If the user leaves a page, the applet sends a message containing
     * stopp time and all key values to get the right access event and
     * store the calculated stay time.
     *
     * @param appletStartTime
     * @param userId
     * @param nodeId
     * @param startTime
     * @param frameName
     * @param stoppedTime The time the page was unloaded
     */
    public void pageUnloaded(long appletStartTime,
            String userId,
            String nodeId,
            long startTime,
            String frameName,
            long stoppedTime) {

        long stayTime = 0;
        Access a;

        // System.out.println("EventDecoder:pageUnloaded " + this.toString());
        // System.out.println("--- Methode pageUnLoaded gestartet");
        if (accessEventExisted) {
            stayTime = stoppedTime - appletStartTime;
            a = AccessCache.get(userId, nodeId, appletStartTime, frameName);
        } else {
            stayTime = stoppedTime - startTime;
            a = AccessCache.get(userId, nodeId, startTime, frameName);
        }
        a.setStayTime(stayTime);
        a.store();  // Save changes to database
        // System.out.println("--- Page unloaded");
        printMsg("AccessTracking: Stay time: " + stayTime);

        // 3rd event after page is "unloaded"...
        if (AccessTracking.props.get("Create extra events after user leaves a page").equals("true"))
            AccessCache.broadcastEvent(new AccessEvent(a));   // broadcast new event!

    }

    /**
     * This Method is invoked by Transceiver every time the User clicked a
     * link in a browser window.
     * If the link is a link to the exact same URI, this information is saved
     * to the LastAction bit array in the History class. So the next page
     * can get the information of it's invokation.
     * If the link is an fragment link, a new AccessEvent is created an added
     * to the history.
     * If the link is a link to another URI, this information is stored to
     * the lastAction bit array for the next page to get this information.
     *
     * @param appletStartTime
     * @param userId
     * @param nodeId
     * @param startTime
     * @param frameName
     * @param frameCount                Number of frames in frame set
     * @param megTime                   Time of javascript creation
     * @param linkTime                  The time the link was clicked
     * @param linkId
     * @param accessEventExistedString  If page comes from cache, a new start time had to be used.
     * @param browserName               Number describing the browser
     */
    public void linkClicked(long appletStartTime,
            String userId,
            String nodeId,
            long startTime,
            String frameName,
            int frameCount,
            String megTime,
            long linkTime,
            String linkId) {
        String key;
        Access a;
        Access ta;     // the access object the target of the link points to
        Access a1;
        Link link;
        FrameHistory fh;

        // System.out.println("EventDecoder:linkClicked " + this.toString());
        // System.out.println("megTime " + megTime);
        // System.out.println("--- Methode Link gestartet");
        link = LinkCache.getById(linkId);   
        // get the Access object from database
        if (accessEventExisted) {
            a = AccessCache.get(userId, nodeId, appletStartTime, frameName);
        } else {
            a = AccessCache.get(userId, nodeId, startTime, frameName);
        }
        key = history.createKey(userId, a.getParentFrameName(), a.getFrameName());
        // System.out.println("key: " + key);
        // is it a link to the same uri
        if (frameCount > 0) { // It is a FrameSet
            if (link.getTarget().toLowerCase().equals("_top")
                    || link.getTarget().toLowerCase().equals("_parent")) {
                // Link to another url
                history.setLastAction(key, LINK);
                a.setLinkId(link.getLinkId());
                a.store();  // Save changes to database
                // System.out.println("Link clicked. LinkId: " + a.getLinkId());
            } else {
                if (link.getTarget().toLowerCase().equals("_self")) {
                    ta = a;
                    // System.out.println("Link: Target = _self");
                } else {
                    ta = history.getLastEvent(key, link.getTarget());
                    // System.out.println("Link: Get Last Event");
                }
                // System.out.println("--- Link target: " + link.getTarget() + " ---");
                // System.out.println("NodeId des Accessobj. " + NetNodeCache.getById(ta.getNodeId()).getNodeId());
                // System.out.println("NodeId des Linkobjeks " + link.getToNode().getNodeId());
                // System.out.println("TargetNodeId: " + ta.getNodeId());
                // System.out.println("Ziel NodeId: " + link.getToNode().getNodeId());
                if (NetNodeCache.getById(ta.getNodeId()).equals(link.getToNode())) {
                    if (link.getFragment().equals(ta.getFragment())) {// link to itself
                        history.setLastAction(key, LINK | SELFLINK);
                        // System.out.println("Link auf die gleiche URL:"+ ta.getNodeId() + " " +link.getToNode());
                    } else { // Link to another fragment
                        ta.setStayTime(linkTime - ta.getTime());
                        printMsg("AccessTracking: StayTime: " + ta.getStayTime());
                        ta.setLinkId(link.getLinkId());
                        ta.store();
                        // Create a new AccessEvent
                        a1 = AccessCache.get(ta.getUserId(),
                                ta.getNodeId(),
                                linkTime,
                                ta.getFrameName());
                        a1.setAction(LINK | FRAGMENT | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                        a1.setFragment(link.getFragment());
                        a1.setQuery(ta.getQuery());
                        a1.setParentFrameNodeId(ta.getParentFrameNodeId());
                        a1.setParentFrameName(ta.getParentFrameName());
                        a1.setReferrerNodeId(ta.getNodeId());
                        a1.setParentAccessId(ta.getParentAccessId());
                        a1.store();
                        // Add new AccessEvent to History
                        // create new FrameSet object
                        FrameSet fs = new FrameSet();
                        // get actual history entry
                        FrameSet thisFrameSet = (FrameSet) history.getLastEntry(key);

                        // copy all unstopped events
                        fs.copyUnstoppedEvents(thisFrameSet);
                        // add this access object to frameset and
                        fs.addToFrameSet(megTime, ta);
                        // add frameset to FrameHistory
                        history.add(key, megTime, fs);
                        printMsg("AccessTracking: same URL, other fragment");
                        // System.out.println("LinkTime=" + linkTime);
                        if (accessEventExisted) {
                            appletStartTime = linkTime;
                        } else {
                            startTime = linkTime;
                        }
                    } // of else
                } else {
                    // System.out.println("link zu anderer URL");
                    // Link to another url
                    history.setLastAction(key, LINK);
                    a.setLinkId(link.getLinkId());
                    a.store();  // Save changes to database
                    // System.out.println("Link clicked. LinkId: " + a.getLinkId());
                }
            }
        } else { // No FrameSet
            if (link.getFromNode() == link.getToNode()) {
                if (link.getFragment().equals(a.getFragment())) {// link to itself
                    history.setLastAction(key, LINK | SELFLINK);
                    // System.out.println("Link auf die gleiche URL: "+link.getFragment()+ " " + a.getFragment());
                } else { // Link to another fragment
                    if (accessEventExisted) {
                        a.setStayTime(linkTime - appletStartTime);
                    } else {
                        a.setStayTime(linkTime - startTime);
                    }
                    // System.out.println("StayTime: " + a.getStayTime());
                    a.setLinkId(link.getLinkId());
                    a.store();
                    // Create a new AccessEvent
                    a1 = AccessCache.get(a.getUserId(),
                            a.getNodeId(),
                            linkTime,
                            a.getFrameName());
                    a1.setAction(LINK | FRAGMENT | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
                    a1.setFragment(link.getFragment());
                    a1.setQuery(a.getQuery());
                    a1.setParentFrameNodeId(a.getParentFrameNodeId());
                    a1.setParentFrameName(a.getParentFrameName());
                    a1.setReferrerNodeId(a.getNodeId());
                    a1.store();
                    a = a1;
                    a1 = null;
                    // Add new AccessEvent to History
                    history.add(key, megTime, a);
                    printMsg("AccessTracking: same URL, other fragment");
                    // System.out.println("Neues Access Event erzeugt");
                    // System.out.println("LinkTime=" + linkTime);
                    if (accessEventExisted) {
                        appletStartTime = linkTime;
                    } else {
                        startTime = linkTime;
                    }
                }
            } else {
                // Link to another url
                // System.out.println("LC: key:      " + key);
                // System.out.println("LC: new key:  " + history.createKey(userId, parentFrameName, frameName));
                history.setLastAction(key, LINK);
                a.setLinkId(link.getLinkId());
                a.store();  // Save changes to database
                // System.out.println("Link clicked. LinkId: " + a.getLinkId());
            }
        }
    }

    /**
     * This Method is invoked by Transceiver every time the User submits a
     * form in a browser window.
     * If the User submits a from, it can either be a mail form or a cgi
     * form. If the form is a cgi form, the browser loads a new page after
     * submit. This method stores the information of a submited form in
     * history.setLastAction for the next page to get it.
     * If it is a mail form, the browser sends the mail but stays
     * on the same page.
     *
     * @param appletStartTime
     * @param userId
     * @param nodeId
     * @param startTime
     * @param frameName
     * @param formTime                  The time the formsubmit button was clicked
     * @param formAction                The Action called by the form
     * @param accessEventExistedString  If page comes from cache, a new start time had to be used.
     * @param browserName               Number describing the browser
     */
    public void formSubmited(long appletStartTime,
            String userId,
            String nodeId,
            long startTime,
            String frameName,
            long formTime,
            String formAction) {
        String key;
        Access a;
        Access a1;
        Link link;

        // System.out.println("EventDecoder:formSubmited " + this.toString());
        // System.out.println("--- Methode formSubmited gestartet");
        if (accessEventExisted) {
            a = AccessCache.get(userId, nodeId, appletStartTime, frameName);
        } else {
            a = AccessCache.get(userId, nodeId, startTime, frameName);
        }
        key = history.createKey(userId, a.getParentFrameName(), a.getFrameName());
        if (formAction.toLowerCase().startsWith("mailto:")) {
            a.setStayTime(formTime - startTime);
            // System.out.println("StayTime: " + a.getStayTime());
            // Neues Event erzeugen
            NetNode formNode = NetNodeCache.get(formAction);
            // System.out.println("formNodeId =  " + formNode.getNodeId());
            // System.out.println("This NodeId = " + NetNodeCache.getById(a.getNodeId()).getNodeId());
            Link formLink = LinkCache.get(NetNodeCache.getById(a.getNodeId()), formNode, "");

            // System.out.println("Linkid =      " + formLink.getLinkId());
            a.setLinkId(formLink.getLinkId());
            a.store();
            // Create a new AccessEvent
            a1 = AccessCache.get(a.getUserId(),
                    a.getNodeId(),
                    formTime,
                    a.getFrameName());
            a1.setAction(FORMSUBMIT | (a.getAction() & (BROWSER_NS | BROWSER_IE)));
            a1.setFragment(a.getFragment());
            a1.setQuery(a.getQuery());
            a1.setParentFrameNodeId(a.getParentFrameNodeId());
            a1.setParentFrameName(a.getParentFrameName());
            a1.setReferrerNodeId(a.getNodeId());
            a1.setParentAccessId(a.getParentAccessId());
            a1.store();
            a = a1;
            a1 = null;
            if (accessEventExisted) {
                appletStartTime = formTime;
            } else {
                startTime = formTime;
            }
            printMsg("AccessTracking: Mail form submited: New Access event created");
        } else {
            history.setLastAction(key, FORMSUBMIT);
            a.setLinkId(new Link(NetNodeCache.getById(a.getNodeId()), NetNodeCache.get(formAction), "").getLinkId());
            a.store();
            printMsg("AccessTracking: CGI Form was submited");
        }
    }

    /**
     * This Method is invoked by Transceiver every time a specific user-action shall be logged.
     * This can be a pressed key, a mouseover-event or anything that can be traced
     * by JavaScript.<P>
     * This message is then reported to the class and the (static) method
     * defined in the transmitted parameters.
     * <P>
     * Use the following JavaScript-Command to send a message:<BR>
     * <CODE>document.LogApplet.logMsg("logMessage\nclassName=PACKAGE.CLASSNAME\nmethod=STATIC_METHOD\nparameter=PARAMETER_TO_BE_SENT\n");</CODE><BR>
     * where the strings in CAPITALS have to be replaced by the class, method and parameter you want...
     * <P>
     * @param className Name of the class that shall be informed
     * @param method Name of the (static) method that shall be informed.
     * @param param The parameter/ message to be sent.
     */
    public void logMessage(String className, 
            String method,
            String param) {
        try {
            Class c = Class.forName(className);               // class loaded

            // System.out.println("Loaded class: " + c);
            printMsg("AccessTracking.logMessage: Loaded class: " + c);
            Method m = c.getDeclaredMethod(method, new Class[] {String.class}); // got method

            // System.out.println("Got method: " + m);
            printMsg("AccessTracking.logMessage: Got method: " + m);
            Object o = m.invoke(null, new Object[] {param});   // call the static method with param as String parameter...

            // System.out.println("Output: " + o);
            printMsg("AccessTracking.logMessage: Output: " + o);
        } catch (Exception e) {
            ErrorLog.log(this, "logMessage", "Error calling static function:\n" + className + "." + method + "(\"" + param + "\")", e);
        }            
        
    }

    public void printMsg(String textMsg) {
        if (showMessages) {
            System.out.println(textMsg);
        }
    }
}
