/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

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

package org.bodington.logbook.server;

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

import org.bodington.database.PrimaryKey;
import org.bodington.server.BuildingServerException;
import org.bodington.server.resources.Resource;


/**
 * This is the Java class used to instantiate objects from records in the
 * log_books database table.
 * 
 * @author Jon Maber
 */
public class LogBook extends Resource
	{

	/**
	 * A constant that defines which bit in the flags int to use for this flag.
	 */
	public static final int FLAG_USER_CLOSABLE = 1;
	
	/**
	 * Marked as private so can only be accessed via get/set methods.
	 */
	private PrimaryKey	log_book_id;
	private int			log_flags;
	
	
	private transient LogBookSection[] sections=null;
	
	
    /**
     * Used to find out what session class to instantiate if a user wants to
     * interact with this LogBook.
     * 
     * @return The class of the session to instantiate.
     */
    public Class sessionClass()
    	{
        // This method is derived from class org.bodington.server.resources.Resource
        // to do: code goes here
        return org.bodington.logbook.server.LogBookSessionImpl.class;
    	}
    	
    			
    /**
     * This method is required for all database objects in the Bodington System. 
     * In this it simply passes on to the get method for the field that
     * contains the primary key.  This is used by the database code of the
     * Bodington System to identify the object in its cache of database objects.
     * 
     * @return The PrimaryKey object that contains a unique id number for this object.
     */
    public PrimaryKey getPrimaryKey()
		{
        return getLogBookId();
        }

    /**
     * This method is called by the Bodington database code after a new object
     * is saved and therefore inserted as a new record in the database.  Never
     * call this method in user code.
     * 
     * @param key A key (primarykey id number) determined by the database handler of the 
     * Bodington System.
     */
    public void setPrimaryKey(PrimaryKey key)
    	{
    	setLogBookId( key );
    	}
    /**
     * Returns the id of this log book.  Will return null if the log book was
     * instantiated but hasn't yet been saved to the database.
     * 
     * @return The id of the log book or null if it has never been saved.
     */
    	

    public PrimaryKey getLogBookId()
		{
        return log_book_id;
        }

    /**
     * User code should never call this method.  It is called by Bodington system
     * when it loads the log book from the database.  The setUnsaved() method is
     * not called here because this method is only called to instantiate the 
     * database record.
     * 
     * @param key The id of the log book.
     */
    public void setLogBookId(PrimaryKey key)
    	{
    	log_book_id = key;
    	setResourceId( key );
    	}
    	
    	
	/**
	 * Get an integer containing all the flags for this log book.  Mainly
	 * used by the Bodington system when saving this object to the database.
	 * 
	 * @return An integer representing up to 32 flags.
	 */
	public int getLogFlags()
		{
		return log_flags;
		}

	/**
	 * Set all (up to 32) flags at once.  Mainly used by the Bodington
	 * system when loading the object from the database.  The setUnsaved()
	 * method is called if changes are made because this method might be
	 * used to modify an already instantiated object and the "unsaved"
	 * flag used later to conditionally save the object only if it is
	 * different to the database record it was loaded from.
	 * 
	 * @param f An integer representing up to 32 flags.
	 */
	public void setLogFlags( int f )
		{
		if ( f==log_flags ) return;
		log_flags=f;
    	setUnsaved();
		}

	/**
	 * Convenience method for finding out if the user closable flag is set.
	 * 
	 * @return True/false flag.
	 */
	public boolean isUserClosable()
		{
		return (log_flags & FLAG_USER_CLOSABLE) != 0;
		}
	
	/**
	 * Convenience user method for setting a single flag.  If it represents a
	 * change of state the object is marked as having unsaved changes.
	 * 
	 * @param b The value to set the flag to.
	 */
	public void setUserClosable( boolean b )
		{
		if ( isUserClosable() == b )
			return;
		log_flags = log_flags ^ FLAG_USER_CLOSABLE;		
		setUnsaved();
		}

 
 
	
	/**
	 * This class can hold references to the sections that belong to it and
	 * so avoid a lot of database queries but this relies on this invalidate
	 * method being called whenever a section is added, deleted or reordered
	 * in the log book.  Since the only class that will do that is the
	 * LogBookSessionImpl it can easily arrange to call this method every time.
	 * When this object is garbage collected the references to the sections will
	 * be lost and so the sections can be garbage collected too.
	 */
	public void invalidateSections()
		{
		sections = null;
		}




	/**
	 * Find the biggest ordinal number of all the sections in the log book.
	 * 
	 * @return Ordinal of last section.
	 */
	public int getMaxSectionOrdinal()
		throws BuildingServerException
		{
		findLogBookSections();
		if ( sections.length == 0 )
			return 0;
		return sections[sections.length-1].getOrdinal();
		}

   	/**
   	 * If sections haven't changed and they were already loaded from the database
   	 * this just returns an array that was prepared earlier.  Otherwise it calls
   	 * a static finder method in LogBookSection with the log_book_id as the only
   	 * search criterion.
   	 * 
   	 * @return A array of sections in the correct order.
   	 * @exception org.bodington.server.BuildingServerException
   	 */
		
   	public LogBookSection[] findLogBookSections()
		throws BuildingServerException
		{
		if ( sections != null )
			return sections;
			
		Enumeration enumeration = LogBookSection.findLogBookSections( "log_book_id = " + log_book_id, "ordinal" );
		Vector list = new Vector();
		while ( enumeration.hasMoreElements() )
			list.addElement( enumeration.nextElement() );
			
		sections =  (LogBookSection[])list.toArray( new LogBookSection[0] );
		return sections;
		}
   	/**
   	 * Loads all the sections and asks each to find all its questions.  These
   	 * are all compiled in a single Hashtable.
   	 * 
   	 * @return A hashtable of all questions under all sections of this log book.
   	 * @exception org.bodington.server.BuildingServerException
   	 */

   	public Hashtable findLogBookQuestions()
		throws BuildingServerException
		{
		int i, j;
		LogBookQuestion[] questions;
		Hashtable q_table = new Hashtable();
		findLogBookSections();
		
		for ( i=0; i<sections.length; i++ )
			{
			questions = sections[i].findLogBookQuestions();
			for ( j=0; j<questions.length; j++ )
				q_table.put( questions[j].getPrimaryKey(), questions[j] );
			}
			
		return q_table;
		}

   	/**
   	 * Asks the specified section to find all its questions.
   	 * 
   	 * @param section_id The unique id of the section whose questions are of interest.
   	 * @return An array of the questions in the specified section in proper order.
   	 * If no section exists in this log book with the id specified an empty list 
   	 * will be returned.
   	 * @exception org.bodington.server.BuildingServerException
   	 */
   	public LogBookQuestion[] findLogBookQuestions( PrimaryKey section_id )
		throws BuildingServerException
		{
		findLogBookSections();

		for ( int i=0; i<sections.length; i++ )
			{
			if ( sections[i].getPrimaryKey().equals( section_id ) )
				return sections[i].findLogBookQuestions();
			}
			
		return new LogBookQuestion[0];
		}

   	/**
   	 * This static method is used to find and load LogBooks from the database.  
   	 * Code that needs to find LogBooks could call PersistentObject.findPersistentObject
   	 * directly but this method is a neater solution and provides an opportunity for
   	 * specialist initialisation for objects found.
   	 * 
   	 * @param key The primary key (id) of the LogBook to find and load.
   	 * @return Reference to the loaded LogBook or null if not found.
   	 * @exception org.bodington.server.BuildingServerException
   	 */
   	public static LogBook findLogBook( PrimaryKey key )
		throws BuildingServerException
		{
		return (LogBook)findPersistentObject( key, "org.bodington.logbook.server.LogBook" );
		}


    public int getResourceType()
    	{
    	return RESOURCE_LOGBOOK;
    	}

	}
	
