/* ======================================================================
The Bodington System Software License, Version 1.0
  
Copyright (c) 2001 The University of Leeds.  All rights reserved.
  
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1.  Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2.  Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3.  The end-user documentation included with the redistribution, if any,
must include the following acknowledgement:  "This product includes
software developed by the University of Leeds
(http://www.bodington.org/)."  Alternately, this acknowledgement may
appear in the software itself, if and wherever such third-party
acknowledgements normally appear.

4.  The names "Bodington", "Nathan Bodington", "Bodington System",
"Bodington Open Source Project", and "The University of Leeds" must not be
used to endorse or promote products derived from this software without
prior written permission. For written permission, please contact
d.gardner@leeds.ac.uk.

5.  The name "Bodington" may not appear in the name of products derived
from this software without prior written permission of the University of
Leeds.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  TITLE,  THE IMPLIED WARRANTIES 
OF QUALITY  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO 
EVENT SHALL THE UNIVERSITY OF LEEDS OR ITS CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
=========================================================

This software was originally created by the University of Leeds and may contain voluntary 
contributions from others.  For more information on the Bodington Open Source Project, please 
see http://bodington.org/

====================================================================== */

package org.bodington.server.events;

import org.bodington.server.*;
import org.bodington.server.resources.Resource;
import org.bodington.sqldatabase.*;
import org.bodington.database.*;
import org.bodington.server.realm.Permission;
import org.bodington.server.realm.User;

import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Vector;
import java.util.Hashtable;


