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

import org.apache.log4j.Logger;

import java.rmi.RemoteException;


import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.Timestamp;


import org.bodington.servlet.*;
import org.bodington.database.PrimaryKey;
import org.bodington.server.*;
import org.bodington.server.resources.Resource;
import org.bodington.server.resources.ResourceUtils;
import org.bodington.server.resources.UploadedFile;
import org.bodington.server.resources.UploadedFileSummary;
import org.bodington.server.events.*;
import org.bodington.server.realm.Acl;
import org.bodington.server.realm.AclEntry;
import org.bodington.server.realm.Permission;
import org.bodington.server.realm.User;
import org.bodington.util.DateFormatter;
import org.bodington.assessment.*;

public class PigeonHoleFacility extends Facility
	{
    
    private static Logger log = Logger.getLogger(PigeonHoleFacility.class);
	

	static final BigDecimal max_weight = new BigDecimal( "100.0" );
	
    public boolean canCopy( Resource resource )
    {
        return true;
    }
    
	public Resource newResource()
		{
		return new PigeonHole();
		}
	
	
	public boolean initResource( Request breq, Resource new_resource )
		throws Exception
		{
		if ( !(new_resource instanceof PigeonHole) )
			throw new Exception( "Technical problem: An incorrect type of resource was created." );
		
		PigeonHole ph;
		ph = (PigeonHole)new_resource;

		String param=breq.getParameter( "maxmark" );
		ph.setMaximumMark( Integer.parseInt( param ) );

		Timestamp timestamps[] = convertToTimestamps(parseDates(breq));
		ph.setDateOpen(timestamps[0]);
		ph.setDateDeadline(timestamps[1]);
		ph.setDatePenalty(timestamps[2]);

		return true;  
		}
		
	public boolean initResource( Resource original_resource, Resource new_resource )
		throws BuildingServerException
         	{
	         if ( !(new_resource instanceof PigeonHole) )
		   throw new BuildingServerException( "Error: The wrong type of resource was created." );

		 PigeonHole original = (PigeonHole)original_resource;
		 PigeonHole new_pigeonhole = (PigeonHole)new_resource;

		 new_pigeonhole.setDateOpen(original.getDateOpen());
		 new_pigeonhole.setDateDeadline(original.getDateDeadline());
		 new_pigeonhole.setDatePenalty(original.getDatePenalty());
		 new_pigeonhole.setMaximumMark(original.getMaximumMark());

		 return true;
		}
	
	/**
	 * Setup the PigeonHole resource to a sensible state. We add Record, Mark 
	 * and Review Permissions to the owner for this.
	 */
	public boolean create(Request request, Connection connection,
        Resource resource)
    {
	    Permission grant[] = {Permission.RECORD, Permission.MARK, Permission.REVIEW};
        return ResourceUtils.grantPermissions(resource, grant);
    }


    /**
     * Copy the content of one resource to another. <p>
     * <h4>Content copied</h4>
     * <ul>
     * <li>authored questions.
     * </ul>
     * <h4>Content <em>not</em> copied</h4>
     * <ul>
     * <li>user entries.
     * <li>user responses.
     * </ul>
     * </p>
     * @param original_resource the resource whose contents are to be copied.
     * @param new_resource the resource to copy content to.
     * @param breq the building request object.
     * @throws BuildingServerException if there is any problem copying the
     *         resource content.
     */
        public void copyContent( Resource original_resource, Resource new_resource, Request breq )
                throws BuildingServerException
	        {
//	  authored : questions
//	  userdata : entries, responses? /** @todo  */

		 BuildingSession session = BuildingSessionManagerImpl.getSession( original_resource );
		 if ( !(session instanceof PigeonHoleSession) )
		   throw new BuildingServerException( "Unable to access appropriate resource session." );

		 try
		 {
		   if ( breq.getParameter( "authored" ) != null )
		   {
		     copyAuthoredContent( original_resource, new_resource );
//		     if ( breq.getParameter( "userdata" ) != null )
//                       copyUserContent( original_resource, new_resource );
		   }
		 }
		 catch (Exception ex)
		 {
		   throw new BuildingServerException( ex.getMessage() );
		 }
	}


    /**
     * Copy the authored content of an existing Resource to another Resource. <p>
     * <i>(WebLearn modification (method added): 02/12/2003 Colin Tatham)</i>
     * @param original_resource The resource with content to copy.
     * @param new_resource The resource to copy content to.
     * @exception BuildingServerException Thrown if there is any problem copying the resource content.
     * @exception RemoteException Thrown if there is any problem copying the resource content.
     *
     */
	private void copyAuthoredContent( Resource original_resource, Resource new_resource )
		throws BuildingServerException, RemoteException
		{
		 PigeonHoleSession session;
		 Vector questions;
		 PigeonHoleQuestion orig_question, new_question;

		 session = (PigeonHoleSession)BuildingSessionManagerImpl.getSession( original_resource );
		 questions = session.getPigeonHoleQuestionsInOrder();

		 for (int i=0; i<questions.size(); i++ )
		 {
		   orig_question = (PigeonHoleQuestion)questions.elementAt( i );
		   new_question = new PigeonHoleQuestion();
		   new_question.setResourceId( new_resource.getResourceId() );
		   new_question.setAvailable( orig_question.getAvailable() );
		   new_question.setNotes( orig_question.getNotes() );
		   new_question.setWeight( orig_question.getWeight() );
		   new_question.setOrdinal( orig_question.getOrdinal() );

		   new_question.save();
		 }
	}


	public boolean createCheck( Request breq, PrintWriter out )
		{
			int n;
			String param;
			java.util.Date[] dates = parseDates(breq);

			String dateDescriptions[] = {
					"Opening Date",
					"Deadline",
					"Penalty Date"
			};
			for (int i = 0; i < dates.length; i++)
			{
				if ( dates[i]==null )
					out.println( "Could not understand the "+
						dateDescriptions[i]+ " field, setting to empty.<br/>" );
			}

			if (!validateDate(out,dates[0],dates[1],dates[2]))
				return false;

			param=breq.getParameter( "maxmark" );
			try
				{
				n = Integer.parseInt( param );
				}
			catch ( NumberFormatException nfex )
				{
				out.println( "You must enter a valid whole number in the maximum mark field." );
				return false;
				}
			if ( n<1 || n>100 )
				{
				out.println( "The maximum mark must be between 1 and 100." );
				return false;
				}
		return true;  
		}

	/**
	 * Finds the three dates used by pigeon holes in the request and attempts to
	 * parse them. Currenly this is called twice when creating a new pigeon hole
	 * and we should really save the values from the first calling.
	 * @param breq The request in which to find the dates.
	 * @return An array of 3 dates (begin, end, penalty).
	 */
	private java.util.Date[] parseDates(Request breq)
	{
		String strdate;
		String dateFields[] = { "date_open", "date_deadline", "date_penalty" };

		java.util.Date[] date = new java.util.Date[dateFields.length];

		for ( int i=0; i<dateFields.length; i++ )
			{
			strdate=breq.getParameter( dateFields[i] );
			if ( strdate!=null )
				{
				strdate=strdate.trim();
				if ( strdate.length()>0 )
					{
					date[i]=DateParser.parse( strdate );
					}
				}
			}
		return date;
	}

	/**
	 * Check that the supplied dates make sense. This does not check the size of the
	 * ranges or how far they are in the past or future.
	 * @param begin The start of the submission period. Can be null.
	 * @param end The end of the submission period. Can be null.
	 * @param penalty The end of the penalty period. Can be null.
	 * @return <code>false</code> if this set of date is invalid otherwise
	 * <code>true</code>.
	 */
	boolean validateDate(PrintWriter out, Date begin, Date end, Date penalty)
		{
		// If we don't have an end date we can't do any real comparisons.
		if (end == null)
		{
			if (penalty != null)
			{
				out.print("You can't have a penalty date without an end date.");
				return false;
			}
		}
		else
		{
			// Compare begin and end
			if (begin != null)
			{
				if (!begin.before(end))
				{
					out.print("Your beginning date isn't before your end date");
					return false;
				}
			}
			
			// Compare end and penalty
			if (penalty != null)
			{
				if(!end.before(penalty))
				{
					out.println("Your end date should be before your penalty date");
					return false;
				}
			}
		}
		return true;
		}
	

	public void insert( Request req, PrintWriter out, String command, String insertname )
		throws ServletException, IOException
		{
		log.debug( Thread.currentThread().getName() + " PigeonHoleFacility insert()" );

		try
			{
			BuildingSession session;
			PigeonHoleSession ph_session;

			session = BuildingSessionManagerImpl.getSession( req.getResource() );
			if ( !(session instanceof PigeonHoleSession) )
		    	{
		    	out.println( "<HR>Technical problem: unable to access appropriate tool session.<HR>" );
		    	return;
		    	}
			ph_session = (PigeonHoleSession)session;
			

			if ( command.equalsIgnoreCase( "upload" ) )
				{
				upload( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "entrylist" ) )
				{
				entrylist( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "entrystatuslist" ) )
				{
				entrystatuslist( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "entrystatusconfirm" ) )
				{
				entrystatusconfirm( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "markpaper" ) )
				{
				markpaper( req, out, ph_session, insertname );
				return;
				}

			if ( command.equalsIgnoreCase( "markitemedit" ) )
				{
				markitemedit( req, out, ph_session, insertname );
				return;
				}

			if ( command.equalsIgnoreCase( "newq" ) )
				{
				newq( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "markdeleteq" ) )
				{
				markdeleteq( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "markeditqconfirm" ) )
				{
				markeditqconfirm( req, out, ph_session );
				return;
				}
	/*
			if ( command.equalsIgnoreCase( "remarkconfirm" ) )
				{
				remark( req, out, ph_session );
				return;
				}
	*/

			if ( command.equalsIgnoreCase( "markpaperconfirm" ) )
				{
				markpaperconfirm( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "adjustedmarkconfirm" ) )
				{
				adjustedmarkconfirm( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "overwrite" ) )
				{
				overwrite( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "reviewlink" ) )
				{
				reviewlink( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "reviewmark" ) )
				{
				reviewmark( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "noupmessage" ) )
				{
				noupmessage( req, out, ph_session );
				return;
				}
				
			if ( command.equalsIgnoreCase( "ifcanup" ) )
				{
				ifcanup( req, out, ph_session );
				return;
				}
				
			if ( command.equalsIgnoreCase( "ifcanseemark" ) )
				{
				ifcanseemark( req, out, ph_session );
				return;
				}
				
			if ( command.equalsIgnoreCase( "ifupentry" ) )
				{
				ifentry( req, out, ph_session );
				return;
				}
				
	//		if ( command.equalsIgnoreCase( "eventlog" ) )
	//			{
	//			eventlog( req, out, ph_session );
	//			return;
	//			}
				
			if ( command.equalsIgnoreCase( "pigeonmodify" ) )
				{
				pigeonmodify( req, out, ph_session );
				return;
				}
				
			if ( command.equalsIgnoreCase( "uplink" ) )
				{
				uplink( req, out, ph_session, insertname );
				return;
				}
				
			if ( command.equalsIgnoreCase( "pigeonfield" ) )
				{
				pigeonfield( req, out, ph_session, insertname );
				return;
				}
			}		
		catch ( BuildingServerException bsex )
			{
			out.println( bsex.toString() );
			return;
			}

		super.insert( req, out, command, insertname );
		}


	private void ifcanseemark( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		try
			{
			if ( ph_session.canSeeMark() )
				req.setSwitchedOff(  false );
			else
				req.setSwitchedOff(  true );
			}
		catch ( Exception ex )
			{
			req.setSwitchedOff(  false );
			logException( null, "PigeonHoleFacility", "ifcanseemark", 
			    "Technical error looking for an entry in the database.",
			    ex );
			return;
			}
		}	

	
	private void ifcanup( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		try
			{
			if ( ph_session.canUpload() )
				req.setSwitchedOff(  false );
			else
				req.setSwitchedOff(  true );
			}
		catch ( Exception ex )
			{
			logException( null, "PigeonHoleFacility", "ifcanup", 
			    "Technical error looking for an entry in the database.",
			    ex );
			return;
			}
		}	

	
	private void noupmessage( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		try
			{
			if ( !ph_session.canUpload() )
				out.print( ph_session.denyUploadMessage() );
			}
		catch ( Exception ex )
			{
			logException( null, "PigeonHoleFacility", "noupmessage", 
			    "Technical error looking for an entry in the database.",
			    ex );
			return;
			}
		}	



	private void ifentry( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		try
			{
			PigeonHoleEntry entry=ph_session.getPigeonHoleEntry();
			if ( entry!=null )
				{
				PrimaryKey fid = entry.getUploadedFileId();
				if ( fid==null )
					{
					req.setSwitchedOff(  true );
					return;
					}
				UploadedFileSummary finfo = ph_session.getUploadedFileSession().getFileSummary( fid );
				req.setSwitchedOff(  finfo.isDeleted() );
				}
			else
				req.setSwitchedOff(  true );
				
			}
		catch ( Exception ex )
			{
			logException( null, "PigeonHoleFacility", "ifentry", 
			    "Technical error looking for an entry in the database.",
			    ex );
			return;
			}
		}	
	
	private void upload( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		int length, i;
		String name, param, value, avalue=null, bvalue=null, cvalue=null;
		String file, file_name, mime_type;
		PigeonHoleEntry entry;

		int l=req.getContentLength();
		
		if ( l>(2*1024*1024) )
			{
			out.println( "<CENTER><H1>Your file was NOT accepted because it is too big (greater than 2Mb).</H1></CENTER>" );
			return;
			}

		avalue=req.getParameter( "a" );
		bvalue=req.getParameter( "b" );
		cvalue=req.getParameter( "c" );
		if ( cvalue!=null )
			{
			//a form for deleting was included so we have to check
			// values.
			if ( avalue==null || bvalue!=null )
				{
				out.println( "<H1>File Rejected.</H1><P>  There is already a file " +
					"in the pigeon hole and your form selections suggest that " +
					"you may want to keep it.  You can't put two files in the " +
					"pigeon hole.</P>" );
				return;
				}
			}

		file = req.getParameter( "file" );
		file_name = req.getParameterFileName( "file" );

		if ( file_name == null || file_name.length() == 0 )
			{
			out.println( "Upload failed because no file name was supplied in the form." );
			return;
			}

		if ( file_name.indexOf( '/' )>=0 )
			{
			out.println( "File names may not contain any slash characters." );
			return;
			}

        file_name = UploadedFile.nameToURL( file_name );
		
		try
			{
			if ( file!=null && file_name!=null )
				{
				mime_type=req.getServletContext().getMimeType( file_name );
				if ( mime_type==null )
				    mime_type="application/octet-stream";
				ph_session.transferFileForAssessment( file, file_name, mime_type );
				out.println( "<HR>Upload succeeded.<HR>" );
				}
			}
		catch ( Exception ex )
			{
			logException( out, "PigeonHoleFacility", "upload", 
			    "Technical problem: ex.toString()",
			    ex );
			}
		}


	
	private void overwrite( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		PigeonHoleEntry entry;

		if ( out==null )
		    return;
		    
		try
			{
			entry=ph_session.getPigeonHoleEntry();

			//no entry, no point showing an overwrite form
			if ( entry==null )
				return;

			out.println( 
				"<INPUT TYPE=CHECKBOX NAME=a>I've thought about it and I want " +
				"to overwrite the current entry.<BR>" +
				"<INPUT TYPE=CHECKBOX NAME=b CHECKED>Actually, I want to keep the " +
				"the current entry." +
				"<INPUT TYPE=HIDDEN NAME=c VALUE=dummy>"
				);
			}
		catch ( Exception ex )
			{
			logException( null, "PigeonHoleFacility", "overwrite", 
			    "Technical error looking for an entry in the database.",
			    ex );
			return;
			}
		
		}


	private void reviewlink( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		PigeonHoleEntry entry=new PigeonHoleEntry();
		try
			{
			entry=ph_session.getPigeonHoleEntry();
			if ( entry==null )
				{
				//no entry no link
				return;
				}

			if ( entry.getStatus() == PigeonHoleEntry.STATUS_UPLOAD_RETURNED )
				{
				out.println( 
					"<UL><LI><A TARGET=docmain HREF=bs_template_pigeonreview.html>Review feedback and/or mark</A></LI></UL>" );
				return;
				}
			}
		catch ( Exception ex )
			{
			out.println( "<HR>Technical error looking for an entry in the database.<HR>"+ex );
			return;
			}
		}
	
	private void reviewmark( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		PigeonHole ph;
		PigeonHoleEntry entry=new PigeonHoleEntry();
		boolean seen=false;

		try
			{
			ph = ph_session.getPigeonHole();
			entry=ph_session.getPigeonHoleEntry();
			if ( entry==null )
				{
				return;
				}

			if ( entry.getStatus() == PigeonHoleEntry.STATUS_UPLOAD_RETURNED )
				{
				if ( entry.getAdjustedMark() != null )
					{
					seen=true;
					out.print( "<CENTER><H1>Mark = " );
					out.print( entry.getAdjustedMark().toString() );
					out.print( " out of " );
					out.print( ph.getMaximumMark() );
					out.println( "</H1></CENTER>" );
					}
				else
					out.println( "<CENTER><P>There is no mark for your work.</P></CENTER>" );
				if ( entry.getFeedback()!=null && entry.getFeedback().trim().length()>0 )
					{
					seen=true;
					out.println( "<H4>Feedback relating to the file you uploaded:</H4>" );
					out.println( "<TABLE CLASS=bs-table-opaque ALIGN=CENTER><TR><TD><I>" );
					out.println( entry.getFeedback() );
					out.println( "</I></TD></TR></TABLE>" );
					}
				else
					out.println( "<CENTER><P>There is no feedback for your work.</P></CENTER>" );

				if ( seen )
					{
        			AssessmentEvent event = new AssessmentEvent( 
        							AssessmentEvent.EVENT_VIEW_ASSESSMENT,
        							req.getResource().getResourceId(), 
        							entry.getUserId(), 
      								null,
        							new Integer( entry.getPigeonHoleEntryId().intValue() ), 
        							entry.getAdjustedMark()==null?null:new BigDecimal( entry.getAdjustedMark().toString() )    );
        			event.save();
					}
				return;
				}

			out.println( "<P>Feedback and/or a mark are not available.</P>" );
			}
		catch ( Exception ex )
			{
			out.println( "<HR>Technical error looking for an entry in the database.<HR>"+ex );
			return;
			}
		
		}
	
	
	
	
	private void entrylist(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int i, j, status;
		Vector users;
		Hashtable entries;
		User user;
		PigeonHoleEntry entry;
		
		
		users = ph_session.getPigeonHoleUsers();
		entries = ph_session.getPigeonHoleEntries();
		
		out.println( "<TABLE BORDER><TR><TD>Entry</TD><TD>Mark</TD><TD>Mod</TD></TR>" );
		
		for ( i=0; i<users.size(); i++ )
			{
			user = (User)users.elementAt( i );
			entry = (PigeonHoleEntry)entries.get( user.getUserId() );
			
			if ( user==null || entry==null )
				{
				out.println( "<TR><TD COLSPAN=3>Problem fetching entry details.</TD></TR>" );
				continue;
				}
				
			out.print(  "<TR><TD><A TARGET=docmain HREF=" + 
						entry.getUserId() +
						"/bs_template_pigeonmarkpaper.html>" );

			if ( user.getName() == null )
				out.print( "no name" );
			else
				out.print( user.getName() );

			out.println( "</A></TD>" );
			
			out.print( "<TD>" );
			if ( entry.getMark() != null )
				out.print( entry.getMark().toString() );
			else
				out.print( "~" );
			out.print( "</TD>" );
			
			out.print( "<TD>" );
			if ( entry.getAdjustedMark() != null )
				out.print( entry.getAdjustedMark().toString() );
			else
				out.print( "~" );
			out.print( "</TD></TR>" );
			}

		out.println( "</TABLE>" );
		}


	private void entrystatuslist(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int i, j, status;
		Vector users;
		Hashtable entries;
		User user;
		PigeonHoleEntry entry;
		
		
		users = ph_session.getPigeonHoleUsers();
		entries = ph_session.getPigeonHoleEntries();
		
		out.println( "<TABLE BORDER=1>");
                out.print(  "<TR VALIGN=TOP><TD>Set all entries" );
                out.println( "</TD><TD><SELECT NAME=\"status\">" );
                out.println("<OPTION VALUE=\"-1\">Set individually");
                for ( j=0; j<PigeonHoleEntry.status_message.length; j++ )
		{
			out.print( "<OPTION VALUE=\"" + j + "\"" );
			out.print( " >CHANGE ALL TO: " );
			out.println( PigeonHoleEntry.status_message[j] );
		}
                out.println("</SELECT>");
                out.println("</TD></TR>");
                out.println("</TABLE>");
                out.println("<TABLE BORDER=1>");
                out.println("<TR><TD>Name</TD><TD>Status</TD><TD>Lock</TD>" +
			         "<TD>If set to date dependent would user be able to upload now?</TD></TR>" );
		for ( i=0; i<users.size(); i++ )
			{
			user = (User)users.elementAt( i );
			entry = (PigeonHoleEntry)entries.get( user.getUserId() );
			
			if ( user==null || entry==null )
				{
				out.println( "<TR><TD COLSPAN=3>Problem fetching entry details.</TD></TR>" );
				continue;
				}
				
			out.print(  "<TR VALIGN=TOP><TD>" );
				
			if ( user.getName() == null )
				out.print( "no name" );
			else
				out.print( user.getName() );

			out.println( "</TD><TD><SELECT NAME=\"status" + entry.getPigeonHoleEntryId() + "\">" );
			for ( j=0; j<PigeonHoleEntry.status_message.length; j++ )
				{
				out.print( "<OPTION VALUE=\"" + j + "\"" );
				if ( j==entry.getStatus() )
					{
					out.print( " SELECTED>" );
					out.println( PigeonHoleEntry.status_message[j] );
					}
				else
					{
					out.println( " >CHANGE TO: " );
					out.println( PigeonHoleEntry.status_message[j] );
					}
				}

			status=entry.getStatus();
			entry.setStatus( PigeonHoleEntry.STATUS_UPLOAD_BY_DATE );
			if ( ph_session.canUpload( user.getUserId() ) )
                            out.println( "</SELECT></TD><TD><INPUT NAME=\"lock" + entry.getPigeonHoleEntryId() + "\" TYPE=CHECKBOX VALUE=\"TRUE\"></TD><TD>Yes</TD>" );
			else
			    out.println( "</SELECT></TD><TD><INPUT NAME=\"lock" + entry.getPigeonHoleEntryId() + "\" TYPE=CHECKBOX VALUE=\"TRUE\"></TD><TD>No, because " + 
								ph_session.denyUploadMessage( user.getUserId() ) +
								".</TD>" );
			entry.setStatus( status );

			out.println( "</TR>" );
			}

		out.println( "</TABLE>" );
		}

	private void entrystatusconfirm(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int i, j, status;
		boolean changeall=false;
                boolean locked = false;
		String param;
                String lock;
		Vector users;
		Hashtable entries;
		User user;
		PigeonHoleEntry entry;
		
		//check to see if change must be made to all entries
		
                param = req.getParameter("status");
                if (param != null)
                {
                    if (param.equalsIgnoreCase("-1")) 
                         changeall = false;
                    else changeall = true;
                }
                
		users = ph_session.getPigeonHoleUsers();
		entries = ph_session.getPigeonHoleEntries();
		
		out.println( "<TABLE BORDER><TR><TD>Name</TD><TD>Status</TD></TR>" );
		for ( i=0; i<users.size(); i++ )
			{
			user = (User)users.elementAt( i );
			entry = (PigeonHoleEntry)entries.get( user.getUserId() );
			
			if ( user==null || entry==null )
				{
				out.println( "<TR><TD COLSPAN=3>Problem fetching entry details.</TD></TR>" );
				continue;
				}
		
                        //check to see if this entry is locked
                        lock = req.getParameter("lock"+entry.getPigeonHoleEntryId().toString());
                        if (lock == null) locked=false; else locked=true;
                   
                        if ((changeall==false)||locked)
			param=req.getParameter( "status" + entry.getPigeonHoleEntryId().toString() );
                        else
                            param=req.getParameter("status");
               
			if ( param==null ) continue;
			
			try
				{
				status=Integer.parseInt( param );
				}
			catch ( Exception numex )
				{
				continue;
				}
			if ( status == entry.getStatus() ||
					status < 0 ||
					status > PigeonHoleEntry.STATUS_MAX )
				continue;
				
			// got this far so entry must need changing	
			
			ph_session.recordStatus( entry.getPigeonHoleEntryId(), status );
			
			out.print(  "<TR VALIGN=TOP><TD>" );
				
			if ( user.getName() == null )
				out.print( "no name" );
			else
				out.print( user.getName() );

			out.println( "</TD><TD>" );
			out.println( PigeonHoleEntry.status_message[status] );
			out.println( "</TD></TR>" );
			}

		out.println( "</TABLE>" );
		}



	private void newq( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException
		{
		try
			{
			ph_session.createQuestion();
			}
		catch ( Exception ex )
			{
			out.println( "<HR>A technical problem occured while trying to create a new question.<HR>" + ex.getMessage() );
			}
		}	

	
	private void markdeleteq(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int n;
		String id, param, notparam;
		PrimaryKey qid;

		if ( !BuildingContext.getContext().checkPermission( Permission.EDIT ) )
			{
			out.println( "<PRE>You don't have permission to delete (edit) questions in this location of the building.</PRE>\n" );
			return;
			}

		//must have an id in the parameter part of the URL
		if ( req.getTemplateParameterCount() < 1 )
			{
			out.println( "<I>Can't delete question.</I>" );
			return;
			}
		id=(String)req.getTemplateParameter( 0 );
		qid= new PrimaryKey( Integer.parseInt( id ) );

		
		param=req.getParameter( "confirm" );
		if ( param==null ) param="";
		notparam=req.getParameter( "notconfirm" );
		if ( notparam==null ) notparam="";
		if ( param.length()<1 || notparam.length()>0 )
			{
			out.println( "<HR>Wrong checkboxes selected - question not deleted.<HR>" );
			return;
			}

		ph_session.removeQuestion( qid );
		}


	private void markeditqconfirm(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int n;
		String id, param;
		PrimaryKey qid;
		Hashtable questions;
		PigeonHoleQuestion q;
		
		if ( !BuildingContext.getContext().checkPermission( Permission.EDIT) )
			{
			out.println( "<PRE>You don't have permission to edit questions in this location of the building.</PRE>\n" );
			return;
			}


		//must have an id in the parameter part of the URL
		if ( req.getTemplateParameterCount() < 1 )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}
		id=(String)req.getTemplateParameter( 0 );
		qid= new PrimaryKey( Integer.parseInt( id ) );

		
		questions = ph_session.getPigeonHoleQuestions();
		q=(PigeonHoleQuestion)questions.get( qid );

		if ( q==null )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}

		param=req.getParameter( "ordinal" );
		try
			{
			q.setOrdinal( Integer.parseInt( param ) );
			}
		catch ( NumberFormatException ex )
			{
			out.println( "<HR>Invalid number in ordinal field.<HR>" );
			return;
			}

		param=req.getParameter( "available" );
		try
			{
			int a = Integer.parseInt( param );
			if ( a<1 || a>100 )
				{
				out.println( "Available Mark must be between 1 and 100" );
				return;
				}
			q.setAvailable( a );
			}
		catch ( NumberFormatException ex )
			{
			out.println( "<HR>Invalid number in available marks field.<HR>" );
			return;
			}

		param=req.getParameter( "weight" );
		try
			{
			BigDecimal big = new BigDecimal( param );
			if ( big.scale()>3 )
				{
				out.println( "The weighting will be rounded because a maximum of 3 decimal places are allowed." );
				big = big.setScale( 3, BigDecimal.ROUND_HALF_UP );
				}
			if ( big.compareTo( max_weight )>0 )
				{
				out.println( "A weight greater than 100.0 is not allowed - the weight has been adjusted to 100.0." );
				big = max_weight;
				}
			q.setWeight( big );
			}
		catch ( NumberFormatException ex )
			{
			out.println( "<HR>Invalid number in weight field.<HR>" );
			return;
			}


		
		ph_session.changeQuestionText( q.getPigeonHoleQuestionId(), 
										q.getNotesBigStringId(), 
										req.getParameter( "notes" ) );


		ph_session.changeQuestion( q.getPigeonHoleQuestionId(), q );
		
		out.println( "<HR>Question Saved<HR>" );
		}


	private void markitemedit(  Request req, PrintWriter out, PigeonHoleSession ph_session, String insertname )
		throws IOException, BuildingServerException
		{
		String id;
		Hashtable questions;
		PigeonHoleQuestion question;
		
		if ( !BuildingContext.getContext().checkPermission( Permission.EDIT ) )
			{
			out.println( "<PRE>You don't have permission to edit questions in this location of the building.</PRE>\n" );
			return;
			}


		//must have an id in the parameter part of the URL
		if ( req.getTemplateParameterCount() < 1 )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}
		id=(String)req.getTemplateParameter( 0 );

		questions=ph_session.getPigeonHoleQuestions();
		if ( questions == null )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}
			
		question = (PigeonHoleQuestion)questions.get( new PrimaryKey( Integer.parseInt( id ) ) );
		
		if ( question==null )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}

		if ( insertname.equalsIgnoreCase( "ordinal" ) )
			{
			out.println( "<INPUT NAME=ordinal VALUE=\"" +question.getOrdinal() + "\">" );
			return;
			}
		if ( insertname.equalsIgnoreCase( "available" ) )
			{
			out.println( "<INPUT NAME=available VALUE=\"" +question.getAvailable() + "\">" );
			return;
			}
		if ( insertname.equalsIgnoreCase( "weight" ) )
			{
			out.println( "<INPUT NAME=weight VALUE=\"" +question.getWeight().toString() + "\">" );
			return;
			}
		if ( insertname.equalsIgnoreCase( "notes" ) )
			{
			out.print( "<SPAN CLASS=bs-textarea><TEXTAREA NAME=notes COLS=45 ROWS=8>" );
			out.print( question.getNotes() );
			out.println( "</TEXTAREA></SPAN>" );
			return;
			}
		}

	private void questionToHTML( PigeonHoleQuestion question, PrintWriter out, String style, PigeonHoleResponse response )
		throws IOException, BuildingServerException
		{

		out.println( "<TR><TD><B>Notes</B></TD></TR><TR><TD>" );
		out.println( question.getNotes() );
		out.println( "</TD></TR>" );

		if ( style.equalsIgnoreCase("mark") )
			{
			out.println( "<TR><TD><B>Mark</B></TD></TR><TR><TD>" );
			int a = question.getAvailable();
			int r=-1;

			if ( response!=null && response.getMark() != null )
				r = response.getMark().intValue();
				
			out.print( "<SELECT NAME=QR" );
			out.print( question.getPigeonHoleQuestionId().toString() );
			out.println( ">" );
			if ( r==-1 )
				out.println( "<OPTION VALUE=\"-1\" SELECTED>Not Marked" );
			else
				out.println( "<OPTION VALUE=\"-1\">Not Marked" );
				
			for ( int i=0; i<=a; i++ )
				{
				if ( r==i )
					out.println( "<OPTION VALUE=\"" + i + "\" SELECTED>" + i );
				else
					out.println( "<OPTION VALUE=\"" + i + "\">" + i );
				}
			out.println( "</SELECT>" );
			}
		else
			{
			if ( style.equalsIgnoreCase( "markreview" ) )
				{
				out.println( "<TR><TD><B>Mark</B></TD></TR><TR><TD>" );
				if ( response!=null && response.getMark() != null )
					{
					out.print( response.getMark().intValue() );
					out.print( " out of " );
					out.print( question.getAvailable() );
					out.println( "." );
					}
				else
					out.println( "No mark assigned." );
				out.println( "</TD></TR>" );
				}
			else
				{
				out.println( "<TR><TD><B>Available Marks</B></TD></TR><TR><TD>" );
				out.println( question.getAvailable() );
				out.println( "</TD></TR>" );
				}
			}
		
		if ( style.equalsIgnoreCase("edit") )
			{
			out.println( "<TR><TD><B>Weight</B></TD></TR><TR><TD>" );
			out.println( question.getWeight().toString() );
			out.println( "</TD></TR>" );
			}
		}
	


	private void markpaper(  Request req, PrintWriter out, PigeonHoleSession ph_session, String mode )
		throws IOException, BuildingServerException
		{
		int i;
		boolean edit, mark=false, canmark=false;
		String param;
		PrimaryKey uid;
		
		Vector questions_in_order;
		Hashtable responses=null;
		PigeonHoleQuestion q;
		PigeonHoleEntry entry=null;
		PigeonHoleResponse response=null;
		Resource resource;

		edit = ( mode!=null && mode.equalsIgnoreCase( "edit" ) );
		mark = ( mode!=null && mode.equalsIgnoreCase( "mark" ) );

		if ( !edit && !mark )
			{
			out.println( "Unkown presentation format selected." );
			return;
			}
			
		if ( edit )
			{
			if ( !BuildingContext.getContext().checkPermission( Permission.EDIT ) )
				{
				out.println( "<PRE>You don't have permission to edit assessment categories here.</PRE>\n" );
				return;
				}
			uid=null;
			}

		if ( mark )
			{
			if ( !BuildingContext.getContext().checkPermission( Permission.MARK ) )
				{
				out.println( "<PRE>You don't have permission to mark papers here.</PRE>\n" );
				return;
				}

			if ( req.getTemplateParameterCount()<1 )
				{
				out.println( "Invalid page address - no user specified" );
				return;
				}

			param=(String)req.getTemplateParameter( 0 );
			if ( param==null )
				{
				out.println( "Invalid page address - no user specified" );
				return;
				}
			uid = new PrimaryKey( Integer.parseInt( param ) );

			entry=ph_session.getPigeonHoleEntry( uid );
			
			if ( entry==null )
				{
				out.println( "<HR>No database entry for this student.</HR>" );
				return;
				}

			if ( entry.getStatus() == PigeonHoleEntry.STATUS_UPLOAD_BY_DATE || 
				entry.getStatus() == PigeonHoleEntry.STATUS_UPLOAD_CHANGEABLE  )
				{
				out.println( "<HR><CENTER><H1>This student's work is not ready for assessment.</H1></CENTER>" );
				return;
				}
				
			canmark=true;
			if ( entry.getStatus() != PigeonHoleEntry.STATUS_UPLOAD_LOCKED &&
				 entry.getStatus() != PigeonHoleEntry.STATUS_UPLOAD_PROCESSED )
				{
				out.println( "<HR><CENTER><H1>Assessment of this student's work is completed.</H1></CENTER>" );
				canmark=false;
				}
				
			responses=ph_session.getPigeonHoleResponses( entry.getPigeonHoleEntryId() );
			if ( responses==null )
				responses = new Hashtable();
			
			if ( canmark )
				out.println( "<FORM ACTION=bs_template_pigeonmarkconfirm.html METHOD=POST>" );
			}
		
		resource = BuildingContext.getContext().getResource();

		questions_in_order = ph_session.getPigeonHoleQuestionsInOrder();
			
			
		for ( i=0; i<questions_in_order.size(); i++ )
			{
			q=(PigeonHoleQuestion)questions_in_order.elementAt( i );
				
			out.println( "<P></P><TABLE BORDER><TR><TD><H4>Category " + 
					(i+1) + " (" + q.getOrdinal() + ")</H4>" );
						
			if ( edit )
				{
				out.print( "<P><FORM METHOD=POST ACTION=\"" );
				out.print( req.getContextPath() );
				out.print( req.getServletPath() );
				out.print( resource.getFullName()  );
				out.print( q.getPigeonHoleQuestionId() + "/bs_template_markeditq.html\">" );
				out.println( "<INPUT TYPE=SUBMIT VALUE=\"Edit This Question\"></FORM></P>" );
				}
					
			out.println( "</TD></TR>" );
				
			if ( mark )
				{
				response = (PigeonHoleResponse)responses.get( q.getPigeonHoleQuestionId() );
				if ( canmark )
					questionToHTML( q, out, mode, response );
				else
					questionToHTML( q, out, "markreview", response );
				}
			else
				questionToHTML( q, out, mode, null );
				
			out.println( "</TABLE>" );
			}

		if ( i==0 )
			out.println( "<P>There are currently no assessment categories for this pigeon hole.</P>" );

		if ( mark )
			{
			if ( canmark )
				{
				out.println( "<H4>General Feedback</H4>" );
				out.print( "<SPAN CLASS=bs-textarea><TEXTAREA COLS=50 ROWS=5 NAME=feedback>" );
				if ( entry.getFeedback()!=null )
					out.print( entry.getFeedback() );
				out.println( "</TEXTAREA></SPAN><BR>" );
				out.println( "<H4>Private Comments</H4>" );
				out.print( "<SPAN CLASS=bs-textarea><TEXTAREA COLS=50 ROWS=5 NAME=comments>" );
				if ( entry.getComments()!=null )
					out.print( entry.getComments() );
				out.println( "</TEXTAREA></SPAN<<BR>" );
				out.println( "<INPUT TYPE=SUBMIT VALUE=\"Save Changes\">" );
				out.println( "</FORM>" );
				}
			else
				{
				if ( BuildingContext.getContext().checkPermission( Permission.MANAGE )  && !ph_session.canSeeMark( entry.getUserId() ))
					{
					out.println( "<HR><H4>Assign Adjusted Mark for Release</H4>" );
					out.println( "<P>The final released mark may be adjusted.  E.g." );
					out.println( " a penalty for handing in late or extra marks " );
					out.println( " because the student was absent or suffered some  " );
					out.println( " other impediment.</P>" );
					out.println( "<FORM NAME=adjustedform METHOD=POST ACTION=bs_template_adjustedmarkconfirm.html>" );
					out.println( "<TABLE CLASS=bs-table-opaque><TR><TD><B>Uploaded:</B></TD><TD><H4>" );
					if ( entry.getWhenSaved()!=null )
						{
						String d=DateFormatter.formatDate( entry.getWhenSaved(), 1 );
						out.println( d );
						}
					else
						out.println( "No date." );
					out.println( "</H4></TR><TR><TD><B>Total Mark:</B></TD><TD><H2>" );
					if ( entry.getMark()!=null )
						{
						out.println( entry.getMark().toString() );
						out.print( "<INPUT TYPE=BUTTON VALUE=\"Set\" ONCLICK=" );
						out.print( "\"adjustedform.adjustedmark.value=" );
						out.print( entry.getMark().toString() );
						out.println( "\">" );
						}
					else
						out.println( "No Mark" );
					out.println( "</H2></TD></TR><TR><TD><B>Adjusted Mark</B></TD><TD>" );
					out.print( "<INPUT NAME=adjustedmark VALUE=\"" );
					if ( entry.getAdjustedMark()!=null )
						out.print( entry.getAdjustedMark().toString() );
					out.println( "\"></TD></TR><TR><TD><B>General Feedback</B></TD><TD>" );
					out.print( "<SPAN CLASS=bs-textarea><TEXTAREA COLS=50 ROWS=5 NAME=feedback>" );
					if ( entry.getFeedback()!=null )
						out.print( entry.getFeedback() );
					out.println( "</TEXTAREA></SPAN>" );
					out.println( "</TD></TR><TR><TD><B>Private Comments</B></TD><TD>" );
					out.print( "<SPAN CLASS=bs-textarea><TEXTAREA COLS=50 ROWS=5 NAME=comments>" );
					if ( entry.getComments()!=null )
						out.print( entry.getComments() );
					out.println( "</TEXTAREA></SPAN></TD></TR><TR><TD></TD><TD>" );
					out.println( "<INPUT TYPE=SUBMIT VALUE=\"Save Adjusted Mark\">" );
					out.println( "</TD></TR></TABLE></FORM>" );
					}
				else
					{
					out.println( "<P>It isn't possible to edit marks or to save a final adjusted mark.</P>" );
					}
				}
			}			
		}
	
	private void markpaperconfirm(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int i;
		Vector questions_in_order;
		PrimaryKey[] qids;
		int[] marks;
		String feedback, comments, uid;
		PrimaryKey user_id;
		PigeonHoleQuestion q;
		PigeonHoleEntry entry;

		
		if ( !BuildingContext.getContext().checkPermission( Permission.MARK ) )
			{
			out.println( "<PRE>You don't have permission to mark papers here.</PRE>\n" );
			return;
			}

		if ( req.getTemplateParameterCount()<1 )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		uid=(String)req.getTemplateParameter( 0 );
		if ( uid==null )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		user_id = new PrimaryKey( Integer.parseInt( uid ) );
		entry = ph_session.getPigeonHoleEntry( user_id );
		if ( entry == null )
			{
			out.println( "Unable to find database entry." );
			return;
			}
		
		
		questions_in_order = ph_session.getPigeonHoleQuestionsInOrder();
		qids = new PrimaryKey[questions_in_order.size()];
		marks = new int[questions_in_order.size()];

		feedback=req.getParameter( "feedback" );
		comments=req.getParameter( "comments" );
		if ( feedback==null || comments==null )
			{
			out.println( "<HR>Invalid form input.<HR>" );
			return;
			}
		
		for ( i=0; i<questions_in_order.size(); i++ )
			{
			q=(PigeonHoleQuestion)questions_in_order.elementAt( i );
			
			qids[i] = q.getPigeonHoleQuestionId();
			
			marks[i] = Integer.parseInt( req.getParameter( "QR" + q.getPigeonHoleQuestionId() ) );
			
			if ( marks[i]<0 )
				out.println( "Warning: no mark assigned for category " + (i+1) + "<BR>" );
			}
			
		entry = ph_session.recordMarks( entry.getPigeonHoleEntryId(), qids, marks, feedback, comments );
			
		out.println( "<P>Entries were recorded." );
		
		if ( entry.getMark() != null )
			{
                        //set a provisional adjusted mark
                        entry.setAdjustedMark(entry.getMark());    
           
			out.print( "<BR>The total mark, before moderation is " );
			out.print( entry.getMark().toString() );
			out.print( " out of " );
			PigeonHole ph = ph_session.getPigeonHole();
			out.print( ph.getMaximumMark() );
			out.println( "." );
			}
		else
			out.println( "Marking is incomplete for this work." );
			
		out.print( "</P>" );
		/*
		String uid, feedback, comments, m;
		ResultSet results;
		Connection con;
		PigeonHoleEntry entry;
		PigeonHoleQuestion question;
		PigeonHoleResponse response;
		Hashtable responses;
		double total;
		boolean complete;
		

		if ( req.getTemplateParameterCount()<1 )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		uid=(String)req.getTemplateParameter( 0 );
		if ( uid==null )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		User user = (User)BuildingContext.getContext().getUser();
		
		try
			{
			con=db.getConnection();
			entry=getEntry( req.getResource().getResourceId().intValue(), uid );
			
			if ( entry==null )
				{
				out.println( "<HR>No database entry for that student.</HR>" );
				return;
				}

			responses=getResponses( entry );
			
			Enumeration enumeration=ph.getQuestions();
			total=0;
			complete=true;
			for ( int q=1; enumeration.hasMoreElements(); q++ )
				{
				out.println( "<H4>Assessment Category " + q + "</H4>" );
				question=(PigeonHoleQuestion)enumeration.nextElement();
				response=(PigeonHoleResponse)responses.get( question.question_id );
				if ( response==null )
					{
					response=new PigeonHoleResponse();
					response.result_id=entry.id;
					response.question_id=question.question_id;
					response.feedback=null;
					response.mark=null;
					response.insert( con );
					}
				
				m=req.getParameter( "QR" + question.question_id );
				if ( m==null )
					{
					out.println( "<P>WARNING - no mark given in category.</P> " + q );
					response.mark=null;
					complete=false;
					}
				else
					{
					out.println( "<P>Mark: " + m + "</P>");
					try
						{
						response.mark=new Integer( m );
						total+=
							question.weight.doubleValue() *
							(double)response.mark.intValue()
							/(double)question.available.intValue();
						}
					catch ( NumberFormatException numex )
						{
						response.mark=null;
						out.println( "<P>Technical problem saving mark.</P>" );
						}
					}
				response.update( con );
				}

			feedback=req.getParameter( "feedback" );
			comments=req.getParameter( "comments" );
			if ( feedback==null || comments==null )
				{
				out.println( "<HR>Invalid form input.<HR>" );
				return;
				}
			entry.marker=req.string_user_id;
			java.util.Date d=new java.util.Date();
			entry.when_marked=new java.sql.Timestamp( d.getTime() );
			entry.feedback=feedback;
			entry.comments=comments;

			if ( complete )
				entry.mark=new Integer( Math.round( (float)(total*10.0) ) );
			else
				entry.mark=null;

			if ( entry.status_upload.intValue() == PigeonHoleEntry.STATUS_UPLOAD_LOCKED )
				entry.status_upload = new Integer( PigeonHoleEntry.STATUS_UPLOAD_PROCESSED );
			entry.update( con );
			
			
        	AssessmentEvent event = new AssessmentEvent( 
        					AssessmentEvent.EVENT_RECORD_ASSESSMENT,
        					req.getResource().getResourceId(), 
        					user.getUserId(), 
							null,
        					new Integer( entry.id.intValue() ), 
        					entry.mark==null?null:new BigDecimal( entry.mark.toString() )
        					);
        	event.save();
			}
		catch ( Exception ex )
			{
			out.println( "</TABLE>" );
			out.println( ex.toString() );
			}
		*/
		}

	private void adjustedmarkconfirm(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int i, amark;
		Vector questions_in_order;
		PrimaryKey[] qids;
		String feedback, comments, uid;
		PrimaryKey user_id;
		PigeonHoleQuestion q;
		PigeonHoleEntry entry;

		
		if ( !BuildingContext.getContext().checkPermission( Permission.MARK ) )
			{
			out.println( "<PRE>You don't have permission to mark papers here.</PRE>\n" );
			return;
			}

		if ( req.getTemplateParameterCount()<1 )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		uid=(String)req.getTemplateParameter( 0 );
		if ( uid==null )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		user_id = new PrimaryKey( Integer.parseInt( uid ) );
		entry = ph_session.getPigeonHoleEntry( user_id );
		if ( entry == null )
			{
			out.println( "Unable to find database entry." );
			return;
			}
		
		
		feedback=req.getParameter( "feedback" );
		comments=req.getParameter( "comments" );

		if ( feedback==null || comments==null )
			{
			out.println( "<HR>Invalid form input.<HR>" );
			return;
			}
		
		//general marks.
		amark=-1;
		if ( req.getParameter( "adjustedmark" )!=null && req.getParameter( "adjustedmark" ).length()>0 )
			{
			try
				{
				amark = Integer.parseInt( req.getParameter( "adjustedmark" ) );
				}
			catch ( NumberFormatException nfex )
				{
				out.println( "The value entered in the adjusted mark box was not recognized as a number." );
				return;
				}
			}
			
		
		entry = ph_session.recordAdjustedMark( entry.getPigeonHoleEntryId(), amark, feedback, comments );
			
		out.println( "<P>Your entries were recorded.</P>" );
		
			
		}
