/*
 * 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.util.ArrayList;

import scone.netobjects.Access;


/**
 * Keeps the history of one Window/Frame of a Web-browser to amalyse the
 * movements of the user.
 *
 * @author Torsten Hass
 * @version 1.0b, 11/02/2001
 */

public class FrameHistory {
    private final static int NEWURL = scone.accesstracking.EventDecoder.NEWURL;   // could not find out the way of opening this page

    private ArrayList accessList; // stores Access and Framset
    private ArrayList timeList;   // stores megTime for accessList
    private int position = 0;     // current position in history
    private int lastAction;       // action that made the browser load next page
    // private String lastMegTime;   // last time of creating JavaScript

    public FrameHistory() {
        accessList = new ArrayList();
        timeList = new ArrayList();
        position = 0;
        lastAction = NEWURL;
    }

    /**
     * Removes history entries behind actual position
     * and adds the new Access object to history list.
     *
     * @param megTime The time when the JavaScriptCode was generated
     * @param a The Access object to add to list
     */
    public void add(String megTime, Access a) {
        // System.out.println("FrameHistory: add(" + megTime + ", " + a.toString() + ")");
        removeLastEntries(timeList.size() - 1 - position);
        timeList.add(megTime);
        accessList.add(a);
        position = timeList.size() - 1;
        // System.out.println("FrameHistory: Pos=" + position + " size=" + timeList.size());
    }

    /**
     * Removes history entries behind actual position
     * and adds the new FrameSet object to history list.
     *
     * @param megTime The time when the JavaScriptCode was generated
     * @param frameSet The FrameSet object to add to list
     */
    public void add(String megTime, FrameSet frameSet) {
        // System.out.println("FrameHistory: add(" + megTime + ", " + frameSet.toString() + ")");
        removeLastEntries(timeList.size() - 1 - position);
        timeList.add(megTime);
        accessList.add(frameSet);
        position = timeList.size() - 1;
        // System.out.println("FrameHistory: Pos=" + position + " size=" + timeList.size());
    }

    /**
     * Inserts the new FrameSet object without removing all history entries
     * behind actual position and increases the position pointer.
     *
     * @param megTime The time when the JavaScriptCode was generated
     * @param frameSet The FrameSet object to insert into FrameHistory
     */
    public void insert(String megTime, FrameSet frameSet) {
        // System.out.println("FrameHistory: insert(" + megTime + ", " + frameSet.toString() + ")");
        position++;
        timeList.add(position, megTime);
        accessList.add(position, frameSet);
        // System.out.println("FrameHistory: Pos=" + position + " size=" + timeList.size());
    }

    /**
     * checks if the megTime is in this FrameHistory
     *
     * @param megTime The megTime as a sting
     * @return True if the list contains megTime
     */
    public boolean contains(String megTime) {
        // System.out.println("FrameHistory: contains(" + megTime + ")");
        boolean result = false;
        int counter;

        for (counter = 0; counter < accessList.size(); counter++) {
            if (accessList.get(counter).getClass().getName().endsWith("FrameSet")) {
                if (((FrameSet) accessList.get(counter)).contains(megTime)) {
                    result = true;
                }
            } else { // access event
                // System.out.println("FrameHistory:contains Teste AccessObject");
                // System.out.println(megTime + " = " + timeList.get(counter));
                if (timeList.get(counter).equals(megTime)) {
                    result = true;
                }
            }
        }
        return result;
    }

    /**
     * Checks if the given Access object matches one in the FrameHistory
     * in following fields:<br>
     * userId, nodeId, frameName, fragment, query<br>
     * If there a FrameSets in this FrameHistory, FrameSet.containsPageLike
     * is invoked in the FrameSet and subframesets
     *
     * @param a The Access object
     * @return <code>true</code> if "a" matches one in the FrameHistory
     */
    public boolean containsPageLike(Access a) {
        int counter;
        boolean result = false;
        Access a1;

        for (counter = 0; counter < accessList.size(); counter++) {
            if (accessList.get(counter).getClass().getName().endsWith("FrameSet")) {
                if (((FrameSet) accessList.get(counter)).containsPageLike(a)) {
                    result = true;
                }
            } else {
                a1 = (Access) accessList.get(counter);
                if (a1.getUserId().equals(a.getUserId())
                        && a1.getNodeId().equals(a.getNodeId())
                        && a1.getFrameName().equals(a.getFrameName())
                        && a1.getFragment().equals(a.getFragment())
                        && a1.getQuery().equals(a.getQuery())
                        && a1.getParentFrameNodeId().equals(a.getParentFrameNodeId())
                        && a1.getParentFrameName().equals(a.getParentFrameName())) {
                    // System.out.println("--- found matching access object ---");
                    result = true;
                }
            }
        }
        return result;
    }