public class UserEventSetup extends org.bodington.sqldatabase.SqlPersistentObject
	{

	/**
	 * Primary key of this entry.
	 */
	private PrimaryKey user_event_setup_id;

	/**
	 * The user_id of the user this entry refers to.
	 */
	private PrimaryKey user_id;

	private java.sql.Timestamp	cutoff_time;
	private java.sql.Timestamp	email_cutoff_time;
	private int 				email_frequency;


	//transient data....
	private Hashtable res_in, res_out;
	
	private UserEventSetupIndexKey[] ikeys;

        
	public static UserEventSetup findUserEventSetup( PrimaryKey key )
	    throws BuildingServerException
	    {
	    return (UserEventSetup)findPersistentObject( key, "org.bodington.server.events.UserEventSetup" );
	    }
	
	public static UserEventSetup findUserEventSetup( String where )
	    throws BuildingServerException
	    {
	    return (UserEventSetup)findPersistentObject( where, "org.bodington.server.events.UserEventSetup" );
	    }
	
	public static UserEventSetup findUserEventSetupByUserId( PrimaryKey user_id )
	    throws BuildingServerException
	    {
	    UserEventSetupIndexKey ikey = new UserEventSetupIndexKey();
	    ikey.setUserId( user_id );
	    return (UserEventSetup)findPersistentObject( ikey, "org.bodington.server.events.UserEventSetup" );
	    }

        public static Enumeration findUserEventSetups( String where )
	    throws BuildingServerException
	    {
	    return findPersistentObjects( where, "org.bodington.server.events.UserEventSetup" );
	    }

	public UserEventSetup()
		{
		ikeys = new UserEventSetupIndexKey[1];
		ikeys[0] = new UserEventSetupIndexKey();
                cutoff_time = new java.sql.Timestamp( System.currentTimeMillis() );
		email_cutoff_time = new java.sql.Timestamp( System.currentTimeMillis() );
		email_frequency = 0;
		}
	

    public void setPrimaryKey(PrimaryKey key)
	    {
        setUserEventSetupId( key );
    	}

    public PrimaryKey getPrimaryKey()
    	{
        return getUserEventSetupId();
    	}

    public void setUserEventSetupId( PrimaryKey key )
	    {
	    user_event_setup_id = key;
	    setUnsaved();
	    }

    public PrimaryKey getUserEventSetupId()
	    {
        return user_event_setup_id;
	    }
	

    public void setUserId( PrimaryKey key )
	    {
	    user_id = key;
	    setUnsaved();
            ikeys[0].setUserId( key );
	    }

    public PrimaryKey getUserId()
	    {
        return user_id;
	    }
	
	public void setUser( User u )
		{
		setUserId( u.getUserId() );
		}
	public User getUser()
		throws BuildingServerException
		{
		return User.findUser( user_id );
		}



	public java.sql.Timestamp getCutoffTime()
		{
		return cutoff_time;
		}
	public void setCutoffTime( java.sql.Timestamp t )
		{
		cutoff_time = t;
        setUnsaved();
		}



	public java.sql.Timestamp getEMailCutoffTime()
		{
		return email_cutoff_time;
		}
	public void setEMailCutoffTime( java.sql.Timestamp t )
		{
		email_cutoff_time = t;
        setUnsaved();
		}


	public int getEMailFrequency()
		{
		return email_frequency;
		}
		
    public void setEMailFrequency( int n )
    	{
    	email_frequency = n;
        setUnsaved();
    	}


	public boolean containsResource( Resource resource )
		throws BuildingServerException
		{
		if ( resource==null ) return false;
		if ( user_id== null )
			throw new BuildingServerException( "Can't add resource to notifications for unknown user." );
		
		if ( res_in==null ) res_in = new Hashtable();
		if ( res_out==null ) res_out = new Hashtable();

		if ( res_in.containsKey( resource.getResourceId() ) )
			return true;
		
		if ( res_out.containsKey( resource.getResourceId() ) )
			return false;
			
		ResourceEventUser res_e_u = 
				ResourceEventUser.findResourceEventUser( 
						"user_id = " + user_id + " AND resource_id = " + resource.getResourceId() );
		
		if ( res_e_u ==null )
			res_out.put( resource.getResourceId(), resource.getResourceId() );
		else
			res_in.put( resource.getResourceId(), resource.getResourceId() );
			
		return ( res_e_u != null );
		}


	public void addResource( Resource resource )
		throws BuildingServerException
		{
		if ( resource==null ) return;
		if ( user_id== null )
			throw new BuildingServerException( "Can't add resource to notifications for unknown user." );
		
		if ( containsResource( resource ) )
			return;
			
		ResourceEventUser res_e_u;
		res_e_u = new ResourceEventUser();
		res_e_u.setUserId( user_id );
		res_e_u.setResource( resource );
		res_e_u.save();

		res_out.remove( resource.getResourceId() );
		res_in.put( resource.getResourceId(), resource.getResourceId() );
		}

	public void removeResource( Resource resource )
		throws BuildingServerException
		{
		if ( resource==null ) return;
		if ( user_id== null )
			throw new BuildingServerException( "Can't remove resource from notifications for unknown user." );
		
		if ( !containsResource( resource ) )
			return;
			
		ResourceEventUser res_e_u = 
				ResourceEventUser.findResourceEventUser( 
						"user_id = " + user_id + " AND resource_id = " + resource.getResourceId() );
						
		res_e_u.delete();
		
		res_in.remove( resource.getResourceId() );
		res_out.put( resource.getResourceId(), resource.getResourceId() );
		}

		
	public Enumeration findResourceIds()
		throws BuildingServerException
		{
		if ( user_id== null )
			throw new BuildingServerException( "Can't find resources for unknown user." );

		Vector resource_ids = new Vector();
		ResourceEventUser reu;
		
		Enumeration enumeration = ResourceEventUser.findResourceEventUsers( "user_id = " + user_id );
		while ( enumeration.hasMoreElements() )
			{
			reu  = (ResourceEventUser)enumeration.nextElement();
			resource_ids.addElement( reu.getResourceId() );
			}
		return resource_ids.elements();
		}
		
	public Enumeration findResources()
		throws BuildingServerException
		{
		Enumeration enumeration = findResourceIds();
		// if there are no resources on list return empty enumeration
		if ( !enumeration.hasMoreElements() )
    	    return enumeration;
		StringBuffer where = new StringBuffer();
		PrimaryKey resource_id;
		where.append( "resource_id IN ( " );
		for ( int i=0; enumeration.hasMoreElements(); i++ )
			{
			resource_id = (PrimaryKey)enumeration.nextElement();
			if ( i>0 )
				where.append( ", " );
			where.append( resource_id.toString() );
			}
		where.append( " )" );
		
		return Resource.findResources( where.toString(), "left_index" );
		}

	public Enumeration findEventsForResource( Resource resource )
		throws BuildingServerException
		{
		return findEventsForResource( resource, cutoff_time );
		}
		
	public Enumeration findEventsForResource( Resource resource, java.sql.Timestamp since )
		throws BuildingServerException
		{
		StringBuffer where = new StringBuffer();
		int importance=Event.IMPORTANCE_USER_MAX+1;
		
		User user = getUser();
		
		if ( resource.checkPermission( user, Permission.VIEW ) )
			importance = Event.IMPORTANCE_USER_MIN;
		
		if ( resource.checkPermission( user, Permission.MANAGE ) )
			importance = Event.IMPORTANCE_MANAGEMENT_MIN;
		
		if ( resource.checkPermission( user, Permission.ADMINISTER ) )
			importance = Event.IMPORTANCE_ADMIN_MIN;
		
		if ( resource.checkPermission( user, Permission.SYSADMIN ) )
			importance = Event.IMPORTANCE_SYSTEM_MIN;
		
		// uses standard JDBC SQL escape sequence for the timestamp
		// to make the JDBC driver convert to the right format string
		
		where.append( "event_time > {ts '" );
		where.append( since );
		where.append( "'} AND resource_id = " );
		where.append( resource.getResourceId() );
		where.append( " AND importance >= " );
		where.append( importance );
		
		return Event.findEvents( where.toString(), "event_time" );
		}
	
	public IndexKey[] getIndexKeys()
	    {
	    
	    if ( ikeys[0].getUserId() == null )
	        return null;

	    return ikeys;
	    }
	    
	public boolean matchesKey( IndexKey testikey )
	    {
	    return testikey.equals( ikeys[0] );
	    }
        
	public int lifetime()
		{
		return 60;
		}

	/**
	 * Finds all the events for a resource that are visible to the current user.
	 * This prevents the calling code worrying about doing checks.
	 * This should really be in a manager class.
	 * @param resource The resource for which we want events.
	 * @return An enumertion of all the visible events.
	 * @throws BuildingServerException 
	 * @see Event#checkPermission() 
	 */
	public Enumeration findVisibleEventsForResource(Resource resource) throws BuildingServerException {
		return new CheckPermissionEnumeration(findEventsForResource(resource));

	}
	
	/**
	 * A wrapper around an enumeration that checks if we have permission
	 * to view the next element.
	 * For this to work all elements of the enumeration must be {@link Event}s.
	 * Having a wrapper saves copying all the elements from one enumeration 
	 * to another one.
	 * @author buckett
	 */
	class CheckPermissionEnumeration implements Enumeration {

		Enumeration source;
		Object next = null;
		
		public CheckPermissionEnumeration (Enumeration source) {
			this.source = source;
		}
		
		public boolean hasMoreElements() {
			if (next != null) {
				return true;
			} else {
				if (source.hasMoreElements()) {
					Object temp = source.nextElement();
					if ( ((Event)temp).checkPermission() ) {
						next = temp;
						return true;
					}
					return hasMoreElements();
				}
				return false;
			}
				
		}

		public Object nextElement() {
			if (hasMoreElements()) {
				Object temp = next;
				next = null;
				return temp;
			}
			throw new NoSuchElementException();
				
		}
		
	}
	
	}
