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, 8-Dec-2002
 */


public class IESpyEventDecoder {
    // 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 - true, no matter how many steps
    public final static int NEXT = 1 << 6;     // user clicked the next button - true, no matter how many steps
    public final static int SKIPPED = 1 << 7;     // user jumped more than one step back or next
    // get the exact number of steps in (AccessObject).getStepsInHistory()
    public final static int NEWWINDOW = 1 << 8;     // new browser window
    // if HTMLMENU == true: right mouse "open in new..." was used
    public final static int BOOKMARK = 1 << 9;     // user clicked a bookmark (favorite from the main menu) -
    // MAINMENU will be true
    public final static int LINKBUTTON = 1 << 10;    // user clicked a button of IE's Link Bar
    public final static int ADDRESSBAR = 1 << 11;    // user typed in an address into the address bar or choose an address from address bar popup
    public final static int HOME = 1 << 12;    // user clicked the home button
    public final static int EXPLORERBAR = 1 << 13;    // user clicked some entry on the side explorer
    // bar (favorties or history or ...)

    // which way did the user choose to call the browsers change page function
    public final static int HOTKEY = 1 << 22;    // a hotkey like ALT & arrow was used for back or next...
    public final static int HTMLMENU = 1 << 23;    // the menu inside the HTML area was used
    public final static int MAINMENU = 1 << 24;    // the main menu was used
    public final static int TBBUTTON = 1 << 25;    // a button of the toolbar was used (next, back)
    public final static int TBMENU = 1 << 26;    // the menu of the toolbar was used (popup next...)
    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 HEURISTIC = 1 << 30;    // a heuristic was used to get this value - unused in IESpyEventDecoder

    private BrowserHistory history;
    private boolean showMessages;
    private Access a;
    private long startTime = 0;
    private int frameCount;
    private String key;
    private IESpyLastAction lastAction = null;

    public IESpyEventDecoder() {// get the BrowserHistory object
    }

