/*
 * 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;


/**
 * FrameSet stores two or more Access objects to allow FrameHistory to work with
 * frame sets
 *
 * @author Torsten Hass
 * @version 1.0b, 11/02/2001
 */
public class FrameSet {
    private final static int NEWURL = scone.accesstracking.EventDecoder.NEWURL;   // could not find out the way of opening this page

    private ArrayList frameList;
    private ArrayList timeList;
    private int lastChanged;         // number of entry that was changed last time

    public FrameSet() {
        frameList = new ArrayList();
        timeList = new ArrayList();
        lastChanged = 0;
    }

    /**
     * Adds a new Access object and a megTime to the FrameSet.
     *
     * @param megTime The time when the JavaScriptCode was generated
     * @param a The Access object to add to list
     */
    public void addToFrameSet(String megTime, Access a) {
        // System.out.println("FrameSet: addToFrameSet(" + megTime + ", "+a.toString() + ")");
        timeList.add(megTime);
        frameList.add(a);
        // System.out.println(timeList.size() + " frames in FrameSet");
        lastChanged = frameList.size() - 1;
    }

    /**
     * Adds a new FrameSet object and a megTime to the FrameSet.
     *
     * @param megTime The time when the JavaScriptCode was generated
     * @param a The   Access object to add to list
     */
    public void addToFrameSet(String megTime, FrameSet fs) {
        // System.out.println("FrameSet: addToFrameSet(" + megTime + ", "+ fs.toString() + ")");
        timeList.add(megTime);
        frameList.add(fs);
        // System.out.println(timeList.size() + " frames in FrameSet");
        lastChanged = frameList.size() - 1;
    }

    /**
     * Checks if the megTime is in the FrameSet
     *
     * @param megTime The megTime as a sting
     * @return <code>true</code> if the FrameSet contains megTime
     */
    public boolean contains(String megTime) {
        // System.out.println("FrameSet: Contains(" + megTime + ")");
        boolean result = false;
        int counter;

        for (counter = 0; counter < frameList.size(); counter++) {
            if (frameList.get(counter).getClass().getName().endsWith("FrameSet")) {
                // System.out.println("Rufe weiteres Frameset auf");
                if (((FrameSet) frameList.get(counter)).contains(megTime)) {
                    result = true;
                }
            } else {
                if (timeList.get(counter).equals(megTime)) {
                    result = true;
                }
            }
        }
        return result;
    }

