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

import org.apache.log4j.Logger;

import java.rmi.RemoteException;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import javax.swing.tree.TreeNode;
import org.bodington.database.PrimaryKey;
import org.bodington.server.*;
import org.bodington.server.realm.*;
import org.bodington.server.resources.Resource;
import org.bodington.server.realm.User;
import org.bodington.server.realm.Permission;
import org.bodington.server.events.MessagingEvent;
import org.bodington.messaging.*;
import org.bodington.text.BigString;
import org.bodington.util.Visibility;
import org.bodington.database.*;

public class MessagingRoomSessionImpl 
	extends org.bodington.server.SingleResourceSession 
	implements org.bodington.messaging.MessagingRoomSession
	{
    
    private static Logger log = Logger.getLogger(MessagingRoomSessionImpl.class);
    
	//if the client needs the server to keep track of
	//open and colapsed branches the ids of visible
	//nodes will be stored in this hashtable.
	//keys are message_ids values are Visibility
	//if a message doesn't have an entry it is implied that it is 
	//closed and only visible if its parent has an entry and 
	//is visible or if it is a top level message.
	private Hashtable visible_message_ids=null;


	//keys to messages that this user in this session
	//has been told about - allows GUI client to request
	//information only about changed items.
	private Hashtable message_keys;
	

    private PrimaryKey selected_message_id = null;
    
    
    public boolean canPostDraft( PrimaryKey key )
		throws RemoteException, BuildingServerException
    	{
		prepareMethodCall();
		try
			{
			User user;
			BuildingContext context;
			MessageSummary summary;
	    	MessagingRoom room = getMessagingRoom();

	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );

			summary = room.getMessageSummary( key );
			if ( summary==null )
				throw new BuildingServerException( "Can't find the message details." );

			// can post a draft if it isn't a draft
			if ( !summary.isDraft() )
				return false;

			// can't post anything without permission to post
			if ( !room.checkPermission( Permission.POST ) )
				return false;

			context = BuildingContext.getContext();
			user = (User)context.getUser();
			// author can post draft if has post level access.
			if ( summary.getUserId().equals( user.getUserId() ) )
				return true;

			// another user can post draft if they have post and manage level access.
			return room.checkPermission( Permission.MANAGE );
			}
		finally
			{
			disposeMethodCall();
			}
    	}

    public boolean canEditMessage( PrimaryKey key )
		throws RemoteException, BuildingServerException
    	{
		prepareMethodCall();
		try
			{
			User user;
			BuildingContext context;
			MessageSummary summary;
	    	MessagingRoom room = getMessagingRoom();

	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );

			summary = room.getMessageSummary( key );
			if ( summary==null )
				throw new BuildingServerException( "Can't find the message details." );


                              /*
                               * WebLearn inserted code; A Corfield 02/12/2003.
                               */
                              Resource res = getResource();
                              if (res == null)
                                throw new BuildingServerException(
                                    "Can't find the resource for this message.");
                              // can edit if user has MANAGE, ADMINISTER or SYSADMIN permission
                              if (res.checkPermission(Permission.MANAGE))
                                  return true;



			// even author can't edit if marked as seen
			if ( summary.beenSeen() )
				return false;

			context = BuildingContext.getContext();
			user = (User)context.getUser();
			// author can edit if not seen
			return summary.getUserId().equals( user.getUserId() );
			}
		finally
			{
			disposeMethodCall();
			}
    	}

	
	public MessagingRoomSessionImpl()
		throws RemoteException
		{
		super();
		}

	public boolean containsMessage( PrimaryKey mid )
		throws BuildingServerException
		{
		prepareMethodCall();
		try
			{
			MessagingRoom room = getMessagingRoom();
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );
						
			return room.containsMessage( mid );
			}
		finally
			{
			disposeMethodCall();
			}

		}
		
	public PrimaryKey postMessage( String subject, String text, int style )
		throws RemoteException, BuildingServerException
		{
		return postMessage( subject, text, style, null, true );
		}
		
	public PrimaryKey postMessage( String subject, String text, int style, PrimaryKey parent_mid, boolean branch )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			MessagingRoom room;
			Message message;
			User user;
			BigString text_obj;
			
			
	    	room = getMessagingRoom();
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.POST ) )
				throw new BuildingServerException( "Post access denied",
						"You don't have 'post' level access to this room." );
						
			user = (User)BuildingContext.getContext().getUser();
			
			if ( parent_mid != null &&!room.containsMessage( parent_mid ) )
				{
				throw new BuildingServerException( "Couldn't find the message this is a reply to." );
				}
			
			
			if ( subject == null )
				subject = "(No subject)";
			if ( text == null )
				text = "";
				
			text = text.trim();
			if ( text.length() == 0 )
				throw new BuildingServerException( "No message given.",
					"No message was specified.  It isn't possble to post an empty message." );
					
			text_obj = new BigString();
			text_obj.setString( text );
			text_obj.save();

			log.debug( "Message text: " );
			log.debug( text_obj.getBigStringId().toString() );
			
			message = room.createMessage( parent_mid, branch );
			message.setUser( user );
			message.setAuthorName( user.getName() );  //so the name on the message doesn't change
			message.setStyle( style );
			message.setSubject( subject );
			message.setBigStringId( text_obj.getBigStringId() );
			log.debug( "Message: " );
			log.debug( message.getBigStringId().toString() );
			room.postMessage( message );

			//newly posted messages are always visible to poster but closed
			if ( visible_message_ids!=null )
				visible_message_ids.put( message.getMessageId(), Visibility.VISIBLE_CLOSED );
				
			
                        // sometimes the caller doesn't want the branch to be
                        // opened so don't do it here - the caller must do it
			//open the parent message so siblings are visible....
			//if ( message.getParentMessageId() != null )
			//	{
			//	openMessageBranch( message.getParentMessageId() );
			//	}

				
			try
				{
				User author=null;
				MessageSummary replyto_summary =null;
				if ( parent_mid!=null )
					{
					replyto_summary = getMessageSummary( parent_mid );
					author = User.findUser( replyto_summary.getUserId() );
					}
				
        		MessagingEvent event = new MessagingEvent( 
        						(parent_mid==null) ? MessagingEvent.EVENT_POST : MessagingEvent.EVENT_REPLY,
        						room.getResourceId(), 
        						user.getUserId(), 
        						author==null?null:author.getUserId(), 
        						new Integer( message.getMessageId().intValue() ), 
        						parent_mid==null ?
        							null : new Integer( parent_mid.intValue() ) 
        						);
        		event.save();
				}
			catch ( Exception ex )
				{
				log.error( ex.getMessage(), ex );
				}

				
                        // sometimes the caller doesn't want the message
                        // selected
			//selected_message_id = message.getMessageId();
			//return selected_message_id;
                        return message.getMessageId();
			}
		finally
			{
			disposeMethodCall();
			}

		}


	public PrimaryKey saveMessage( String subject, String text, int style, PrimaryKey mid, boolean branch )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			MessagingRoom room;
			Message message;
			User user;
			BigString text_obj;
			PrimaryKey parent_mid, replyto_mid;
			boolean edit_message, edit_big_string, is_branched, edit_tree;
			
	    	room = getMessagingRoom();
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.POST ) )
				throw new BuildingServerException( "Post access denied",
						"You don't have 'post' level access to this room." );
						
			user = (User)BuildingContext.getContext().getUser();
			
			if ( mid == null || !room.containsMessage( mid ) )
				throw new BuildingServerException( "Couldn't find the message to edit." );
			message = Message.findMessage( mid );
			if ( message == null )
				throw new BuildingServerException( "Couldn't load the message to edit it." );
				
			replyto_mid = message.getReplyToMessageId();
			parent_mid = message.getParentMessageId();
			
			if ( parent_mid != null && !room.containsMessage( parent_mid ) )
				{
				throw new BuildingServerException( "Couldn't find the message this is a reply to." );
				}
			
			
			if ( subject == null )
				subject = "(No subject)";
			if ( text == null )
				text = "";
				
			text = text.trim();
			if ( text.length() == 0 )
				throw new BuildingServerException( "No message given.",
					"No message was specified.  It isn't possble to post an empty message." );
					

			text_obj = BigString.findBigString( message.getBigStringId() );
			
			//work out what needs to be edited to save on database accesses if possible
			edit_message =	style != message.getStyle() 			|| 
							!subject.equals( message.getSubject() );
							
			edit_big_string = !text.equals( text_obj.getString() );
			
			is_branched = parent_mid==null || replyto_mid==null || parent_mid.equals( replyto_mid );
			edit_tree = branch != is_branched;
					
			if ( edit_big_string )
				{
				text_obj.setString( text );
				text_obj.save();						  //save text of message
				}

                        /*
                         * WebLearn inserted code; A Corfield 02/12/2003.
                         */
			if ( edit_big_string || edit_message )
				{
				message.setStyle( style );				  // new style setting
				message.setSubject( subject );			  // new subject
				message.save();
				}


			if ( !edit_tree )
				return mid;
				
			log.debug( "Message moving not implemented yet." );
			// now we have to move the message in the tree.....
				
			// not yet implemented!!!
			
			selected_message_id = mid;
			return mid;
			}
		finally
			{
			disposeMethodCall();
			}

		}


    public MessageTreeNode[] getMessageTreeNodes( int sort_type )
        throws RemoteException, BuildingServerException
    {
        prepareMethodCall();
        try
        {
            MessageTreeNode root_node = getMessageTree();
            MessagingRoom room = getMessagingRoom();
            Enumeration enumeration = root_node.preorderEnumeration();
            Vector list = new Vector();
            while (  enumeration.hasMoreElements() )
                list.addElement( enumeration.nextElement() );
            MessageTreeNode[] oarray = new MessageTreeNode[list.size()];
            oarray = (MessageTreeNode[])list.toArray( oarray );
            MessageTreeNodeComparator comparator = new MessageTreeNodeComparator( room, sort_type );
            java.util.Arrays.sort( oarray, comparator );

            return oarray;
        }
        finally
        {
            disposeMethodCall();
        }
    }

	public MessageTreeNode getMessageTree()
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
	    	MessagingRoom room = getMessagingRoom();
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );


			return room.getMessageRoot();
			}
		finally
			{
			disposeMethodCall();
			}
		}

	public String getMessageText( PrimaryKey key, String mime_type )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			User user = (User)BuildingContext.getContext().getUser();

			MessageSummary summary;
	    	MessagingRoom room = getMessagingRoom();

	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );

                if ( key == null )
                    return room.getIntroduction();
                
			summary = room.getMessageSummary( key );
			if ( summary==null )
				throw new BuildingServerException( "Can't find the message text." );

			if ( !summary.beenSeen() )  //if message has not been seen should it be marked so now?
				{
				if ( !summary.isDraft() && !summary.getUserId().equals( user.getUserId() ) )
					{
					// the message is not a draft and the current reader is
					// not the author so this counts as seeing the final version of the message.
					Message message = Message.findMessage( summary.getMessageId() );
					message.setSeen( true );
					message.save();
					}
				}
			
			//record event....
			
			try
				{
				User author;
				author = User.findUser( summary.getUserId() );
				
        		MessagingEvent event = new MessagingEvent( 
        						MessagingEvent.EVENT_READ,
        						room.getResourceId(), 
        						user.getUserId(), 
        						author.getUserId(), 
        						new Integer( summary.getMessageId().intValue() ), 
        						summary.getReplyToMessageId()==null?null:
        						new Integer( summary.getReplyToMessageId().intValue() ) );
        		event.save();
				}
			catch ( Exception ex )
				{
				log.error( ex.getMessage(), ex );
				}
			
			
			return Message.getMessageText( summary, mime_type );
			}
		finally
			{
			disposeMethodCall();
			}
		}
		
	public String getMessageTextSample( PrimaryKey key, int max_length )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			User user = (User)BuildingContext.getContext().getUser();

			MessageSummary summary;
	    	MessagingRoom room = getMessagingRoom();

	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );

			summary = room.getMessageSummary( key );
			if ( summary==null )
				throw new BuildingServerException( "Can't find the message text." );

			// if only a small sample is being fetched we don't mark the
                        // message as read.
                        
                        
			return Message.getMessageTextSample( summary, max_length );
			}
		finally
			{
			disposeMethodCall();
			}
		}

    public Vector getReaders( PrimaryKey key, boolean with_dates )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			User user;
			Vector list;
			MessageSummary summary;
	    	MessagingRoom room = getMessagingRoom();

	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );

			summary = room.getMessageSummary( key );
			if ( summary==null )
				throw new BuildingServerException( "Can't find the message." );

			list = new Vector();
			
			StringBuffer where = new StringBuffer();
			where.append( "user_id IN (SELECT active_user_id FROM events, messaging_events " );
			where.append( "WHERE event_id = messaging_event_id AND resource_id = " );
			where.append( room.getResourceId().toString() );
			where.append( " AND event_code = " );
			where.append( MessagingEvent.EVENT_READ );
			where.append( " AND message_id = " );
			where.append( key.toString() );
			where.append( " )" );
			Enumeration enumeration = User.findUsers( where.toString(), "surname" );

			if ( !with_dates )
				{
				//just list the users.
				while ( enumeration.hasMoreElements() )
					{
					user = (User)enumeration.nextElement();
					list.addElement( user.getName() );
					}
				}
			else
				{
				StringBuffer line = new StringBuffer( 50 );
				//get the events
				where.setLength( 0 );
				where.append( "resource_id = " );
				where.append( room.getResourceId().toString() );
				where.append( " AND event_code = " );
				where.append( MessagingEvent.EVENT_READ );
				where.append( " AND message_id = " );
				where.append( key );
				enumeration = MessagingEvent.findMessagingEvents( where.toString(), "event_time" );
				MessagingEvent event;
				while ( enumeration.hasMoreElements() )
					{
					event = (MessagingEvent)enumeration.nextElement();
					line.setLength( 0 );
					line.append( event.getEventTime().toString() );
					line.append( " " );
					user = event.getActiveUser();
					line.append( user.getName() );
					list.addElement( line.toString() );
					}
				}
			
			return list;
			}
		finally
			{
			disposeMethodCall();
			}
		}

    public Vector getNonReaders( PrimaryKey key )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			Principal principal;
			User user;
			Hashtable reader_list;
			Vector non_reader_list;
			MessageSummary summary;
	    	MessagingRoom room = getMessagingRoom();

	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );

			summary = room.getMessageSummary( key );
			if ( summary==null )
				throw new BuildingServerException( "Can't find the message." );

			//put user_ids into the reader list
			reader_list = new Hashtable();
			
			StringBuffer where = new StringBuffer();
			where.append( "user_id IN (SELECT active_user_id FROM events, messaging_events " );
			where.append( "WHERE event_id = messaging_event_id AND resource_id = " );
			where.append( room.getResourceId().toString() );
			where.append( " AND event_code = " );
			where.append( MessagingEvent.EVENT_READ );
			where.append( " AND message_id = " );
			where.append( key.toString() );
			where.append( " )" );
			Enumeration enumeration = User.findUsers( where.toString() );

			while ( enumeration.hasMoreElements() )
				{
				user = (User)enumeration.nextElement();
				reader_list.put( user.getUserId(), user.getUserId() );
				}
			
			// now work through people who can view and build
			// list of those who are not in the reader list
			non_reader_list=new Vector();
			Acl acl = room.getAcl();
			enumeration = room.everyoneWhoCan( Permission.VIEW, false );
			while ( enumeration.hasMoreElements() )
				{
				principal = (Principal)enumeration.nextElement();
				if ( principal instanceof User )
					{
					user = (User)principal;
					if ( !reader_list.containsKey( user.getUserId() ) )
						non_reader_list.addElement( user.getName() );
					}
				}
			
			
			return non_reader_list;
			}
		finally
			{
			disposeMethodCall();
			}
		}

    public Vector getParticipants( int style )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
	    	MessagingRoom room = getMessagingRoom();

	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );


			switch ( style )
				{
				case MessagingParticipantStyle.PARTICIPANTS_CAN_VIEW:
					return getCanParticipate( room, false );
				case MessagingParticipantStyle.PARTICIPANTS_CAN_POST:
					return getCanParticipate( room, true );
				case MessagingParticipantStyle.PARTICIPANTS_READERS:
					return getReadOrPostParticipants( room, false );
				case MessagingParticipantStyle.PARTICIPANTS_POSTERS:
					return getReadOrPostParticipants( room, true );
				case MessagingParticipantStyle.PARTICIPANTS_NON_READERS:
					return getReadOrPostNonParticipants( room, false );
				case MessagingParticipantStyle.PARTICIPANTS_NON_POSTERS:
					return getReadOrPostNonParticipants( room, true );
				}
			throw new BuildingServerException( "Unknown style of participant list requested." );
			}
		finally
			{
			disposeMethodCall();
			}
		}

    private Vector getCanParticipate( MessagingRoom room, boolean post )
		throws RemoteException, BuildingServerException
		{
		Vector list = new Vector();
		String group_name, description;
		User user;
		Group group;
		Principal principal;
		Resource other_resource;
		
		Acl acl = room.getAcl();
		Enumeration enumeration;
		if ( post )
			enumeration = room.everyoneWhoCan( Permission.POST, false );
		else
			enumeration = room.everyoneWhoCan( Permission.VIEW, false );
		
		while ( enumeration.hasMoreElements() )
			{
			principal = (Principal)enumeration.nextElement();
			if ( principal instanceof Group )
				{
				group = (Group)principal;
				group_name = group.getName();
				description = null;
				
				if ( group.isOwnersGroup() )
					{
					if ( group.getResourceId().equals( room.getResourceId() ) )
						description = "Owners of this discussion room";
					else
						{
						other_resource = group.getResource();
						description = "Owners of \"" + other_resource.getTitle() + "\".";
						}
					}
				else if ( group.isAdhocGroup() )
					{
					if ( group.getResourceId().equals( room.getResourceId() ) )
						description = "An adhoc group of users of this discussion room";
					else
						{
						other_resource = group.getResource();
						description = "An adhoc group of users of \"" + other_resource.getTitle() + "\".";
						}
					}
				
				if ( group.isLocalGroup() )
					{
						group_name = group.getLocalName();
					}
				//three entries for each group
				list.addElement( "$group" );
				list.addElement( group_name );
				
				if ( description == null )
					{
					if ( group.getSpecialGroup() != null )
						description = group.getDescription() + " It isn't possible to list the members of this group.";
					else
						description = group.getDescription() + " Members of this group will be listed individually.";
					}
				list.addElement( description );
				}
			if ( principal instanceof User )
				{
				user = (User)principal;
				//one entry for each user
				list.addElement( user.getName() );
				}
			}
		
		return list;
		}
	
    private Vector getReadOrPostParticipants( MessagingRoom room, boolean posters )
		throws RemoteException, BuildingServerException
		{
		Vector list;
		User user;

		list = new Vector();
			
		StringBuffer where = new StringBuffer();
		where.append( "user_id IN (SELECT active_user_id FROM events, messaging_events "	);
		where.append( "WHERE event_id = messaging_event_id AND resource_id = "  			);
		where.append( room.getResourceId().toString()										);
		if ( posters )
			{
			where.append( " AND ( event_code = "											);
			where.append( MessagingEvent.EVENT_POST 										);
			where.append( " OR event_code = "												);
			where.append( MessagingEvent.EVENT_REPLY										);
			where.append( " )"																);
			}
		else
			{
			where.append( " AND event_code = "												);
			where.append( MessagingEvent.EVENT_READ 										);
			}
		where.append( " )"																	);
		Enumeration enumeration = User.findUsers( where.toString(), "surname"						);

		while ( enumeration.hasMoreElements() )
			{
			user = (User)enumeration.nextElement();
			list.addElement( user.getName() );
			}
			
		return list;
		}
	
    private Vector getReadOrPostNonParticipants( MessagingRoom room, boolean posters )
		throws RemoteException, BuildingServerException
		{
		Hashtable participant_list;
		Vector non_participants;
		Principal principal;
		User user;

		participant_list = new Hashtable();
			
		StringBuffer where = new StringBuffer();
		where.append( "user_id IN (SELECT active_user_id FROM events, messaging_events "	);
		where.append( "WHERE event_id = messaging_event_id AND resource_id = "  			);
		where.append( room.getResourceId().toString()										);
		if ( posters )
			{
			where.append( " AND ( event_code = "											);
			where.append( MessagingEvent.EVENT_POST 										);
			where.append( " OR event_code = "												);
			where.append( MessagingEvent.EVENT_REPLY										);
			where.append( " )"																);
			}
		else
			{
			where.append( " AND event_code = "												);
			where.append( MessagingEvent.EVENT_READ 										);
			}
		where.append( " )"																	);
		Enumeration enumeration = User.findUsers( where.toString(), "surname"						);

		while ( enumeration.hasMoreElements() )
			{
			user = (User)enumeration.nextElement();
			participant_list.put( user.getUserId(), user.getUserId() );
			}
			
			
		// now work through people who can view and build
		// list of those who are not in the reader list
		non_participants=new Vector();
		Acl acl = room.getAcl();
		log.debug( "Looking for non " + (posters?"posters":"readers") );
		enumeration = room.everyoneWhoCan( posters?Permission.POST:Permission.VIEW, false );
		while ( enumeration.hasMoreElements() )
			{
			principal = (Principal)enumeration.nextElement();
			if ( principal instanceof User )
				{
				user = (User)principal;
				if ( !participant_list.containsKey( user.getUserId() ) )
					non_participants.addElement( user.getName() );
				}
			}
			
			
		return non_participants;
		}

	
	public MessageSummary getMessageSummary( PrimaryKey key )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
	    	MessagingRoom room = getMessagingRoom();
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );


			return room.getMessageSummary( key );
			}
		finally
			{
			disposeMethodCall();
			}
		}

	public Hashtable getMessageSummaries()
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			Enumeration enumeration;
			PrimaryKey key;
	    	MessagingRoom room = getMessagingRoom();
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );


			message_keys = new Hashtable();

			Hashtable r = room.getMessageSummaries();
			if ( r==null ) return null;

			enumeration = r.keys();
			while ( enumeration.hasMoreElements() )
				{
				key = (PrimaryKey)enumeration.nextElement();
				message_keys.put( key, key );
				}


			return (Hashtable)r.clone();
			}
		finally
			{
			disposeMethodCall();
			}
		}
		
	public Hashtable getMoreMessageSummaries()
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			PrimaryKey key;
			Enumeration enumeration;
			Message message;
			MessageSummary summary;
	    	MessagingRoom room = getMessagingRoom();
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );

			if ( message_keys==null )
				return getMessageSummaries();

			Hashtable r = room.getDifferentMessageSummaries( message_keys );
			if ( r==null ) return null;

			enumeration = r.keys();
			while ( enumeration.hasMoreElements() )
				{
				key = (PrimaryKey)enumeration.nextElement();
				message_keys.put( key, key );
				}


			return (Hashtable)r.clone();
			}
		finally
			{
			disposeMethodCall();
			}
		}


	public PrimaryKey getSelectedMessage()
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			return selected_message_id;
			}
		finally
			{
			disposeMethodCall();
			}
		}


	public void selectMessage( PrimaryKey mid )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			MessagingRoom room;
			
	    	room = getMessagingRoom();
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
						
			if ( mid != null && !room.containsMessage( mid ) )
				throw new BuildingServerException( "Couldn't find the message to edit." );
				
			selected_message_id = mid;
			}
		finally
			{
			disposeMethodCall();
			}
		}


    public void selectMessageAndSimplifyTree( PrimaryKey mid )
    throws RemoteException, BuildingServerException
    {
        prepareMethodCall();
        try
        {
            MessagingRoom room;
            MessageTreeNode node, current_node, child;
            Hashtable vis;
            Enumeration children;
            boolean has_children, is_thread;

            room = getMessagingRoom();

            if ( room == null )
                throw new BuildingServerException( "Can't find correct resource." );

            node = room.getMessageNode( mid );
            if ( node == null )
                throw new BuildingServerException( "Couldn't find the message to select." );
            
            has_children = !node.isLeaf();
            is_thread = has_children || node.getLevel()<=1;
            
            // start by collapsing whole tree
            vis = getVisibleMessages();
            vis.clear();
            
            // make selected node visible and open if it has children
            if ( node.getPrimaryKey()!=null )
                vis.put( node.getPrimaryKey(), has_children?Visibility.VISIBLE_OPEN:Visibility.VISIBLE_CLOSED );
            
            // make ancestors visible
            current_node = node;
            while ( (current_node = (MessageTreeNode)current_node.getParent()) != null )
                if ( current_node.getPrimaryKey()!=null )
                    vis.put( current_node.getPrimaryKey(), Visibility.VISIBLE_OPEN );
            
            // if is thread, show children otherwise show siblings
            //if ( is_thread )
                current_node = node;
            //else
            //    current_node = (MessageTreeNode)node.getParent();
            
            if ( current_node != null )
            {
                children = current_node.children();
                while ( children.hasMoreElements() )
                {
                    child = (MessageTreeNode)children.nextElement();
                    vis.put( child.getPrimaryKey(), Visibility.VISIBLE_CLOSED );
                }
            }
            
            selected_message_id = mid;
            
            
        }
        finally
        {
            disposeMethodCall();
        }
    }
    
    public void selectMessageAndMakeVisible( PrimaryKey mid )
    throws RemoteException, BuildingServerException
    {
        prepareMethodCall();
        try
        {
            MessagingRoom room;
            MessageTreeNode node, current_node, child;
            Hashtable vis;
            Enumeration children;
            boolean has_children, is_thread;

            room = getMessagingRoom();

            if ( room == null )
                throw new BuildingServerException( "Can't find correct resource." );

            node = room.getMessageNode( mid );
            if ( node == null )
                throw new BuildingServerException( "Couldn't find the message to select." );
            
            // get ancestors of node
            // open every ancestor but don't open selected node.
            TreeNode[] path = node.getPath();
            
            int i;
            for ( i=0; i<(path.length-1); i++ )
            {
                current_node = (MessageTreeNode)path[i];
                openMessageBranch( current_node.getPrimaryKey() );
            }
            
            selected_message_id = mid;
            
            
        }
        finally
        {
            disposeMethodCall();
        }
    }

	public Hashtable getVisibleMessages()
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			Enumeration children;
			MessageTreeNode root, child;
	    	MessagingRoom room = getMessagingRoom();
	    	PrimaryKey mid;
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );

			if ( visible_message_ids == null )
				{
				// this user, in this session hasn't asked before
				// so make initial list
				visible_message_ids = new Hashtable();
				
				root = room.getMessageRoot();
				
				children = root.children();
				
				while ( children.hasMoreElements() )
					{
					child = (MessageTreeNode)children.nextElement();
					mid = child.getPrimaryKey();
					if ( mid != null )
						{
						//if ( child.getChildCount()==0 )
						//	visible_message_ids.put( mid, Boolean.TRUE );
						//else
						visible_message_ids.put( mid, Visibility.VISIBLE_CLOSED );
						}
					}
				}
				
			return visible_message_ids;
			}
		finally
			{
			disposeMethodCall();
			}
		}

	public void openMessageBranch( PrimaryKey mid )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			Enumeration children=null;
			MessageTreeNode root, node, child;
	    	MessagingRoom room = getMessagingRoom();
	    	PrimaryKey nodemid;
	    	Enumeration enumeration;
	    	Hashtable vis;
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );


			vis = getVisibleMessages();

			root = room.getMessageRoot();
			enumeration = root.preorderEnumeration();
			while ( enumeration.hasMoreElements() )
				{
				node = (MessageTreeNode)enumeration.nextElement();
				nodemid = node.getPrimaryKey();
				if ( nodemid==null ) continue;
				if ( !nodemid.equals( mid ) ) continue;
				
				//indicate that selected node is now open
				vis.put( nodemid, Visibility.VISIBLE_OPEN );
				
				children = node.children();
				while ( children.hasMoreElements() )
					{
					child = (MessageTreeNode)children.nextElement();
					vis.put( child.getPrimaryKey(), Visibility.VISIBLE_CLOSED );
					}
				break;
				}
				
			selected_message_id = mid;
			return;
			}
		finally
			{
			disposeMethodCall();
			}
		
		}

	
	public void closeMessageBranch( PrimaryKey mid )
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			Enumeration children=null;
			MessageTreeNode root, node, child;
	    	MessagingRoom room = getMessagingRoom();
	    	PrimaryKey nodemid;
	    	Enumeration enumeration;
	    	Hashtable vis;
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );


			vis = getVisibleMessages();

			root = room.getMessageRoot();
			enumeration = root.preorderEnumeration();
			while ( enumeration.hasMoreElements() )
				{
				node = (MessageTreeNode)enumeration.nextElement();
				nodemid = node.getPrimaryKey();
				if ( nodemid==null ) continue;
				if ( !nodemid.equals( mid ) ) continue;
				
				//indicate that selected node is now closed
				vis.put( nodemid, Visibility.VISIBLE_CLOSED );
				
				//now get all children from this branch and make sure they
				//are made invisible.
				children = node.preorderEnumeration();
				//skip the node itself - leave it visible
				children.nextElement();
				while ( children.hasMoreElements() )
					{
					child = (MessageTreeNode)children.nextElement();
					vis.put( child.getPrimaryKey(), Visibility.INVISIBLE_CLOSED );
					}
				break;
				}
				
			selected_message_id = mid;
			return;
			}
		finally
			{
			disposeMethodCall();
			}
		
		}
	
	
	public void openAllMessageBranches()
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			MessageTreeNode root, node;
	    	MessagingRoom room = getMessagingRoom();
	    	PrimaryKey nodemid;
	    	Enumeration enumeration;
	    	Hashtable vis;
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );


			vis = getVisibleMessages();

			root = room.getMessageRoot();
			enumeration = root.preorderEnumeration();
			while ( enumeration.hasMoreElements() )
				{
				node = (MessageTreeNode)enumeration.nextElement();
				nodemid = node.getPrimaryKey();
				if ( nodemid==null )
					continue;
				
				//indicate that node is now open
				if ( node.getChildCount()>0 )
					vis.put( nodemid, Visibility.VISIBLE_OPEN );
				else
					// but empty nodes are closed
					vis.put( nodemid, Visibility.VISIBLE_CLOSED );
				}
				
			selected_message_id = null;
			return;
			}
		finally
			{
			disposeMethodCall();
			}
		
		}

	
	public void closeAllMessageBranches()
		throws RemoteException, BuildingServerException
		{
		prepareMethodCall();
		try
			{
			MessageTreeNode root, node;
	    	MessagingRoom room = getMessagingRoom();
	    	PrimaryKey nodemid;
	    	Enumeration enumeration;
	    	Hashtable vis;
	    	
	    	if ( room == null )
	    		throw new BuildingServerException( "Can't find correct resource." );
	    	if ( !room.checkPermission( Permission.VIEW ) )
				throw new BuildingServerException( "View access denied",
						"You don't have 'view' level access to this room." );


			vis = getVisibleMessages();

			root = room.getMessageRoot();
			enumeration = root.preorderEnumeration();
			while ( enumeration.hasMoreElements() )
				{
				node = (MessageTreeNode)enumeration.nextElement();
				nodemid = node.getPrimaryKey();
				if ( nodemid==null )
					continue;

				//top level messages left visible (but closed)
				if ( node.getLevel()<=1 )
					vis.put( nodemid, Visibility.VISIBLE_CLOSED );
				else
					// but all others hidden
					vis.put( nodemid, Visibility.INVISIBLE_CLOSED );
				}
				
			selected_message_id = null;
			return;
			}
		finally
			{
			disposeMethodCall();
			}
		
		}

	
	
	
	
	private synchronized MessagingRoom getMessagingRoom()
		throws BuildingServerException
		{
		Resource r = getResource();
		if ( r == null || !(r instanceof MessagingRoom) )
			throw new BuildingServerException( 
				"Wrong type of session for this type of resource.",
				"There was a technical problem attempting to access this tool." );
				
		return (MessagingRoom)r;
		}
		
	}
	