/*
 * 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.HashMap;
import java.util.Iterator;
import java.util.Map;

import scone.netobjects.Access;


/**
 * This class stores a map of keys, containing userId, parent frame name and
 * frame name. Each key object is mapped to a list, storing the visited
 * Access objects in order of visiting.
 * It can be instanciated only once (Singleton pattern).
 *
 * @author Torsten Hass
 */

class BrowserHistory {
    private static BrowserHistory _instance = null;
    private Map map;
    private FrameHistory frameHistory;

    /**
     * Get a sole instance of BrowserHistory, using the Singleton pattern.
     */
    static BrowserHistory getInstance() {
        if (_instance == null) {
            _instance = new BrowserHistory();
            // System.out.println("New instance of bH");
        } else {}
        return _instance;
    }

    private BrowserHistory() {
        map = new HashMap();
    }

    /**
     * returns true if the key is already known.
     * @param key The String to be tested
     * @return True if the string exists.
     */
    boolean containsKey(String key) {
        return map.containsKey(key);
    }

    /**
     * This Method adds a new Access object to the history list of
     * a specific key.
     * If this key does not exist, the key is added to the map and
     * the Access object is stored in a new list.
     * @param key The key to identify user and browser frame
     * @param megTime The megtime
     * @param a The Access object to be added to list
     */
    void add(String key, String megTime, Access a) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            frameHistory.add(megTime, a);
            // System.out.println("BrowserHistory: added Acceess object to " + key);
        } else {
            frameHistory = new FrameHistory();
            map.put(key, frameHistory);
            frameHistory.add(megTime, a);
            // System.out.println("BrowserHistory: created new key: " + key);
            // System.out.println("BrowserHistory: added Access object to " + key);
        }
    }

    /**
     * This Method adds a new FrameSet object to the history list of
     * a specific key.
     * If this key does not exist, the key is added to the map and
     * the Access object is stored in a new list.
     * @param key The key to identify user and browser frame
     * @param frameSet
     */
    void add(String key, String megTime, FrameSet frameSet) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            frameHistory.add(megTime, frameSet);
            // System.out.println("BrowserHistory: added FrameSet object to " + key);
        } else {
            frameHistory = new FrameHistory();
            map.put(key, frameHistory);
            frameHistory.add(megTime, frameSet);
            // System.out.println("BrowserHistory: created new key: " + key);
            // System.out.println("BrowserHistory: added FrameSet object to " + key);
        }
    }

    /**
     * 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
     */
    void insert(String key, String megTime, FrameSet frameSet) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            frameHistory.insert(megTime, frameSet);
        } else {
            frameHistory = new FrameHistory();
            map.put(key, frameHistory);
            frameHistory.insert(megTime, frameSet);
        }
    }

    /**
     * Tests if a given Access object is part of the history list
     * @param key The key to identify user and browser frame
     * @param a The Access object to be searched in the list
     * @return true if a was found
     */
    boolean contains(String key, String megTime) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            if (frameHistory.contains(megTime)) {// System.out.println("BrowserHistory: " + uri + " is in " + key);
            } else {// System.out.println("BrowserHistory: " + uri + " is not in " + key);
            }
            return frameHistory.contains(megTime);
        } else {
            // System.out.println("BrowserHistory: Key: " + key + " does not exist");
            return false;
        }
    }

    /**
     * Tests if a given Access object matches all fields except
     * startTime, action and linkId and referrer
     * @param key The key to identify user and browser frame
     * @param a The Access object to be searched in the list
     * @return true if a was found
     */
    boolean containsPageLike(String key, Access a) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            return frameHistory.containsPageLike(a);
        } else {
            return false;
        }
    }

    /**
     * removes the last count Access objects from history list.
     * @param key The key to identify user and browser frame
     * @param count Number of Access objects to remove
     */

    /* public void removeLastEntries (String key, int count) {
     int counter;

     if (map.containsKey(key)) {
     frameHistory = (FrameHistory)map.get(key);
     frameHistory.removeLastEntries(count);
     } else {
     //         System.out.println("BrowserHistory: Key: " + key + " does not exist.");
     //         System.out.println("BrowserHistory: Could not remove elements from list");
     }
     }
     */
    
    /**
     * Creates a Key value that identifies the browser frame
     * Puts userId as an 16 digit string and parent frame name together and
     * returns it. If the page contains no frames and so parentFrameName is
     * empty, frameName is used.
     * @param userId
     * @param parentFrameName
     * @param frameName
     * @return All params in one string
     */
    String createKey(String userId, String parentFrameName, String frameName) {
        int counter;
        String key = "";

        // convert UserId to a 16 character string
        for (counter = userId.length(); counter < 16; counter++) {
            key += "0";
        }
        key += userId;
        // System.out.println("UserId = " + key);
        if (parentFrameName.equals("")) {
            key += frameName;
        } else {
            key += parentFrameName;
        }
        return key;
    }

    /**
     * Gets the megTime of the last Access object
     * @param key The key to identify user and browser frame
     * @return last meg time
     */

    /* public String getLastMegTime (String key) {
     if (map.containsKey(key)) {
     frameHistory = (FrameHistory)map.get(key);
     return frameHistory.getLastMegTime();
     } else {
     return "0";
     }
     }
     */
    
    /**
     * Set last meg time
     * @param key The key to identify user and browser frame
     * @param last meg time
     void setLastMegTime (String key, String megTime) {
     if (map.containsKey(key)) {
     frameHistory = (FrameHistory)map.get(key);
     frameHistory.setLastMegTime(megTime);
     }
     }
     */

    /**
     * Get lastAction
     * @param key The key to identify user and browser frame
     * @return bit Array that holds reason for last new page
     */
    int getLastAction(String key) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            return frameHistory.getLastAction();
        } else {
            return -1;
        }
    }

    /**
     * Set lastAction
     * @param key The key to identify user and browser frame
     * @param lastAction Bit array that holds reason for last page change
     */
    void setLastAction(String key, int lastAction) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            frameHistory.setLastAction(lastAction);
            // System.out.println("LastAction of key: " + key + " set to: " + lastAction);
        }
    }

    /**
     * Get last access event, the one the position points to
     * @param key The key to identify user and browser frame
     * @param frameName The name of the frame in case of a frame set
     * @return Access event
     */
    Access getLastEvent(String key, String frameName) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            return frameHistory.getLastEvent(frameName);
        } else {
            return null;
        }
    }

    /**
     * Get last entry of frame history list
     * @param key The key to identify user and browser frame
     * @return Access or FrameSet object
     */
    Object getLastEntry(String key) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            return frameHistory.getLastEntry();
        } else {
            return null;
        }
    }

    /**
     * Get previous entry of frame history list (that one before the last one)
     * @param key The key to identify user and browser frame
     * @return Access or FrameSet object
     */
    Object getPrevEntry(String key) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            return frameHistory.getPrevEntry();
        } else {
            System.out.println("AccessTracking:BrowserHistory: getPrevEntry gibt null zurück");
            return null;
        }
    }

    /**
     * Show history contents for one Frame
     */
    void showFrameContents(String key) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            frameHistory.showFrameContents();
        } else {
            System.out.println("AccessTracking:showFrameContents: No Entry for key " + key + " found");
        }
    }

    /**
     * Delete the previous history entry of a special frame
     */
    void delPrevEntry(String key) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            frameHistory.delPrevEntry();
        }
    }

    /**
     * Replace last access event with a
     * @param key The key to identify user and browser frame
     * @param megTime
     * @param a
     */
    void replaceLastEvent(String key, String megTime, Access a) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            frameHistory.replaceLastEvent(megTime, a);
        }
    }

    /**
     * Replace last access event with a
     * This method has an extra parameter frameName to identify
     * the Access object, that has to be replaced
     * @param key The key to identify user and browser frame
     * @param megTime
     * @param frameName
     * @param a
     */
    void replaceLastEvent(String key, String frameName, String megTime, Access a) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            frameHistory.replaceLastEvent(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) {
        FrameHistory frameHistory;
        int counter = 0;
        Iterator contents;
      
        contents = map.keySet().iterator();
        while (contents.hasNext()) {
            frameHistory = (FrameHistory) map.get(contents.next());
            frameHistory.replaceAllPagesLike(a, megTime);
        }
    }

    /**
     * Get distance of position and Access object a in history
     * @param key The key to identify user and browser frame
     * @param megTime
     * @param frameName
     * @return distance of position and a in history
     */
    int getDistance(String key, String megTime) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            return frameHistory.getDistance(megTime);
        } else {
            return Integer.MAX_VALUE;
        }
    }

    /**
     * Get distance of position and Access object that matches a page
     * in history
     * @param key The key to identify user and browser frame
     * @param a The Access object
     * @return distance of position and a in history
     */
    int getDistanceOfPageLike(String key, Access a) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            return frameHistory.getDistanceOfPageLike(a);
        } else {
            return Integer.MAX_VALUE;
        }
    }

    /**
     * Set position to Access object a in history
     * @param key The key to identify user and browser frame
     * @param a The Access object to set it's position in history
     */
    void setPositionTo(String key, String megTime) {
        if (map.containsKey(key)) {
            frameHistory = (FrameHistory) map.get(key);
            frameHistory.setPositionTo(megTime);
        }
    }

    /**
     * Get frame history object to lock it with synchronized
     * If the FrameHistory does not exist, it is created
     * @param key The key to identify user and browser frame
     * @return The FrameHistory object
     */
    synchronized FrameHistory getFrameHistory(String key) {
        if (map.containsKey(key)) {
            return (FrameHistory) map.get(key);
        } else {
            frameHistory = new FrameHistory();
            map.put(key, frameHistory);
            return frameHistory;
        }
    }

}   