    /**
     * Removes the last <number> of entries from this FrameHistory.
     *
     * @param number Number of entries to remove
     */
    private void removeLastEntries(int number) {
        int counter;

        for (counter = 1; counter <= number; counter++) {
            if (!timeList.isEmpty()) {
                timeList.remove(timeList.size() - 1);
            }
            if (!accessList.isEmpty()) {
                accessList.remove(accessList.size() - 1);
            }
        }
    }

    /*
     * Get last meg time
     *
     * @return last meg time
     public String getLastMegTime () {
     return lastMegTime;
     }

     * Set last meg time
     * @param last meg time
     public void setLastMegTime (String megTime) {
     lastMegTime = megTime;
     }
     */

    /**
     * Get lastAction
     *
     * @return bit Array that holds reason for last new page
     */
    public int getLastAction() {
        return lastAction;
    }

    /**
     * Set lastAction
     *
     * @param lastAction Bit array that holds reason for last page change
     */
    public void setLastAction(int lastAction) {
        this.lastAction = lastAction;
    }

    /**
     * Get position in history
     *
     * @return position in history
     */
    public int getPosition() {
        return position;
    }

    /**
     * Set position in history
     *
     * @param position Position in history
     */
    public void setPosition(int position) {
        this.position = position;
    }

    /**
     * Get last access event
     *
     * @return Access event
     */
    public Access getLastEvent(String frameName) {
        if ((position < accessList.size()) && (accessList.size() > 0)) {
            if (accessList.get(position).getClass().getName().endsWith("Access")) {
                return (Access) accessList.get(position);
            } else { // if it is an FrameSet object
                return ((FrameSet) accessList.get(position)).getLastEvent(frameName);
            }
        } else {
            System.out.println("FrameHistory.getLastEvent(" + frameName + "): value of variable position outside of list. position = " + (position + 1) + ". Item, " + accessList.size() + " Items in FrameHistory.");
            return null;
        }
    }

    /**
     * Get last accessed entry of FrameHistory
     *
     * @return Access or FrameSet object
     */
    public Object getLastEntry() {
        // System.out.println("FrameHistory: getLastEntry()");
        if ((position < accessList.size()) && (accessList.size() > 0)) {
            return accessList.get(position);
        } else {
            return null;
        }
    }

    /**
     * Get previous accessed entry of FrameHistory (That one accessed
     * before the last accessed one)
     *
     * @return Access or FrameSet object
     */
    public Object getPrevEntry() {
        if ((0 <= (position - 1)) && ((position - 1) < accessList.size())
                && (accessList.size() > 0)) {
            // System.out.println("Position = " + position + " Accesslistsize = " + accessList.size());
            return accessList.get(position - 1);
        } else {
            return null;
        }
    }

    /**
     * Replace last megTime and access event
     *
     * @param megTime
     * @param a
     */
    public void replaceLastEvent(String megTime, Access a) {
        if (!timeList.isEmpty() && (position >= 0)
                && (position < timeList.size())) {
            timeList.set(position, megTime);
        }
        if (!accessList.isEmpty() && (position >= 0)
                && (position < accessList.size())) {
            accessList.set(position, a);
        }
    }

    /**
     * Delete the previous FrameHistory entry.
     */
    public void delPrevEntry() {
        if ((0 <= (position - 1)) && ((position - 1) < accessList.size())
                && (accessList.size() > 0)) {
            accessList.remove(position - 1);
            timeList.remove(position - 1);
            position--;
        }
    }

    /**
     * Replace last megTime and access event.
     * This method has an extra parameter frameName to identify
     * the Access object, that has to be replaced
     *
     * @param frameName  Identifies the frame
     * @param megTime
     * @param a          The Access Event
     */
    public void replaceLastEvent(String frameName, String megTime, Access a) {
        if (!timeList.isEmpty() && (position >= 0)
                && (position < timeList.size())) {
            if (accessList.get(position).getClass().getName().endsWith("Access")) {
                timeList.set(position, megTime);
                accessList.set(position, a);
            } else { // it is a FrameSet
                ((FrameSet) accessList.get(position)).replaceEvent(frameName, megTime, a);
                timeList.set(position, megTime);
            }
        }
    }

    /**
     * Get distance of position and Access object a in history
     *
     * @param megTime
     * @return distance of position and a in history
     */
    public int getDistance(String megTime) {
        // System.out.println("FrameHistory: getDistance(" + megTime + ")");
        int counter;
        int result = Integer.MAX_VALUE;

        // test every entry
        for (counter = 0; counter < accessList.size(); counter++) {
            // if it is an access object
            if (accessList.get(counter).getClass().getName().endsWith("Access")) {
                // then compare timeList entry with megTime
                if (((String) timeList.get(counter)).equals(megTime)) {
                    result = counter - position;
                }
            } else { // if it is an FrameSet object
                // test if there is an entry with megTime in frameset
                if (((FrameSet) accessList.get(counter)).contains(megTime)) {
                    result = counter - position;
                }
            }
        }
        return result;
    }

