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


import scone.netobjects.Access;
import scone.netobjects.AccessCache;
import scone.netobjects.AccessEvent;
import scone.netobjects.LinkCache;
import scone.netobjects.Link;


/**
 * The methods of this class are called by AccessTrackingMeg. Every
 * message of the meg has its own method to evaluate the received
 * information and to create and change Access events.
 *
 * @author Torsten Hass
 * @author Harald Weinreich
 * @version 1.2, 28-Jul-2004
 */


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 or some other skip...
    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 NEWFRAME   = 1 << 14;    // new browser window
    public final static int NEWURL     = 1 << 15;    // user typed in a new url or selected from bookmarks (IE)
    public final static int BROWSER_FIREFOX = 1 << 27;   // browser is Firefox
    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 << 30;   // 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
    public  final int OTHER = 0;  // unknown browser type
    public  final int EXPLORER = 1;  // Microsoft Internet Explorer
    public  final int NETSCAPE = 2;  // Netscape 4ff
    public final int GECKO = 3;  // Mozilla & Firefox etc.
    public final int FIREFOX = 4;  // Special Access-Tracking in Firefox...

    protected final int MAXHISTORY = 50;  // Maximum history length

    protected BrowserHistory bh;
    protected int browserName;
    protected boolean accessEventExisted;
    protected boolean showMessages;

    public EventDecoder() {
        // get the BrowserHistory object
        bh = 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 userId
     * @param nodeId
     * @param start
     * @param frame
     * @param fragment Fragment part of the URL
     * @param query Query part of the URL
     * @param post The posted form data
     * @param referrerNodeId Last URL
     * @param parentFrameName Name of the parent frame
     * @param parentNodeId URL of the parent frame
     * @param history Number of Frames in page
     */
    void startEvent( 
                    String userId,
                    String nodeId,
                    long start,
                    String frame,
                    String fragment,
                    String query,
                    String post,
                    String referrerNodeId,
                    String parentFrame,
                    String parentNodeId,
					String windowSize,
					String clickPos,
                    int history ) {

        if (frame.equals("") || nodeId.equals("") ) {
            // System.out.println("START: Window name missing - probably an iframe!");
            return; // No frame name, no nodeID - not really an event...
        }
        
        // Create key of this browser window/frame
        String key = bh.createKey(userId, parentFrame, frame);

        // System.out.println("--- Methode Started");
        // Create a new Access event, test if the page comes from browser's
        // cache, store all data of the applet in the access event.
        Access a = AccessCache.get(userId, nodeId, start, frame);
        a.setReferrerNodeId(referrerNodeId);
        a.setParentFrameName(parentFrame);
        a.setParentFrameNodeId(parentNodeId);

        // Post data?
        if (post!=null && !post.equals("")) {
            a.setAction(FORMSUBMIT | a.getAction() );
            a.setPost(post);
            bh.setLastAction(key, 0);  // Delete last action
        }
        // Maybe there are get parameter too, but that does not mean, the user clicked submit...
        a.setQuery(query);

        if (bh.contains(key)) { // Does a history for this browser frame already exist?
            // bh.getLast(key,nodeId);

            int historyLength = bh.getHistoryLength(key);   // Length of history in Scone
            int eventPosition = bh.getPosition(key,a);      // Position of current event in history
            int position = bh.getPosition(key);             // Position of last event in history

            //System.out.println("userId        : "+userId );
            //System.out.println("nodeId        : "+nodeId );
            //System.out.println("start         : "+start  );
            //System.out.println("frame         : "+frame  );
            //System.out.println("fragment      : "+fragment  );
            //System.out.println("Last Node ID  : " + bh.getLastEvent(key).getNodeId());
            //System.out.println("Refer. Node ID: " + referrerNodeId);
            //System.out.println("Last Action   : " + bh.getLastAction(key));

            //System.out.println("historyLength : "+historyLength );
            //System.out.println("history       : "+history       );
            //System.out.println("this eventPos : "+eventPosition+" xxxxxxxxxxxxxxxx");
            //System.out.println("last Pos      : "+position+" xxxxxxxxxxxxxxxx");

            // Ops! Skipped some events...
            if (history > historyLength + 1) {  
                System.out.println("Some Browser events were skipped!?!---------------");
                for (int i=historyLength; i < history ; i++) {
                    bh.add(key, new Access(userId,"",0,"") );  // Add dummy events...
                }
                a.setAction(SKIPPED | HEURISTIC | a.getAction() );  // forward several steps...
                a.setStepsInHistory(history - historyLength);
            }

            // History limit of Mozilla reached!
            if (history >= MAXHISTORY) {  
                if (history == historyLength) {  // History same length
                    if (bh.getLastAction(key) == FORMSUBMIT)           // Submit was clicked
                        bh.removeFirstEntries(key, historyLength-MAXHISTORY+1);  // delete oldest event
                    else if (bh.getLastAction(key) == LINK)          // Link was clicked
                        bh.removeFirstEntries(key, historyLength-MAXHISTORY+1);  // delete oldest event
                    else if (eventPosition == 0 || eventPosition <= position - 16) // Unlikely that he went back more than 16 steps...
                        bh.removeFirstEntries(key, historyLength-MAXHISTORY+1);  // delete oldest event
                }
            }

            // read again... may have changed...
            historyLength = bh.getHistoryLength(key);   // Length of history in Scone
            eventPosition = bh.getPosition(key,a);      // Position of current event in history
            position = bh.getPosition(key);             // Position of last event in history
            //System.out.println("historyLength : "+historyLength );
            //System.out.println("this eventPos : "+eventPosition+" xxxxxxxxxxxxxxxx");
            //System.out.println("last Pos      : "+position+" xxxxxxxxxxxxxxxx");

            // History one longer - Add a new page
            if (history == historyLength + 1) {  
                bh.add(key,a);  // New action event
                if (bh.getLastAction(key) == FORMSUBMIT) {
                    a.setAction(FORMSUBMIT | a.getAction() );  // Submit was clicked
                } else if (bh.getLastAction(key) == LINK) {
                    a.setAction(LINK | a.getAction() );  // Link was clicked
                    a.setLinkType(bh.getLastLinkType(key));
                } else {
                    if (bh.getLastEvent(key).getNodeId() == referrerNodeId || !fragment.equals("")) {
                        a.setAction(LINK | HEURISTIC | a.getAction() );  // Link was probably clicked
                    }
                    if (bh.getLastReferrer(key) == referrerNodeId && fragment.equals("")) { // Only if not fragment was clicked!
                        a.setAction(NEWURL | HEURISTIC | a.getAction() );  // Something else was done, same referrer as before!
                    }
                }                
            }
            // length did not change.. Maybe back button...?            
            if (historyLength == history) {  
                if (eventPosition == 0) {
                    // Not found!!! -> Hub and Spoke
                    bh.removeLastEntries(key, 1);
                    bh.add(key, a);  // New action event
                    if (bh.getLastAction(key) == FORMSUBMIT) {
                        a.setAction(FORMSUBMIT | a.getAction() );  // Submit was clicked
                    } else if (bh.getLastAction(key) == LINK) {
                        a.setAction(LINK | a.getAction() );  // Link was clicked
                        a.setLinkType(bh.getLastLinkType(key));
                    } else {
                        if (bh.getLastEvent(key).getNodeId() == referrerNodeId) {
                            a.setAction(LINK | HEURISTIC | a.getAction() );  // Link was probably clicked
                        }
                    }
                } else if (eventPosition == position) {
                    a.setAction(RELOAD | HEURISTIC | a.getAction() );  // page reloaded
                    if (bh.getLastAction(key) == FORMSUBMIT) {
                        a.setAction(FORMSUBMIT | a.getAction() );  // Submit was clicked and came to same page
                    } else if (bh.getLastAction(key) == LINK) {
                        a.setAction(LINK | a.getAction() );  // Link was clicked and came to same page
                        a.setLinkType(bh.getLastLinkType(key));
                    } 
                } else if (eventPosition <= position && (eventPosition > position - 2 ||  
                                (eventPosition > position - 16 && history < 50)) ) {  // Höchstens 16 und bei history == 50 mal ganz vorsichtig...
                    a.setAction(BACK | a.getAction() );  // back was probably clicked
                    if (( bh.getLastReferrer(key) != referrerNodeId && 
                          bh.getEventAt(key,eventPosition).getReferrerNodeId() != referrerNodeId)
                        || !fragment.equals("")) { 
                        a.setAction(HEURISTIC | a.getAction() );  // back several steps...
                    }
                    if (eventPosition != position - 1) {
                        a.setAction(SKIPPED | HEURISTIC | a.getAction() );  // back several steps...
                        a.setStepsInHistory(position - eventPosition );
                    }
                    bh.setPosition(key,eventPosition);
                } else if (eventPosition > position) {
                    if (bh.getLastAction(key) == FORMSUBMIT) {
                        a.setAction(FORMSUBMIT | a.getAction() );  // Submit was clicked and came to same page
                    } else if (bh.getLastAction(key) == LINK) {
                        a.setAction(LINK | a.getAction() );  // Link was clicked and came to same page
                        a.setLinkType(bh.getLastLinkType(key));
                    } else {
                        a.setAction(NEXT | HEURISTIC | a.getAction() );  // back was probably clicked
                        bh.setPosition(key,eventPosition);
                    }
                }
            }            
            if (history < historyLength) {  
                    bh.removeLastEntries(key, historyLength - history + 1);  // Delete last entries...
                    bh.add(key, a);  // New action event
                    if (bh.getLastAction(key) == FORMSUBMIT) {
                        a.setAction(FORMSUBMIT | a.getAction() );  // Submit was clicked
                    } else if (bh.getLastAction(key) == LINK) {
                        a.setAction(LINK | a.getAction() );  // Link was clicked
                    } else {
                        if (bh.getLastEvent(key).getNodeId() == referrerNodeId) {
                            a.setAction(LINK | HEURISTIC | a.getAction() );  // Link was probably clicked
                        }
                    }
            }            
                
        }
        else // New browser window
        {
            bh.add(key,a);  // New browser Frame and new action event
            if ( parentFrame.equals("") && parentNodeId.equals("") && frame.startsWith("SCONE")) {
                a.setAction(NEWWINDOW | a.getAction() );
                if (history > 1) {
                    System.out.println("---------------------------------------------------------------");
                    System.out.println("---- Browser history not empty! Please restart browser!!! -----");
                    System.out.println("---------------------------------------------------------------");
                }
            } else {
                a.setAction(NEWFRAME | a.getAction() );
            }
            if (history > 1) {
                for (int i=1; i < history ; i++) {
                    bh.add(key, new Access(userId,"",0,"") );  // Add dummy events...
                }
            }
        }

        // set fragment = "#top" etc.
        if (!fragment.equals("")) {
            a.setFragment(fragment);
            a.setAction(FRAGMENT | a.getAction() );
        }

        // a.setAction(action);
        // a.store(); -> Save if user leaves a page...
        AccessCache.broadcastEvent(new AccessEvent(a));   // Now broadcast event again with Action infos...!
        
        bh.setLastReferrer(key, referrerNodeId);
        bh.setLastAction(key, 0);  // Delete last action
        bh.setLastLinkType(key, 0);  // Delete last type
        bh.setLastLinkTitle(key, "");  // Delete 
        bh.setLastLinkAnchor(key, "");  // Delete 
        bh.setLastLinkPos(key, "");  // Delete 
        bh.setLastWindowSize(key, "");  // Delete 
    }

    /**
     * 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 userId
     * @param nodeId
     * @param startTime
     * @param frameName
     * @param referrerNodeId Last URL
     * @param parentFrameName Name of the parent frame
     * @param parentNodeId URL of the parent frame
     * @param loadedTime The time the page was loaded completly
     */
    void loadEvent(
                    String userId,
                    String nodeId,
                    long start,
                    String frame,
                    String referrerNodeId,
                    String parentFrame,
                    String parentNodeId,
                    long loadTime ) {

        if (frame.equals("") || nodeId.equals("") ) {
            // System.out.println("LOAD: Window name missing - probably and iframe!");
            return; // No frame name - no event
        }

        // Create key of this browser window/frame
        String key = bh.createKey(userId, parentFrame, frame);
        bh.setLastAction(key, 0);  // Delete last action

        // Get access object
        Access a = AccessCache.get(userId, nodeId, start, frame);

        if (a.getReferrerNodeId().equals("0")) {
            a.setReferrerNodeId(referrerNodeId);
        }
        if (a.getParentFrameName().equals("")) {
            a.setParentFrameName(parentFrame);
        }
        if (a.getParentFrameNodeId().equals("0")) {
            a.setParentFrameNodeId(parentNodeId);
        }
        a.setLoadTime(loadTime);
        a.store();  // maybe not really necessary...

        // System.out.println("EventDecoder:pageLoaded " + this.toString());
        // System.out.println("--- Methode pageLoaded gestartet");
       
        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
     */
    void stopEvent(
                    String userId,
                    String nodeId,
                    long start,
                    String frame,
                    String referrerNodeId,
                    String parentFrame,
                    String parentNodeId,
                    long stayTime,
                    long loadTime,
                    String linkId,
                    int linkType,
					String windowSize,
					String clickPos,
                    String formAction ) {

        if (frame.equals("") || nodeId.equals("") ) {
            // System.out.println("STOP: Window name missing - probably and iframe!");
            return; // No frame name - no event
        }

        // Create key of this browser window/frame
        String key = bh.createKey(userId, parentFrame, frame);

        // Get access object
        Access a = AccessCache.get(userId, nodeId, start, frame);
    
        a.setStayTime(stayTime);
        if (loadTime > 0 && a.getLoadTime()<=0)
        	a.setLoadTime(loadTime);
       
        a.setLinkId(linkId);

        String linkAnchor = "";
        String linkTitle  = "";
        Link link = LinkCache.getById(linkId);
        if (link != null) {
        	linkAnchor = link.getLinkText();
        	linkTitle = link.getTitle();
        }
        
        if (!linkId.equals("") && !linkId.equals("0")) {
            bh.setLastAction(key, LINK);
            bh.setLastLinkType(key, linkType);
            bh.setLastWindowSize(key, windowSize);
            bh.setLastLinkPos(key, clickPos);
            bh.setLastLinkAnchor(key, linkAnchor);
            bh.setLastLinkTitle(key, linkTitle);
            // System.out.println("LINK !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"+linkId);
        }
        if (!formAction.equals("")) {
            bh.setLastAction(key, FORMSUBMIT);
            // System.out.println("SUBMIT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        }


        // now save the data...
        a.store();


        // 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 called from AccesstrackingMeg. It decodes the events
     * it gets from firefox and creates and broadcasts accessEvents.
     *
     * @param userId
     * @param nodeId
     * @param start
     * @param frame
     * @param event the Event...
     * @param uriString URL of the current window
     * @param history Number of Frames in page
     */
    void event( 
                    String userId,
                    String nodeId,
                    long start,
                    String frame,
					String event,
                    String uriString,
                    int history ) {
    }
    
    public void printMsg(String textMsg) {
        if (showMessages) {
            System.out.println(textMsg);
        }
    }

}