/*
	private void remark(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException
		{
		String uid, feedback, comments, m;
		ResultSet results;
		Connection con;
		PigeonHoleEntry entry;
		PigeonHoleQuestion question;
		PigeonHoleResponse response;
		Hashtable responses=new Hashtable();
		Vector questions=new Vector();
		Vector entries=new Vector();
		

		try
			{
			con=db.getConnection();

			results=db.select( 
				"SELECT * FROM pigeon_hole_questions WHERE (location = " + 
				req.location.id +
				")" );
			while ( results.next() )
				{
				question = new PigeonHoleQuestion();
				question.load( results );
				questions.addElement( question );
				}

			results=db.select( 
				"SELECT id, location, user_id, up_changed, down_changed, " +
				"deleted, marker, when_marked, mark, penalty, adjusted_mark, " +
				"feedback, comments, DATALENGTH(upload) up_length, " +
				"DATALENGTH(download) down_length " +
				"FROM pigeon_hole_entries WHERE (location = " +
				req.location.id +
				")" );
			while ( results.next() )
				{
				entry=new PigeonHoleEntry();
				entry.load( results );
				entries.addElement( entry );
				}


			for ( int s=0; s<entries.size(); s++ )
				{
				entry=(PigeonHoleEntry)entries.elementAt( s );

				responses=new Hashtable();
				results=db.select( 
					"SELECT * FROM pigeon_hole_responses WHERE (result_id = " + 
					entry.id +
					")" );
				while ( results.next() )
					{
					response = new PigeonHoleResponse();
					response.load( results );
					responses.put( response.question_id, response );
					}

				entry.mark=new Integer( 0 );
				for ( int q=0; q<questions.size(); q++ )
					{
					question=(PigeonHoleQuestion)questions.elementAt( q );
					response=(PigeonHoleResponse)responses.get( question.question_id );
					if ( response==null || response.mark==null )
						{
						out.println( "<HR>Missing response for: " + entry.user_id + "<BR>" );
						entry.mark=null;
						break;
						}
					else
						{
						entry.mark=new Integer( 
								entry.mark.intValue() + 
								response.mark.intValue() );
						}
				 	}
				entry.update( con );
				}
			}
		catch ( Exception ex )
			{
			out.println( "</TABLE>" );
			out.println( ex.toString() );
			}
		}
*/	

