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


import java.sql.Connection;
import java.sql.ResultSet;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;

import scone.util.ErrorLog;


/**
 * caches the <B>access</B> table from the database and provides means to update the database
 *
 * @author Harald Weinreich
 * @author Volkert Buchmann
 */

public class AccessCache extends Observable implements TableRecordCacheInterface {

    protected static CacheTable cache;

    public static Object lock = new Object();

    /**
     * creates a new CacheTable, generates a new Connection and a lock object
     * @param timeTillCleanUp how long between runs of clean()
     * @param gcType how soon are the objects removed from the memory
     */
    public static void init(int timeTillCleanUp, int gcType) {
        // 1 = How many keys are used to access the fields?
        cache = new CacheTable("Access Objects", 1, timeTillCleanUp, gcType);
        lock = new Object();
        observable = new AccessCache();
    }

    /**
     * returns an Access object to a specific key
     * @param userId: The id of the user (from cookie)
     * @param nodeId: The current Node
     * @param time: The (start) time of the access
     * @param frameName: Name of the frame or window (given or generated by <I>Scone</I>)
     */
    public static Access get(String userId, String nodeId, long time, String frameName) {
        String userIdString = userId;
        String nodeIdString = nodeId;
        String timeString = String.valueOf(time);
        Access a = null;
        String key = createKey(userIdString, nodeIdString, timeString, frameName);

        synchronized (lock) {
            // System.out.println(key);
            a = (Access) cache.get(0, key);
            if (a == null) {
                a = new Access(userId, nodeId, time, frameName);
                cache.put(a);

                // broadcastEvent(new AccessEvent(a));  // Removed by HW as events were raised too early!

            }
        }
        return a;
    }

    /**
     * This method returns a Vector of Access-Objects to a given query string
     * @param sqlClause The where clause for the query string.
     */
    public static Vector getAccesses(String sqlClause) {
        Connection con = Access.dbTable.getConnection();
        Vector accesses = new Vector();
        int i = 0;

        synchronized (lock) {
            ResultSet results = Access.dbTable.queryDb(con, Access.dbTable.getTableName(), sqlClause);

            // eval all records returned...
            while (results != null) {
                Access a = new Access(results);  // Create new use object
                String id = a.getAccessId();

                if (id.length() > 0 && !id.equals("-1")) // another set read from DB
                {
                    // System.out.println(results.getString("userId") + "  " +results.getString("userName"));
                    if (cache.get(0, id) == null) {   // user not known in cache...
                        cache.put(a);
                    }
                    accesses.add(a);
                } else {  // End of results reached!
                    return accesses;
                }
            }
        }
        return accesses;
    }

    /**
     * This method returns a number of Accesses to a given query string.
     * @param tables The table names.
     * @param sqlClause The where clause for the query string.
     * @return The number of nodes.
     */
    public static int getAccessCount(String tables, String sqlClause) {
        Connection con = Access.dbTable.getConnection();
        int count;

        synchronized (lock) {
            count = Access.dbTable.rowCountDb(con, tables, sqlClause);
        }
        return count;
    }

    /**
     * This method returns a number of Accesses to a given query string.
     * @param sqlClause The where clause for the query string.
     * @return The number of nodes.
     */
    public static int getAccessCount(String sqlClause) {
        return getAccessCount(Access.dbTable.getTableName(), sqlClause);
    }

    public static String createKey(String userId, String nodeId, String timeString, String frameName) {
        return userId + "/" + nodeId + "/" + timeString + "+" + frameName;
    }

    public static void clean() {
        cache.clean();
    }

    // the static implementation of Observable

    // this instance will notify the Observers
    protected static AccessCache observable;

    /**
     * adds an Observer to the AccessCache class.
     * <br>
     * Use this static method instead of addObserver() !
     * @param o the Observer
     */
    public static void putObserver(Observer o) {
        observable.addObserver(o);
    }

    /**
     * removes an Observer from the AccessCache class.
     * <br>
     * Use this static method instead of deleteObserver() !
     * @param o the Observer
     */
    public static void removeObserver(Observer o) {
        observable.deleteObserver(o);
    }

    // notify the observers immediatly
    public static void broadcastEvent(AccessEvent event) {
        try {
            observable.setChanged();
            observable.notifyObservers(event);
        } catch (Exception e) {
            ErrorLog.log("AccessCache", "broadcastEvent", "An error occured when triggering the access-event", e);
            System.err.println(e.toString());
        }
    }

}