    /**
     * This method is called when JavaScript was started and sent it's
     * first information via ie's statusline. With that information
     * an AccessEvent is created.
     *
     * @param startTime
     * @param userId
     * @param nodeId
     * @param fragment Fragment part of the URL
     * @param query Query part of the URL
     * @param frameName
     * @param parentNodeName URL of the parent frame
     * @param parentFrameName Name of the parent frame
     * @param referrer Last URL
     * @param frameCount Number of Frames in page
     */
    void javaScriptStarted(
            long startTime,
            String userId,
            String nodeId,
            String fragment,
            String query,
            String frameName,
            String parentNodeName,
            String parentFrameName,
            String referrer,
            int frameCount,
            boolean showMessages,
            IESpyLastAction lastAction) {

        FrameAccess frameAccess = FrameAccess.getInstance();

        this.lastAction = lastAction;
        FrameHistory fh;
        NetNode referrerNode;
        Object fhEntry;
        Object prevEntry;

        
        history = BrowserHistory.getInstance();

        // System.out.println("--- Methode appletStarted gestartet");
        this.showMessages = showMessages;
        // Create a new Access event
        a = AccessCache.get(userId, nodeId, startTime, frameName);

        // Add the access object to FrameAccess, so that plugins can access the AccessObject
        frameAccess.addAccessObject(parentFrameName, frameName, a); 

        // Fragment
        if (fragment.startsWith("#")) {     // cut off leading "#"
            fragment = fragment.substring(1);
        }
        a.setFragment(fragment);
        // query
        a.setQuery(query);
        // 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());
        // BrowserName
        a.setAction(BROWSER_IE);         // IESpy only works with Internet Explorer
        // Load time
        this.startTime = startTime;
        a.store();
        this.frameCount = frameCount;

        key = history.createKey(userId, parentFrameName, frameName);

        if (frameCount > 0) { // the page is part of a frame set

            // *** Page contains FrameSet

            // get the FrameHistory object for key, create it if
            // it does not exist
            fh = history.getFrameHistory(key);
            synchronized (fh) {

                switch (lastAction.get() & 0x3FFF) { // All "change page" bits
                case NEWWINDOW:
                    // new Frame window
                    // set action to a new value, but keep the browser type bits untouched
                    a.setAction(a.getAction() | lastAction.get());
                    printMsg("AccessTracking: new browser window");
                    break;

                case LINK:
                case LINK | SELFLINK:
                    a.setAction(a.getAction() | lastAction.get());
                    printMsg("AccessTracking: Link clicked");
                    break;

                case BACK:
                case BACK | SKIPPED:
                    a.setAction(a.getAction() | lastAction.get());
                    a.setStepsInHistory(lastAction.getStepsInHistory());
                    // System.out.println(a.getStepsInHistory());
                    printMsg("AccessTracking: Back clicked (" + lastAction.getStepsInHistory() + " step(s))");
                    break;

                case NEXT:
                case NEXT | SKIPPED:
                    a.setAction(a.getAction() | lastAction.get());
                    a.setStepsInHistory(lastAction.getStepsInHistory());
                    printMsg("AccessTracking: Next clicked (" + lastAction.getStepsInHistory() + " step(s))");
                    // System.out.println(a.getStepsInHistory());
                    break;

                case HOME:
                    a.setAction(a.getAction() | lastAction.get());
                    printMsg("AccessTracking: HOME clicked");
                    break;

                case RELOAD:
                    a.setAction(a.getAction() | lastAction.get());
                    printMsg("AccessTracking: Reload clicked");
                    break;

                case ADDRESSBAR:
                    a.setAction(a.getAction() | lastAction.get());
                    printMsg("AccessTracking: new URL typed in or chosen from pulldown");
                    break;

                case EXPLORERBAR:
                    a.setAction(a.getAction() | lastAction.get());
                    printMsg("AccessTracking: Explorerbar (Favorites or history)");
                    break;

                case BOOKMARK:
                    a.setAction(a.getAction() | lastAction.get());
                    printMsg("AccessTracking: Bookmark clicked");
                    break;

                case LINKBUTTON:
                    a.setAction(a.getAction() | lastAction.get());
                    printMsg("AccessTracking: Button of Link bar clicked");
                    break;
                }

                // 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(Long.toString(startTime), 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 = new Access(userId,
                            a.getParentFrameNodeId(),
                            new Date().getTime(),
                            parentFrameName);

                    pa.setAction(a.getAction() & (BROWSER_NS | BROWSER_IE));
                    pa.store();
                    AccessCache.broadcastEvent(new AccessEvent(pa));   // broadcast event for parent frame set
                    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);
                        lastAction.set(0);
                        // System.out.println(frameCount + " von " + fs.getFrameListSize() + " Frames eingetragen");
                    }
                    // add frameset to FrameHistory
                    history.add(key, Long.toString(startTime), fs);
                } 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() & 0x3FFF) == 0) {
                                // 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(Long.toString(startTime), 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
                                lastAction.set(0);
                                // System.out.println(frameCount + " von " + ((FrameSet)fhEntry).getFrameListSize() + " Frames eingetragen");
                                // 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);
                                }
                            }

                        } 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() & 0x3FFF) == 0) {
                                // 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(Long.toString(startTime), 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 = new Access(userId,
                                            a.getParentFrameNodeId(),
                                            new Date().getTime(),
                                            parentFrameName);

                                    pa.setAction(a.getAction() & (BROWSER_NS | BROWSER_IE));
                                    pa.store();
                                    AccessCache.broadcastEvent(new AccessEvent(pa));   // broadcast event for parent frame set
                                    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()) {
                                lastAction.set(0);
                                // System.out.println(frameCount + " von " + fs.getFrameListSize() + " Frames eingetragen");
                            }
                            // 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, Long.toString(startTime), fs);
                            } else {
                                history.add(key, Long.toString(startTime), fs);
                            }
                        }
                    } 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(Long.toString(startTime), 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 = new Access(userId,
                                a.getParentFrameNodeId(),
                                new Date().getTime(),
                                parentFrameName);

                        pa.setAction(a.getAction() & (BROWSER_NS | BROWSER_IE));
                        pa.store();
                        AccessCache.broadcastEvent(new AccessEvent(pa));   // broadcast event for parent frame set
                        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
                            lastAction.set(0);
                            // System.out.println(frameCount + " von " + fs.getFrameListSize() + " Frames eingetragen");
                        }
                        // add frameset to FrameHistory
                        history.add(key, Long.toString(startTime), fs);
                    }
                    // history.showFrameContents(key);
                }
            }
            // *** No Frameset
        } else { // this page is not part of a frame set
            // 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 (lastAction.get() & 0x3FFF) { // All "change page" bits
            case NEWWINDOW:
                a.setAction(a.getAction() | lastAction.get());
                printMsg("AccessTracking: new window");
                break;

            case LINK | SELFLINK:
                a.setAction(a.getAction() | lastAction.get());
                printMsg("AccessTracking: Link to same URL clicked");
                break;

            case FORMSUBMIT:
                a.setAction(a.getAction() | lastAction.get());
                printMsg("AccessTracking: Form submited");
                break;

            case LINK:
                a.setAction(a.getAction() | lastAction.get());
                printMsg("AccessTracking: Link clicked");
                break;

            case BACK:
            case BACK | SKIPPED:
                a.setAction(a.getAction() | lastAction.get());
                a.setStepsInHistory(lastAction.getStepsInHistory());
                // System.out.println(a.getStepsInHistory());
                printMsg("AccessTracking: Back clicked (" + lastAction.getStepsInHistory() + " step(s))");
                break;

            case NEXT:
            case NEXT | SKIPPED:
                a.setAction(a.getAction() | lastAction.get());
                a.setStepsInHistory(lastAction.getStepsInHistory());
                printMsg("AccessTracking: Next clicked (" + lastAction.getStepsInHistory() + " step(s))");
                // System.out.println(a.getStepsInHistory());
                break;

            case HOME:
                a.setAction(a.getAction() | lastAction.get());
                printMsg("AccessTracking: HOME clicked");
                break;

            case RELOAD:
                a.setAction(a.getAction() | lastAction.get());
                printMsg("AccessTracking: Reload clicked");
                break;

            case ADDRESSBAR:
                a.setAction(a.getAction() | lastAction.get());
                printMsg("AccessTracking: new URL typed in or chosen from pulldown");
                break;

            case EXPLORERBAR:
                a.setAction(a.getAction() | lastAction.get());
                printMsg("AccessTracking: Explorerbar (favorites or history)");
                break;

            case BOOKMARK:
                a.setAction(a.getAction() | lastAction.get());
                printMsg("AccessTracking: Bookmark clicked");
                break;

            case LINKBUTTON:
                a.setAction(a.getAction() | lastAction.get());
                printMsg("AccessTracking: Button of Link bar clicked");
                break;
            }
            lastAction.set(0);
            history.add(key, Long.toString(startTime), a);
            a.store();  // Save changes to database
        }
        // Send 1st AccessEvent
        AccessCache.broadcastEvent(new AccessEvent(a));   // Now broadcast event!
    }

    /**
     * If the page is loaded completely, Browser Helper Object sends a DCPL
     * (Document Complete). This method 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 loadedTime) {

        long loadTime = 0;

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

        loadTime = loadedTime - startTime;
        if (a != null) {
            a.setLoadTime(loadTime);
            a.store();  // Save changes to database
        }

        printMsg("AccessTracking: Load time: " + loadTime);

        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...
    }

    /**
     * When Page is unloaded, the Applet sends a corresponding text via status-
     * line.
     *
     * @param stoppedTime The time the page was unloaded
     */
    public void pageUnloaded(long stoppedTime) {

        long stayTime = 0;

        stayTime = stoppedTime - startTime;
        // System.out.println("--- Page unloaded");
        printMsg("AccessTracking: Stay time: " + stayTime);

        if (a != null) {
            a.setStayTime(stayTime);
            a.store();  // Save changes to database
            // 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 everytime the user clicks a link or chooses a
     * Bookmark or performes any other action to load new page.
     * The "action" is stored in variable lastAction. When next Applet starts,
     * the reason for last page change is read out of lastAction.
     *
     * @param action                 BitArray indicating users last action
     */
    public void linkClicked(String linkId,
            long linkTime) {

        // lastAction = action;
        if (a != null) {
            Link link = LinkCache.getById(linkId);

            if (frameCount > 0) { // It is a FrameSet
                // System.out.println("LinkTo:" + link.getToNode().getNodeId() + " This:" + a.getNodeId());
                if (link.getToNode().getNodeId().equals(a.getNodeId())) {
                    // System.out.println("Gleiche NodeId");
                    if (link.getFragment().equals(a.getFragment())) {// link to itself
                        lastAction.set(LINK | SELFLINK);
                        // System.out.println("Link auf die gleiche URL und Fragment:" + a.getNodeId() + " " + link.getToNode());
                    } else { // Link to another fragment
                        // System.out.println("FragmentLink");
                        a.setStayTime(linkTime - a.getTime());
                        printMsg("AccessTracking: StayTime: " + a.getStayTime());
                        a.setLinkId(link.getLinkId());
                        a.store();
                        // Create a new AccessEvent
                        Access 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.setParentAccessId(a.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(Long.toString(startTime), a);
                        // add frameset to FrameHistory
                        history.add(key, Long.toString(startTime), fs);
                        printMsg("AccessTracking: same URL, other fragment");
                        // System.out.println("LinkTime=" + linkTime);
                        startTime = linkTime;
                    } // of else
                } else {
                    // Link to another url
                    lastAction.set(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
                        lastAction.set(LINK | SELFLINK);
                        // System.out.println("Link auf die gleiche URL: " + link.getFragment() + " " + a.getFragment());
                    } else { // Link to another fragment
                        a.setStayTime(linkTime - startTime);
                        // System.out.println("StayTime: " + a.getStayTime());
                        a.setLinkId(link.getLinkId());
                        a.store();
                        // Create a new AccessEvent
                        Access 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, Long.toString(startTime), a);
                        printMsg("AccessTracking: same URL, other fragment");
                        // System.out.println("Neues Access Event erzeugt");
                        // System.out.println("LinkTime=" + linkTime);
                        startTime = linkTime;
                    }
                } else {
                    // Link to another url
                    lastAction.set(LINK);
                    a.setLinkId(link.getLinkId());
                    a.store();  // Save changes to database
                    // System.out.println("Link clicked. LinkId: " + a.getLinkId());
                }
            }
            a.setLinkId(linkId);
        }
        if (lastAction != null) {
            printMsg("AccessTracking: Action: " + lastAction.get());
        }
    }

    /**
     * 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 formTime,
            String formAction) {
        Link link;

        // System.out.println("EventDecoder:formSubmited " + this.toString());
        if (formAction.toLowerCase().startsWith("mailto:")) {
            if (a != null) {
                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
            }
            printMsg("AccessTracking: Mail form submited");
        } else {
            lastAction.set(FORMSUBMIT);
            if (a != null) {
                a.setLinkId(new Link(NetNodeCache.getById(a.getNodeId()), NetNodeCache.get(formAction), "").getLinkId());
                a.store();
            }
            printMsg("AccessTracking: 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);
        }
    }
}