    /**
     * Get distance of position and matching Access Object a in the
     * FrameHistory.
     *
     * @param a The Access object
     * @return The distance of position and a in history
     */
    public int getDistanceOfPageLike(Access a) {
        int counter;
        int distance = 0;
        int diff = 0;
        Access a1;

        for (counter = 0; counter < accessList.size(); counter++) {
            if (accessList.get(counter).getClass().getName().endsWith("FrameSet")) {
                if (((FrameSet) accessList.get(counter)).containsPageLike(a)) {
                    distance = counter - position;
                }
            } else {
                a1 = (Access) accessList.get(counter);
                if (a1.getUserId().equals(a.getUserId())
                        && a1.getNodeId().equals(a.getNodeId())
                        && a1.getFrameName().equals(a.getFrameName())
                        && a1.getFragment().equals(a.getFragment())
                        && a1.getQuery().equals(a.getQuery())
                        && a1.getParentFrameNodeId().equals(a.getParentFrameNodeId())
                        && a1.getParentFrameName().equals(a.getParentFrameName())) {
                    distance = counter - position;
                }
            }
        }
        return distance;
    }

    /**
     * Show the contents of the FrameHistory.
     *
     * @return distance of position and a in history
     */
    public void showFrameContents() {
        int counter;

        for (counter = 0; counter < accessList.size(); counter++) {
            if (accessList.get(counter).getClass().getName().endsWith("FrameSet")) {
                System.out.println(counter + " FrameSet");
                ((FrameSet) accessList.get(counter)).showFrameSetContents();
            } else { // it's not a FrameSet, it's an Access object
                System.out.println(counter + " Access obj. with FrameName = " + ((Access) accessList.get(counter)).getFrameName());
            }
        }
    }

    /**
     * Set position to Access object a in history
     *
     * @param megTime Access object to set position to
     */
    public void setPositionTo(String megTime) {
        int counter;

        for (counter = 0; counter < accessList.size(); counter++) {
            // if it is an access object
            if (accessList.get(counter).getClass().getName().endsWith("Access")) {
                if (timeList.get(counter).equals(megTime)) {
                    position = counter;
                    // frameSequence.add(position);
                }
            } else { // if it is a FrameSet object
                if (((FrameSet) accessList.get(counter)).contains(megTime)) {
                    position = counter;
                    // frameSequence.add(position);
                }
            }
            // System.out.println("current position in history = " + position);
        }
    }

    /**
     * Set position to an Access object that matches a in history
     * @param a Access object to set position to
     */

    /* public void setPositionToPageLike (Access a) {
     int counter;
     int distance = 0;
     int diff = 0;
     Access a1;

     for(counter = 0; counter < list.size(); counter++) {
     a1 = (Access)list.get(counter);
     if (a1.getUserId().equals(a.getUserId()) &&
     a1.getNodeId().equals(a.getNodeId()) &&
     a1.getFrameName().equals(a.getFrameName()) &&
     a1.getFragment().equals(a.getFragment()) &&
     a1.getQuery().equals(a.getQuery()) &&
     a1.getParentFrameNodeId().equals(a.getParentFrameNodeId()) &&
     a1.getParentFrameName().equals(a.getParentFrameName())) {
     position = counter;
     }
     }
     }
     */


    /**
     * Replace the megTime of all pages like this one.<br>
     * This method is needed because the Internet Explorer 
     * stores only one copy of each page in it's history.
     * If a page is called more then one time, the old entry
     * in the History is replaced by the new one. If page A 
     * is called by a browser window and it was called by 
     * another browser window before, AccessTracking will
     * not recognize the page if the user uses the back button
     * to navigate back to page A. This is because the old 
     * megTime was erased when IE replaced the cache copy.
     * 
     * @param a The access object to compare the others with
     * @param megTime The new megTime to save with all similar access objects
     */
    void replaceAllPagesLike(Access a, String megTime) {
        int counter;
        Access a1;

        for (counter = 0; counter < accessList.size(); counter++) {
            if (accessList.get(counter).getClass().getName().endsWith("FrameSet")) {
                ((FrameSet) accessList.get(counter)).replaceAllPagesLike(a, megTime);
            } else {
                a1 = (Access) accessList.get(counter);
                if (a1.getUserId().equals(a.getUserId())
                        && a1.getNodeId().equals(a.getNodeId())
                        && a1.getFragment().equals(a.getFragment())
                        && a1.getQuery().equals(a.getQuery())
                        && a1.getParentFrameNodeId().equals(a.getParentFrameNodeId())) {
                    timeList.set(counter, megTime);
                }
            }
        }
    }
   
}