/*
	private void eventlog(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException
		{
		int j;
		String sql;
		PreparedStatement statement;
		Connection con;
		ResultSet results;
		java.util.Date jdate;
		Event event;

		if ( !BuildingContext.getContext().checkPermission( "manage" ) )
			{
			out.println( "<HR>You need manage access to view the log.<HR>" );
			return;
			}

		try
			{
			sql="SELECT * " +
				"FROM events_detail " +
				"WHERE (location = ? AND detail_level <= 1000 ) " +
				"ORDER BY happened ASC";
			con=db.getConnection();
			statement=con.prepareStatement( sql );
			statement.setInt( 1, req.location.id.intValue() );
			results=statement.executeQuery();
			
			for ( j=0; results.next();  )
				{
				event=new Event( );
				event.load( results );
				jdate=new java.util.Date( event.happened.getTime() );
				
				if ( j==0 )
					out.println( "</A></H3><TABLE CLASS=bs-table-opaque>" );

				out.println( "<TR><TD><I>" );
				outputDate( req, out, jdate, 0 );
				out.println( "</I></TD><TD>" );
				printEvent( out, event );
				out.println( "</TD></TR>" );
				j++;
				}
			if ( j>0 )
				out.println( "</TABLE>" );
			}
		catch ( Exception ex )
			{
			out.println( "<HR>There was a technical problem trying to get a list of events.<HR>" + ex.getMessage() );
			return;
			}
		
		}
*/
	
	private void uplink( Request req, PrintWriter out, PigeonHoleSession ph_session, String url )
		throws ServletException, IOException, BuildingServerException
		{
		PigeonHoleEntry entry;
		UploadedFileSummary uf;
		PrimaryKey uid=null;
		
	    if ( req.getTemplateParameterCount()>0 )
	    	{
	    	uid = new PrimaryKey( Integer.parseInt( (String)req.getTemplateParameter( 0 ) ) );
			entry = ph_session.getPigeonHoleEntry( uid );
			}
		else
			{
			entry = ph_session.getPigeonHoleEntry();
			if ( entry!=null )
			    uid = entry.getUserId();
			}
		
		if ( entry == null || uid == null )
			return;
			
		uf = ph_session.getUploadedFileSession().getFileSummary( entry.getUploadedFileId() );
		if ( uf == null )
			return;
		
		out.print( "<A HREF=\"" + req.absoluteURL() );
        out.print( uid.toString() );
        out.print( "/" );
        out.print( uf.getUrl() );
        out.print( "\">" );
        out.print( uf.getName() );
        out.print( "</A>" );
        
		
		}
	
	private void pigeonfield( Request req, PrintWriter out, PigeonHoleSession ph_session, String name )
		throws ServletException, IOException, BuildingServerException
		{
		if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE ) )
			{
			out.println( "<PRE>You don't have permission to manage this pigeon hole.</PRE>\n" );
			return;
			}


		PigeonHole ph = ph_session.getPigeonHole();

		if ( name.equalsIgnoreCase( "maximum_mark" ) )
			{
			out.print( "<INPUT NAME=maximum_mark VALUE=\"" );
			out.print( ph.getMaximumMark() );
			out.println( "\">" );
			return;
			}
		if ( name.equalsIgnoreCase( "date_open" ) )
			{
			out.print( "<INPUT NAME=date_open VALUE=\"" );
			if ( ph.getDateOpen()!=null )
				DateFormatter.outputDate( out, ph.getDateOpen(), 1 );
			out.println( "\">" );
			return;
			}
		if ( name.equalsIgnoreCase( "date_deadline" ) )
			{
			out.print( "<INPUT NAME=date_deadline VALUE=\"" );
			if ( ph.getDateDeadline()!=null )
				DateFormatter.outputDate( out, ph.getDateDeadline(), 1 );
			out.println( "\">" );
			return;
			}
		if ( name.equalsIgnoreCase( "date_penalty" ) )
			{
			out.print( "<INPUT NAME=date_penalty VALUE=\"" );
			if ( ph.getDatePenalty()!=null )
				DateFormatter.outputDate( out, ph.getDatePenalty(), 1 );
			out.println( "\">" );
			return;
			}

		
		return;
		}


	private void pigeonmodify( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException, BuildingServerException
		{
		String strdate;
		int max;


		Timestamp timestamps[] = convertToTimestamps(parseDates(req));

		strdate=req.getParameter( "maximum_mark" );
		try
			{
			max = Integer.parseInt( strdate );
			}
		catch ( NumberFormatException nfex )
			{
			out.println( "The entry in the maximum mark field could not be understood as a valid number - changes have not been saved." );
			return;
			}
			
		ph_session.setDates( timestamps[0], timestamps[1], timestamps[2] );
		
		out.println( "<p>Dates saved.</P>" );
		
		if ( max<1 )
			{
			max = 1;
			out.println( "The maximum mark field has been adjusted up to 1." );
			}

		if ( max>100 )
			{
			max = 100;
			out.println( "The maximum mark field has been adjusted down to 100." );
			}
		ph_session.setMaximumMark( max )	;
		
		out.println( "<p>Maximum mark saved.</P>" );
		}
	
	
	/**
	 * Utility method to convert an array of Dates to an array of Timestamps.
	 * If a date is null the timestamp is also null.
	 * @param dates The array of dates to convert.
	 * @return An array of Timestamps.
	 */
	private Timestamp[] convertToTimestamps(Date dates[])
	{
		Timestamp timestamps[] = new Timestamp[dates.length];
		
		for (int i = 0; i < dates.length; i++)
		{
			if (dates[i] == null)
				timestamps[i] = null;
			else
				timestamps[i] = new Timestamp(dates[i].getTime());
		}
		return timestamps;
	}
	
	public boolean canDownload(String path)
	{
		return false;
	}


	}