    /**
     * Checks if the Access object matches one in the FrameSet in following
     * fields:<br>
     * userId, nodeId, frameName, fragment, query<br>
     * If one of the entries is a FrameSet itself, it is searched recursively
     * with it's <code>containsPageLike</code> method.
     *
     * @param a The Access object.
     * @return <code>true</code> if the Access object matches one in the
     *         FrameSet.
     */
    public boolean containsPageLike(Access a) {
        // System.out.println("FrameSet: containsPageLike(" + a.toString() + ")");
        int counter;
        boolean result = false;
        Access a1;

        for (counter = 0; counter < frameList.size(); counter++) {
            if (frameList.get(counter).getClass().getName().endsWith("FrameSet")) {
                if (((FrameSet) frameList.get(counter)).containsPageLike(a)) {
                    result = true;
                }
            } else {
                a1 = (Access) frameList.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;
    }

    /**
     * Get last access event
     *
     * @param frameName To identify the Access object
     * @return Access object
     */
    public Access getLastEvent(String frameName) {
        // System.out.println("FrameSet: getLastEvent(" + frameName + ")");
        int counter;

        // search all entries for frameName
        for (counter = 0; counter < frameList.size(); counter++) {
            if (frameList.get(counter).getClass().getName().endsWith("Access")) {
                if (((Access) frameList.get(counter)).getFrameName().equals(frameName)) {
                    // System.out.println("Referrer correcture: found matching frame name");
                    return (Access) frameList.get(counter);
                }
            } else { // FrameSet Object
                return ((FrameSet) frameList.get(counter)).getLastEvent(frameName);
            }
        }
        if ((lastChanged < frameList.size()) && (frameList.size() > 0)) {
            // if it is an access object
            if (frameList.get(lastChanged).getClass().getName().endsWith("Access")) {
                return (Access) frameList.get(lastChanged);
            } else { // if it is an FrameSet object
                return ((FrameSet) frameList.get(lastChanged)).getLastEvent(frameName);
            }
        } else {
            return null;
        }
    }

    /**
     * Get first access object of this FrameSet
     *
     * @return Access object
     */
    public Access getFirstEvent() {
        int counter;

        // search for the first access object
        for (counter = 0; counter < frameList.size(); counter++) {
            if (frameList.get(counter).getClass().getName().endsWith("Access")) {
                return (Access) frameList.get(counter);
            }
        }
        return null; // if no access object was found
    }

    /**
     * Sets the parentAccessIds of every Access object in this FrameSet.
     *
     * @param parentAccessId AccessId of the parent Access object
     */
    public void setParentAccessIds(String parentAccessId) {
        int counter;

        // search for access objects
        for (counter = 0; counter < frameList.size(); counter++) {
            if (frameList.get(counter).getClass().getName().endsWith("Access")) {
                // set parentAccessId of Access object
                ((Access) frameList.get(counter)).setParentAccessId(parentAccessId);
            }
        }
    }

    /**
     * Show FrameSet contents to std out.
     */
    public void showFrameSetContents() {
        int counter;

        System.out.println("Contents of Frame:");
        for (counter = 0; counter < frameList.size(); counter++) {
            if (frameList.get(counter).getClass().getName().endsWith("Access")) {
                System.out.println("   " + timeList.get(counter) + "  " + ((Access) frameList.get(counter)).getFrameName());
            } else { // FrameSet Object
                System.out.println("FrameStart");
                ((FrameSet) frameList.get(counter)).showFrameSetContents();
                System.out.println("FrameEnde");
            }
        }
    }

    /**
     * Test if all access events have stayTime = 0, so no event
     * was stopped by the applet.
     *
     * @return <code>true</code> if this FrameSet contains no stopped
     *         access objects.
     */
    public boolean noStoppedEvents() {
        // System.out.println("FrameSet: noStoppedEvents()");
        int counter;
        boolean result = true;

        for (counter = 0; counter < frameList.size(); counter++) {
            // if it is an access object
            if (frameList.get(counter).getClass().getName().endsWith("Access")) {
                if (((Access) frameList.get(counter)).getStayTime() != 0) {
                    result = false;
                }
            } else { // if it is an FrameSet object
                if (!((FrameSet) frameList.get(counter)).noStoppedEvents()) {
                    result = false;
                }
            }
        }
        return result;
    }

    /**
     * Copy unstopped events: all Access objects that have not been stopped
     * (have an stay time > 0) are Frames that have not changed in FrameSet
     * and have to be copied to the next FrameSet

     * @param source The FrameSet to get the unstopped Access object from.
     */
    public void copyUnstoppedEvents(FrameSet source) {
        // System.out.println("FrameSet: copyUnstopped Events(" + source.toString() + ")");
        int counter;
        boolean result = true;

        for (counter = 0; counter < source.getFrameListSize(); counter++) {
            // if it is an access object
            if (source.getFrame(counter).getClass().getName().endsWith("Access")) {
                // and stay time = 0 (unstopped)
                if (((Access) source.getFrame(counter)).getStayTime() == 0) {
                    // then copy the access object
                    addToFrameSet(source.getMegTime(counter), (Access) source.getFrame(counter));
                    // System.out.println("copying unstopped object: " + ((Access)source.getFrame(counter)).getFrameName());
                }
            } else { // if it is an FrameSet object
                // create new FrameSet
                FrameSet fs = new FrameSet();

                // copy all unstopped events
                fs.copyUnstoppedEvents((FrameSet) source.getFrame(counter));
                // add new frame set to this frame set
                if (fs.getFrameListSize() > 0) {
                    addToFrameSet(source.getMegTime(counter), fs);
                }
            }
        }
    }

    /**
     * Get number of entries in FrameSet. FrameSets, that are entries of
     * this FrameSet are counted as one entry.
     *
     * @return Number of entries
     */
    public int getFrameListSize() {
        return frameList.size();
    }

    /**
     * Get frame of a position
     *
     * @param number of FrameSet entry
     * @return FrameSet entry
     */
    public Object getFrame(int index) {
        return frameList.get(index);
    }

    /**
     * Get megTime of a position
     *
     * @param number of FrameSet entry
     * @return megTime
     */
    public String getMegTime(int index) {
        return (String) timeList.get(index);
    }

    /**
     * Replace last megTime and access object
     *
     * @param frameName To identify the frame to be replaced
     * @param megTime The megTime that replaces the previous one
     * @param a The Access object that replaces the previous one
     */
    public void replaceEvent(String frameName, String megTime, Access a) {
        int counter;

        for (counter = 0; counter < frameList.size(); counter++) {
            // if it is an access object
            if (frameList.get(counter).getClass().getName().endsWith("Access")) {
                // and frameName matches the frame name of the access object
                if (((Access) frameList.get(counter)).getFrameName().equals(frameName)) {
                    // then replace it with the new access object
                    frameList.set(counter, a);
                    // and the new megTime
                    timeList.set(counter, megTime);
                }
            } else { // if it is a FrameSet
                // then call 'replaceEvent' in that FrameSet
                ((FrameSet) frameList.get(counter)).replaceEvent(frameName, megTime, a);
            }
        }
    }

    /**
     * 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 < frameList.size(); counter++) {
            if (frameList.get(counter).getClass().getName().endsWith("FrameSet")) {
                ((FrameSet) frameList.get(counter)).replaceAllPagesLike(a, megTime);
            } else {
                a1 = (Access) frameList.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);
                }
            }
        }
    }
   
}
