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


import org.apache.log4j.Logger;


import org.bodington.servlet.*;

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

import org.bodington.server.*;
import org.bodington.server.realm.AliasEditor;
import org.bodington.server.realm.PassPhrase;
import org.bodington.server.realm.Permission;
import org.bodington.server.realm.User;
import org.bodington.server.realm.Group;
import org.bodington.server.realm.WebAuthUser;
import org.bodington.server.events.*;
import org.bodington.server.resources.Resource;
import org.bodington.server.resources.ResourceUtils;
import org.bodington.util.DateFormatter;
import org.bodington.database.PrimaryKey;
import org.bodington.assessment.*;

public class MCQFacility extends org.bodington.servlet.facilities.Facility
	{
    
    private static Logger log = Logger.getLogger(MCQFacility.class);
	

	static final int EVENT_RECORD  =0;

	public Resource newResource()
		{
		return new McqPaper();
		}
	
    public boolean canCopy( Resource resource )
    {
        return true;
    }
    
    public List postCreate(Request request, Resource resource) throws Exception
    {
        List errors = super.postCreate(request, resource);
        Permission grant[] = {Permission.RECORD, Permission.REVIEW};
        if (ResourceUtils.grantPermissions(resource, grant))
        {
            try {
                resource.getAcl().save();
                return errors;
            } catch (BuildingServerException e) {
                log.error("Failed to save resource ACL.");
            }
        }
        errors = new ArrayList(errors);
        errors.add("Failed to add extra permissions.");
        return errors;
    }
    
	public List initResource( HttpServletRequest resource, Resource new_resource )
		{
	    if ( !(new_resource instanceof McqPaper) )
	        throw new IllegalArgumentException( "Technical problem: An incorrect type of resource was created." );
	    List errors = new LinkedList();
	    
	    McqPaper mcq_paper = (McqPaper)new_resource;

		String once, mark_witheld, dead;
		java.util.Date deaddate;
		java.sql.Timestamp sqldeaddate=null;

		once=resource.getParameter( "once_only" );
		mark_witheld = resource.getParameter( "mark_witheld" );
		dead=resource.getParameter( "deadline" );
		if ( dead!=null )
			{
			dead=dead.trim();
			if ( dead.length()>0 )
				{
				deaddate=DateParser.parse( dead );
				if ( deaddate==null )
					errors.add( "The deadline date couldn't be understood." );
				else
					sqldeaddate=new java.sql.Timestamp( deaddate.getTime() );
				}
			}
		
		mcq_paper.setOnceOnly( once!=null && once.length()>0 );
		mcq_paper.setMarkWitheld( mark_witheld!=null && mark_witheld.length()>0 );
		mcq_paper.setDeadline( sqldeaddate );
		
		errors.addAll(super.initResource(resource, new_resource));

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

		 McqPaper original = (McqPaper)original_resource;
		 McqPaper new_paper = (McqPaper)new_resource;

		 new_paper.setOnceOnly( original.isOnceOnly() );
		 new_paper.setMarkWitheld( original.isMarkWitheld() );
		 new_paper.setDeadline( original.getDeadline() );
		 new_paper.setMcqFlags( original.getMcqFlags() );

		 return true;
	}


    /**
     * Copy the content of one resource to another. <p>
     * <h4>Content copied</h4>
     * <ul>
     * <li>authored questions.
     * <li>authored statements.
     * <li>authored explanations.
     * <li>authored answers.
     * </ul>
     * <h4>Content <em>not</em> copied</h4>
     * <ul>
     * <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, statements, explanations, answers
//	  userdata : responses /** @todo  */

		 BuildingSession session = BuildingSessionManagerImpl.getSession( original_resource );
		 if ( !(session instanceof McqPaperSession) )
		   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.
     *
     */
	private void copyAuthoredContent( Resource original_resource, Resource new_resource )
		throws BuildingServerException
	      	{
		 McqPaperSession session;
		 Vector questions;
		 Hashtable responses;
		 McqQuestion original, new_q;
		 String qtext, explain;

		 session = (McqPaperSession)BuildingSessionManagerImpl.getSession( original_resource );
		 questions = session.getMcqQuestionsInOrder();

		 for (int i=0; i<questions.size(); i++ )
		 {
		   original = (McqQuestion)questions.elementAt( i );
		   new_q = new McqQuestion();
		   new_q.setResourceId( new_resource.getResourceId() );
		   new_q.setQuestion( original.getQuestion() );
		   new_q.setExplanation( original.getExplanation() );
           // don't need this - it's coded in flags
		   // new_q.setOneTrueAnswer( original.isOneTrueAnswer() );
		   for ( int n=0; n<5; n++ )
		   {
		     new_q.setStatement( n, original.getStatement(n) );
		     new_q.setAnswer( n, original.getAnswer(n) );
		   }
		   new_q.setOrdinal( original.getOrdinal() );
		   new_q.setFlags( original.getFlags() );// needed?  - yes.
		   new_q.save();
		 }
	}

		
	public boolean createCheck( Request breq, PrintWriter out )
		{
		//default creation does nothing but other facilities can
		//check user input print errors and prevent resource creation
		//by returning false.
		
		log.debug( "Checking MCQ creation input." );
		
		java.util.Date deaddate, now;
		now = new java.util.Date();
		String dead=breq.getParameter( "deadline" );
		if ( dead!=null )
		{
		    dead=dead.trim();
		    if ( dead.length()>0 )
		    {
		        //there is date input so check it now
		        deaddate=DateParser.parse( dead );
		        if ( deaddate==null )
		        {
		            out.println( "<HR>The date was not in a format that could be understood.  " +
		                "Please use d/m/yyyy format.  (d = 1 or 2 digit date, m = 1 or two digit month, " +
		            "yyyy= four digit year)" );
		            return false;
		        }
		        log.debug( deaddate );
		        if ( now.after( deaddate ) )
		        {
		            out.println( "<HR>The date was not acceptable as a deadline because it has already passed.  " );
		            return false;
		        }
		        if ( deaddate.getYear() > (now.getYear()+100) )
		        {
		            out.println( "<HR>The date was not acceptable as a deadline because it is in the very distant future.  " );
		            return false;
		        }
		    }
		}

		
		return true;
		}


	public void insert( Request req, PrintWriter out, String command, String insertname )
		throws ServletException, IOException
		{
		log.debug( Thread.currentThread().getName() + " MCQFacility insert()" );
		
		try
			{
			BuildingSession session;
			McqPaperSession mcq_session;
			McqPaper mcq;

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


			if ( command.equalsIgnoreCase( "importmcqcards" ) ||
		    	command.equalsIgnoreCase( "mcqpaper" ) ||
		    	command.equalsIgnoreCase( "mcqrecord" ) ||
		    	command.equalsIgnoreCase( "mcqitemedit" ) ||
		    	command.equalsIgnoreCase( "mcqeditqconfirm" ) ||
		    	command.equalsIgnoreCase( "mcqdeleteq" ) ||
		    	command.equalsIgnoreCase( "mcqmarks" ) ||
		    	command.equalsIgnoreCase( "mcqrunbutton" ) ||
		    	command.equalsIgnoreCase( "mcqfield" ) ||
		    	command.equalsIgnoreCase( "mcqmodify" ) ||
		    	command.equalsIgnoreCase( "mcqanalysis" ) ||
		    	command.equalsIgnoreCase( "ifmcqonceonly" ) ||
		    	command.equalsIgnoreCase( "ifmcqonceonlydone" ) ||
		    	command.equalsIgnoreCase( "ifmcqdeadline" ) ||
				command.equalsIgnoreCase( "ifmcqpassed" ) ||
		    	command.equalsIgnoreCase( "mcqnewq" ) ||
		    	command.equalsIgnoreCase( "import" ) ||
		    	command.equalsIgnoreCase( "export" )	)
				{
				if ( mcq==null )
					{
					out.println( "<HR>Unable to load MCQ from database.</HR>" );
					return;
					}
				if ( command.equalsIgnoreCase( "import" ) )
					importq( req, out, mcq_session );
				if ( command.equalsIgnoreCase( "export" ) )
					exportXml( req, out, mcq_session );
				if ( command.equalsIgnoreCase( "importmcqcards" ) )
					importmcqcards( req, out, mcq_session, mcq,insertname );
				if ( command.equalsIgnoreCase( "mcqpaper" ) )
					mcqpaper( req, out, mcq_session, mcq,insertname );
				if ( command.equalsIgnoreCase( "mcqrecord" ) )
					mcqrecord( req, out, mcq_session, mcq,insertname );
				if ( command.equalsIgnoreCase( "mcqnewq" ) )
					mcqnewq( req, out, mcq_session, mcq,insertname );
				if ( command.equalsIgnoreCase( "mcqitemedit" ) )
					mcqitemedit( req, out, mcq_session, mcq,insertname );
				if ( command.equalsIgnoreCase( "mcqeditqconfirm" ) )
					mcqeditqconfirm( req, out, mcq_session, mcq,insertname );
				if ( command.equalsIgnoreCase( "mcqdeleteq" ) )
					mcqdeleteq( req, out, mcq_session, mcq );
				if ( command.equalsIgnoreCase( "mcqmarks" ) )
					mcqmarks( req, out, mcq_session, mcq );
				if ( command.equalsIgnoreCase( "mcqrunbutton" ) )
					mcqrunbutton( req, out, mcq_session, mcq );
				if ( command.equalsIgnoreCase( "mcqfield" ) )
					mcqfield( req, out, mcq_session, mcq,insertname );
				if ( command.equalsIgnoreCase( "mcqmodify" ) )
					mcqmodify( req, out, mcq_session, mcq );
				if ( command.equalsIgnoreCase( "mcqanalysis" ) )
					mcqanalysis( req, out, mcq_session, mcq );
				if ( command.equalsIgnoreCase( "ifmcqonceonly" ) )
					req.setSwitchedOff(  !mcq.isOnceOnly() );
				if ( command.equalsIgnoreCase( "ifmcqonceonlydone" ) )
					{
					if ( !mcq.isOnceOnly() || req.getUserId()==null || BuildingContext.getContext().checkPermission( Permission.EDIT ) )
						req.setSwitchedOff( true );
					else
						req.setSwitchedOff(  !( onceonlydone( req, mcq_session ) ) );
					}
				if ( command.equalsIgnoreCase( "ifmcqdeadline" ) )
					req.setSwitchedOff(  (mcq.getDeadline()==null) );
				if ( command.equalsIgnoreCase( "ifmcqpassed" ) )
					{
					if ( mcq.getDeadline()==null || req.getUserId()==null || BuildingContext.getContext().checkPermission( Permission.EDIT ) )
						req.setSwitchedOff( true );
					//only if there is a deadline and the user is a student
					else
						{
						java.util.Date now=new java.util.Date();
						out.println( "<P>comparing " + now.toString() + " with " + mcq.getDeadline().toString() + "</P>" );
						req.setSwitchedOff(  !now.after( mcq.getDeadline() ) );
						}
					}
				
				
				return;
				}
			}		
		catch ( BuildingServerException bsex )
			{
			out.println( bsex.toString() );
            log.error("insert()", bsex);
			return;
			}
		
		super.insert( req, out, command, insertname );
		}
	

	private void importmcqcards(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq, String insertname )
		throws IOException
		{
		/*
		BRealm realm=getRealm();
		Database db=realm.getDatabase();
		Connection con=null;
		ResultSet results;
		
		String sql, param;
		int total_available=0, total_right=0, total_wrong=0, total_lost=0;
		int available, right, wrong, lost;
		boolean answer[] = new boolean[5];
		boolean response[] = new boolean[5];
		char letter;
		double mark;
		
		
		Enumeration enumeration; //questions in order
		McqQuestion q;
		McqResult qresult;
		McqResponse qresponse;
		MCQCard card;
		
		Vector cards=new Vector();
		
		if ( !BuildingContext.getContext().checkPermission( "manage" ) )
			{
			out.println( "<PRE>You don't have permission to import questions in this location of the building.</PRE>\n" );
			return;
			}
		
		//takes mcqcards in database and
		//creates entries in McqResults and mcqresponses
		out.println( "<PRE>" );
		
		try
			{
			con=db.getConnection();
			con.setAutoCommit( false );

			sql="SELECT * FROM " + DBObject.schema + "mcq_cards WHERE location = (" + req.getResource().getResourceId() + ")";
			results=db.select( sql );
			
			if ( results==null )
				{
				out.println( "<HR>A problem occurred trying to retrieve the cards.<HR>" );
				return;
				}
			
			while ( results.next() )
				{
				card = new MCQCard();
				card.load( results );
				out.println( card.student_id );
				cards.addElement( card );
				}
			
			for ( int k=0; k<cards.size(); k++ )
				{
				total_available=0;
				total_right=0;
				total_wrong=0;
				total_lost=0;
				
				card=(MCQCard)cards.elementAt( k );
				if ( card==null )
					continue;

				out.print( card.student_id + "\t" );
				
				qresult=new McqResult();
				qresult.user_id=new String( card.student_id );
				qresult.location=new Integer( req.getResource().getResourceId().intValue() );
				qresult.insert( con );

				enumeration=mcq.getQuestions(); //questions in order
				for ( int i=0; enumeration.hasMoreElements(); i++ )
					{
					q=(McqQuestion)enumeration.nextElement();

					answer[0]=q.answer_a;
					answer[1]=q.answer_b;
					answer[2]=q.answer_c;
					answer[3]=q.answer_d;
					answer[4]=q.answer_e;
				
					//get responses in card image
					for ( int j=0; j<5; j++ )
						{
						response[j]=  
							(((int)card.card_image.charAt( i*2 ) - (int)'0') & 
								(1 << (4-j))  ) != 0;
						}

					available=0;
					right=0;
					wrong=0;
					for ( int j=0; j<5; j++ )
						{
						if ( answer[j] )
							{
							available++;
							if ( response[j] ) right++;
							}
						else
							{
							if ( response[j] ) wrong++;
							}
						}
					lost=(wrong>right) ? right : wrong;
					
					total_available+=available;
					total_right+=right;
					total_wrong+=wrong;
					total_lost+=lost;
					

					qresponse=new McqResponse();
					qresponse.result_id=qresult.result_id;
					qresponse.question_id=q.question_id;
					
					qresponse.response_a=response[0];
					qresponse.response_b=response[1];
					qresponse.response_c=response[2];
					qresponse.response_d=response[3];
					qresponse.response_e=response[4];

					qresponse.insert( con );
					}

				qresult.available=new Integer( total_available );
				qresult.deductions=new Integer( total_lost );
				qresult.wrong=new Integer( total_wrong );
				qresult.right=new Integer( total_right );
				java.util.Date now= new java.util.Date();
				qresult.when_done=new java.sql.Timestamp( now.getTime() );
				qresult.when_marked=new java.sql.Timestamp( now.getTime() );
				qresult.update( con );
				
				//card.result_id=qresult.result_id;
				//card.update( con );
				
				mark=	(  ((double)total_right-(double)total_lost)
						   /(double)total_available );

				out.println( card.student_id + "\t" + (Math.round( mark*1000.0 )/10.0) );
				}
			con.commit();
			return;
			}
		catch ( Exception ex )
			{
			out.println( "<HR>A problem occurred trying to store your responses.<HR>" + ex.getMessage() );
			}
		
		try
			{
			if ( con!=null )
				con.rollback();
			}
		catch ( SQLException sqlex )
			{
			out.println( "<HR>A problem occurred trying to roll back database edits." );
			}
		
		*/
		}
	
	private void questionToHTML( McqQuestion question, McqQuestionAnalysis q_analysis, PrintWriter out, String style )
		throws IOException, BuildingServerException
		{
		char letter;
		boolean answer[]=new boolean[5];
		String statement[]=new String[5];
		byte type = question.getQuestionType();
                
                
                if ( style.equalsIgnoreCase( "edit" ) )
                {
                    if ( type==McqQuestion.TYPE_MULTIPLE_TFD )
                        out.println( "<tr><td colspan=\"3\">Multi True/False/Don't Know</td></tr>" );
                    else if ( type==McqQuestion.TYPE_MULTIPLE_TF )
                        out.println( "<tr><td colspan=\"3\">Multi True/False</td></tr>" );
                    else if ( type==McqQuestion.TYPE_STRICT_MULTIPLE_TF )
                        out.println( "<tr><td colspan=\"3\">Strict Multi True/False</td></tr>" );
                    else if ( type == McqQuestion.TYPE_ONE_TRUE )
                        out.println( "<tr><td colspan=\"3\">Single True (MCQ)</td></tr>" );
                    else
                        out.println( "<tr><td colspan=\"3\">Multi True (MRQ)</td></tr>" );
                }

                out.println( "<tr><td colspan=\"3\">" );
                if ( style.equalsIgnoreCase( "run" ) )
                    {
                    out.println( "<input type=\"hidden\" value=\"true\" name=\"Q" + question.getMcqQuestionId() + "\">" );
                    }
                out.println( question.getQuestion() );
                out.println( "</td></tr>" );

                for ( int i=0; i<5; i++ )
			{
			letter=(char)('A'+i);
			if ( style.equalsIgnoreCase( "run" ) )
                            if ( type == McqQuestion.TYPE_MULTIPLE_TF || 
                                 type == McqQuestion.TYPE_STRICT_MULTIPLE_TF || 
                                 type == McqQuestion.TYPE_MULTIPLE_TFD )
                                {    
                                    out.println( "<tr>");
                                    out.println( "<td valign=\"top\" width=\"4%\"><strong>"+letter+"</strong></td>" );
                                    out.println( "<td valign=\"top\" width=\"4%\">" );
                                    out.println( "<select name=\"QR"+question.getMcqQuestionId()+letter+"\">" );
                                    out.println( "<option value=\"UNDECIDED\" selected=\"selected\">Undecided</option>" );
                                    if ( type == McqQuestion.TYPE_MULTIPLE_TFD )
                                        out.println( "<option value=\"DONT\">Don't Know</option>" );
                                    out.println( "<option value=\"TRUE\">True</option>" );
                                    out.println( "<option value=\"FALSE\">False</option>" );
                                    out.println( "</select>" );
                                    out.println( "</td>" );
                                    out.print( "<td valign=\"top\" width=\"80%\">");
                                    out.print( question.getStatement( i ) );
				    out.println( "</td></tr>" );
                                }
                            else
				{
				out.println( "<tr><td valign=\"top\" width=\"20\"><strong>" );
				out.println( letter );
				out.println( "</strong></td><td valign=\"top\" width=\"20\">" );
				if ( type == McqQuestion.TYPE_ONE_TRUE )
					{
					out.print( "<input type=\"radio\" " );
					out.println( "name=\"QR" + question.getMcqQuestionId() + "\" value=\"" + letter + "\" />" );
					}
				else
					{
					out.print( "<input type=\"checkbox\" " );
					out.println( "name=\"" + letter + question.getMcqQuestionId()  + "\" value=\"TRUE\" />" );
					}
				out.println( "</td><td valign=\"top\" width=\"1000\">" );
				out.println( question.getStatement( i ) );
				out.println( "</td></tr>" );
				}
			else
				{
				out.println( "<tr><td valign=\"top\" width=\"20\"><strong>" );
				out.println( letter );
				out.println( "</strong></td><td valign=\"top\" width=\"40\"><em>" );
				out.println( question.getAnswer( i )?"TRUE":"FALSE" );
				out.println( "</em></td><td valign=\"top\" width=\"1000\">" );
				out.println( question.getStatement( i ) );
				out.println( "</td></tr>" );
				}
			}
		
                if ( style.equalsIgnoreCase( "run" ) )
                {
                    if ( type == McqQuestion.TYPE_ONE_TRUE )
                    {
                        out.print( "<tr><td></td><td colspan=\"2\">" );
                        out.print( "<input checked=\"checked\" type=\"radio\" " );
                        out.print( "name=\"QR" + question.getMcqQuestionId() + "\" value=\"UNDECIDED\" />" );
                        out.print( "Undecided</td></tr>" );
                    }
                    else if ( type == McqQuestion.TYPE_MULTIPLE_TRUE )
                    {
                        out.print( "<tr><td></td><td colspan=\"2\">" );
                        out.print( "<input type=\"checkbox\" checked=\"checked\" " );
                        out.println( "name=\"undecided" + question.getMcqQuestionId()  + "\" value=\"TRUE\" />" );
                        out.print( "Undecided</td></tr>" );
                    }
                                    
                    if ( type==McqQuestion.TYPE_MULTIPLE_TFD )
                        out.println( "<tr><td colspan=\"3\"><em>In this question wrong answers cancel out marks for right answers but you cannot score less than zero.</em></td></tr>" );
                    else if ( type==McqQuestion.TYPE_MULTIPLE_TF )
                        out.println( "<tr><td colspan=\"3\"><em>In this question wrong answers are ignored.</em></td></tr>" );
                    else if ( type==McqQuestion.TYPE_STRICT_MULTIPLE_TF )
                        out.println( "<tr><td colspan=\"3\"><em>In this question you only get the marks if you answer all statements correctly.</em></td></tr>" );
                    else if ( type == McqQuestion.TYPE_ONE_TRUE )
                        out.println( "<tr><td colspan=\"3\"><em>In this question wrong answers are ignored.</em></td></tr>" );
                    else
                        out.println( "<tr><td colspan=\"3\"><em>In this question wrong answers cancel out marks for right answers but you cannot score less than zero.</em></td></tr>" );
                }

                if ( style.equalsIgnoreCase( "edit" ) )
			{
			out.println( "<tr><td colspan=\"3\">" );
			out.println( question.getExplanation() );
			out.println( "</td></tr>" );
			}

		if ( style.equals( "analysis" ) && q_analysis!=null )
			{
			out.print( "<tr><td colspan=\"3\"><p>Statistical Analysis</p>" );
			PrintWriter pout = new PrintWriter( out );
			q_analysis.dumpAnalysis( pout );
			pout.flush();
			out.println( "</td></tr>" );
			}
		}
	
    private void questionDebriefToHTML( McqQuestion question, Request request, PrintWriter out, McqResponse response, boolean technique )
        throws IOException, BuildingServerException
    {
        char letter='A';
        int i;
        String guessadvice, certainadvice;
        byte type = question.getQuestionType();

        out.println( "<div>" );
        out.println( question.getQuestion() );
        out.println( "</div>" );

        int right=0, wrong=0, deducted=0, available=0, rounding=0, item_mark;
        boolean item_mark_interesting;

        out.println( "<h3>Mark Breakdown</h3>" );
        // border-collapse and border-spacing won't work with CSS1 browsers
        out.println( "<table style=\"border-spacing: 0px; border-collapse: collapse;\"><tr><td colspan=\"5\">" );
        out.print( "<tr><th>Item</th>" );
        out.print( "<th>Statement</th>" );
        out.print( "<th>Perfect<br />Response</th>" );
        out.print( "<th>Your<br />Response</th>" );
        if ( type == McqQuestion.TYPE_STRICT_MULTIPLE_TF )
            out.print( "<th>Match</th>" );
        else
            out.print( "<th>Mark</th>" );
        out.println( "</tr>" );
        for (i=0; i<5; i++)
        {
            item_mark=0;
            item_mark_interesting=false;
            letter=(char)('A'+i);

            switch ( type )
            {
                case McqQuestion.TYPE_ONE_TRUE:
                    if ( question.getAnswer( i ) )
                    {
                        available++;
                        if ( response.getResponse( i ) == McqResponse.RESPONSE_TRUE )
                        {
                            item_mark = 1;
                            item_mark_interesting=true;
                            right++;
                        }
                    }
                    else
                    {
                        if ( response.getResponse( i ) == McqResponse.RESPONSE_TRUE )
                        {
                            item_mark_interesting=true;
                            wrong++;
                        }
                    }
                    break;
                case McqQuestion.TYPE_MULTIPLE_TRUE:
                    if ( question.getAnswer( i )  )
                    {
                        available++;
                        if ( response.getResponse( i ) == McqResponse.RESPONSE_TRUE )
                        {
                            item_mark = 1;
                            item_mark_interesting=true;
                            right++;
                        }
                    }
                    else
                    {
                        if ( response.getResponse( i ) == McqResponse.RESPONSE_TRUE )
                        {
                            item_mark = -1;
                            item_mark_interesting=true;
                            wrong++;
                        }
                    }                                
                    break;
                case McqQuestion.TYPE_MULTIPLE_TF:
                    available++;
                    if ( response.getResponse( i ) != McqResponse.RESPONSE_DONT_KNOW )
                    {
                        boolean certain_response = response.getResponse( i ) == McqResponse.RESPONSE_TRUE;
                        if ( question.getAnswer( i ) ==  certain_response )
                        {
                            item_mark = 1;
                            item_mark_interesting=true;
                            right++;
                        }
                        else
                        {
                            item_mark_interesting=true;
                            wrong++;
                        }
                    }
                    break;
                case McqQuestion.TYPE_STRICT_MULTIPLE_TF:
                    available++;
                    if ( response.getResponse( i ) != McqResponse.RESPONSE_DONT_KNOW )
                    {
                        boolean certain_response = response.getResponse( i ) == McqResponse.RESPONSE_TRUE;
                        if ( question.getAnswer( i ) ==  certain_response )
                        {
                            item_mark = 1;
                            item_mark_interesting=true;
                            right++;
                        }
                        else
                        {
                            item_mark = -1;
                            item_mark_interesting=true;
                            wrong++;
                        }
                    }
                    break;
                case McqQuestion.TYPE_MULTIPLE_TFD:
                    available++;
                    if ( response.getResponse( i ) != McqResponse.RESPONSE_DONT_KNOW )
                    {
                        boolean certain_response = response.getResponse( i ) == McqResponse.RESPONSE_TRUE;
                        if ( question.getAnswer( i ) ==  certain_response )
                        {
                            item_mark = 1;
                            item_mark_interesting=true;
                            right++;
                        }
                        else
                        {
                            item_mark = -1;
                            item_mark_interesting=true;
                            wrong++;
                        }
                    }
                    break;
            }

            // border-collapse and border-spacing won't work with CSS1 browsers
            String table_cell_attributes_green = "valign=\"top\" style=\"background-color: #77dd77; padding: 0.3em; border: solid 1px black; margin: 0px\"";
            String table_cell_attributes_red = "valign=\"top\" style=\"background-color: #dd7777; padding: 0.3em; border: solid 1px black; margin: 0px\"";
            String table_cell_attributes_grey = "valign=\"top\" style=\"background-color: #777777; padding: 0.3em; border: solid 1px black; margin: 0px\"";
            String cell_span = "<span class=\"bs-plain\" style=\"padding: 0.1em\">";
            
            out.println( "<tr>" );
            if (question.getAnswer(i))
                out.print( "<th width=\"20\" " + table_cell_attributes_green + " >" );
            else
                out.print( "<th width=\"20\" " + table_cell_attributes_red + " >" );
            out.print( cell_span );
            out.println( letter );
            out.print( "</span>" );
            out.println( "</th>" );
            if (question.getAnswer(i))
                out.print( "<td width=\"1000\" " + table_cell_attributes_green + " >" );
            else
                out.print( "<td width=\"1000\" " + table_cell_attributes_red + " >" );
            out.print( cell_span );
            out.print( question.getStatement(i) );
            out.print( "</span>" );
            out.println( "</td>" );
            if ( type == McqQuestion.TYPE_MULTIPLE_TF || 
                 type == McqQuestion.TYPE_STRICT_MULTIPLE_TF || 
                 type == McqQuestion.TYPE_MULTIPLE_TFD )
            {
                if ( question.getAnswer( i ) )
                {
                    out.print( "<td  " + table_cell_attributes_green + " >" );
                    out.print( cell_span );
                    out.print( "true" );
                    out.print( "</span>" );
                }
                else
                {
                    out.print( "<td  " + table_cell_attributes_red + " >" );
                    out.print( cell_span );
                    out.print( "false" );
                    out.print( "</span>" );
                }
            }
            else
            {
                if ( question.getAnswer( i ) )
                {
                    out.print( "<td  " + table_cell_attributes_green + " >" );
                    out.print( cell_span );
                    out.print( "true" );
                    out.print( "</span>" );
                }
                else
                {
                    out.print( "<td  " + table_cell_attributes_red + " >" );
                }
            }
            out.println( "</td>" );
            
            if ( response.getResponse( i ) == McqResponse.RESPONSE_TRUE )
            {
                out.print( "<td  " + table_cell_attributes_green + " >" );
                out.print( cell_span );
                out.print( "true");
                out.print( "</span>" );
            }
            else if ( response.getResponse( i ) == McqResponse.RESPONSE_FALSE )
            {
                if ( type == McqQuestion.TYPE_MULTIPLE_TF || 
                     type == McqQuestion.TYPE_STRICT_MULTIPLE_TF || 
                     type == McqQuestion.TYPE_MULTIPLE_TFD )
                {
                    out.print( "<td  " + table_cell_attributes_red + " >" );
                    out.print( cell_span );
                    out.print( "false");
                    out.print( "</span>" );
                }
                else
                {
                    out.print( "<td  " + table_cell_attributes_red + " >" );
                }
            }
            else
            {
                    out.print( "<td  " + table_cell_attributes_grey + " >" );
            }
            out.println( "</td>" );
            if ( item_mark_interesting )
            {
                if ( type == McqQuestion.TYPE_STRICT_MULTIPLE_TF )
                {
                    if ( item_mark!=0 )
                    {
                        if (item_mark>0)
                        {
                            out.print( "<td  " + table_cell_attributes_green + " >" );
                            out.print( cell_span );
                            out.print( "right" );
                        }
                        else
                        {
                            out.print( "<td  " + table_cell_attributes_red + " >" );
                            out.print( cell_span );
                            out.print( "wrong" );
                        }
                        out.print( "</span>" );
                    }
                    else
                    {
                        out.print( "<td  " + table_cell_attributes_grey + " >" );
                    }
                }
                else
                {
                    if (item_mark>0)
                    {
                        out.print( "<td  " + table_cell_attributes_green + " >" );
                        out.print( cell_span );
                        out.print( "+" );
                    }
                    else
                    {
                        out.print( "<td  " + table_cell_attributes_red + " >" );
                        out.print( cell_span );
                    }
                    out.print( item_mark );
                    out.print( "</span>" );
                }
            }
            else
                    out.print( "<td  " + table_cell_attributes_grey + " >" );
                
            out.println( "</td>" );

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


        switch ( type )
        {
            case McqQuestion.TYPE_ONE_TRUE:
                deducted = 0;
                break;
            case McqQuestion.TYPE_MULTIPLE_TRUE:
                if ( wrong>right )
                {
                    deducted=right;
                    rounding = wrong-right;
                }
                else
                    deducted=wrong;
                break;
            case McqQuestion.TYPE_MULTIPLE_TF:
                deducted = 0;
                break;
            case McqQuestion.TYPE_STRICT_MULTIPLE_TF:
                deducted = (right<available)?right:0;
                break;
            case McqQuestion.TYPE_MULTIPLE_TFD:
                if ( wrong>right )
                {
                    deducted=right;
                    rounding = wrong-right;
                }
                else
                    deducted=wrong;
                break;
        }

        if ( type == McqQuestion.TYPE_STRICT_MULTIPLE_TF )
        {
            out.print( "<tr><td></td><td>On this type of question you only score marks for perfect answers.</td>" );
            out.print( "<td></td><td></td><td></td></tr>" );
        }
            
        if ( rounding > 0 )
        {
            out.print( "<tr><td></td><td>Addition to stop you getting a negative mark on this question.</td><td></td><td></td><td>+" );
            out.print( rounding );
            out.print( "</td></tr>" );
        }
        out.println( "</table>" );


        //put score in separate table
        out.print( "<h4 style=\"text-align: right\">Mark for this question: " );
        out.print( (right-deducted) );
        out.print( " out of " );
        out.print( available );
        out.println( "</h4>" );

        out.println( "<h3>Explanatory Text from Question Author</h3>" );
        out.println( "<div>" );
        out.println( question.getExplanation() );
        out.println( "</div>" );


        if ( !technique )
            return;
        
        //give advice if appropriate
        guessadvice="";
        certainadvice="";
        if ( type == McqQuestion.TYPE_ONE_TRUE )
        {
            if ( wrong==0 && right==0 ) //i.e. no selection
            {
                guessadvice=
                    "You didn't select one of statements.  If you didn't know " +
                    "the answer it wouldn't have done any harm to guess.  This "+
                    "question was the type that always has one true statement so "+
                    "you had a one in five change of getting a mark. ";
                certainadvice=
                    "There are never questions with no true statements.  So if "+
                    "you were certain all statements are false you should have "+
                    "realised you were wrong and guessed a statement.";
            }
            if ( wrong==1 )
            {
                certainadvice=
                    "You selected the wrong statement.  If you were "+
                    "certain about the answer, you ought to do some study to "+
                    "see where you went wrong.";
                guessadvice=
                    "If you didn't know the answer, "+
                    "it was a good idea to guess - you might have gained a mark.";
            }
            if ( right==1 )
            {
                certainadvice=
                    "You selected the right statement.  Well done!";
                guessadvice=
                   "It was a good idea to guess - you picked up a mark.";
            }
        }
        else if ( type == McqQuestion.TYPE_MULTIPLE_TRUE )
        {
            if ( wrong==0 )
            {
                if ( right==0 )
                {
                    guessadvice=
                        "You didn't select one of statements.  If you didn't know " +
                        "the answer it wouldn't have done any harm to guess one.  The " +
                        "marking scheme doesn't deduct marks if there are none to deduct.";
                    certainadvice=
                        "There are never questions with five false statements.  So if " +
                        "you were certain all statements are false you should have " +
                        "realised you were wrong and guessed one statement.";
                }
                if ( right==1 )
                {
                    if ( available==1 )
                    {
                        guessadvice=
                            "You were lucky to select the true statement and you were wise " +
                            "to avoid guessing more because you might have lost your mark " +
                            "by selecting a false statement. ";
                        certainadvice=
                            "Well done.";
                    }
                    if ( available>1 && available<5 )
                    {
                        guessadvice=
                            "You were lucky to select a true statement and you were wise " +
                            "to avoid guessing more because you might have lost your mark " +
                            "by selecting a false statement. ";
                        certainadvice=
                            "Well done for gaining a mark.  It's just as well " +
                            "you didn't guess at the other statements because " +
                            "you might have lost your well earned mark.";
                    }
                    if ( available==5 )
                    {
                        guessadvice=
                            "On this question you coudn't loose marks because " +
                            "all the statements were true, BUT there is no way " +
                            "to know this in advance so you were right to just " +
                            "guess one statement.";
                        certainadvice=
                            "If you knew in advance that all the statements were " +
                            "true you could have got full marks on guesswork.  It's " +
                            "just as well that you only selected the statement you " +
                            "were sure about though.";
                    }
                }
                if ( right>1 )
                {
                    if ( available==right && available<5 )
                    {
                        guessadvice=
                            "You were very lucky to select the all the true statements " +
                            "but you were running a risk. " +
                            "You didn't know how many true statements there were " +
                            "and with most questions guessing two or more statements " +
                            "will results in a score of zero.";
                        certainadvice=
                            "Well done.";
                    }
                    if ( available>right && available<5 )
                    {
                        guessadvice=
                            "You were very lucky to select more than one true statement " +
                            "but you were running a risk. " +
                            "You didn't know how many true statements there were " +
                            "and with most questions guessing two or more statements " +
                            "will results in a score of zero.";
                        certainadvice=
                            "Well done for gaining marks.  It's just as well " +
                            "you didn't guess at the other statements because " +
                            "you might have lost one or more of your well earned marks.";
                    }
                    if ( available==5 )
                    {
                        guessadvice=
                            "You were very lucky to select more than one true statement " +
                            "but you were running a risk. " +
                            "You didn't know how many true statements there were " +
                            "and with most questions guessing two or more statements " +
                            "will results in a score of zero.";
                        certainadvice=
                            "If you knew in advance that all the statements were " +
                            "true you could have got full marks on guesswork.  It's " +
                            "just as well that you only selected the statements you " +
                            "were sure about though.";
                    }
                }
            }
            else
            {//one or more wrong marks
                if ( right==0 )
                {
                    if ( wrong==1 )
                    {
                        guessadvice=
                            "You were unlucky to gain no marks but it was " +
                            "reasonable to guess.  ";
                        certainadvice=
                            "You should do some study to see where you went wrong.";
                    }
                    if ( wrong>1 )
                    {
                        guessadvice=
                            "You were unwise to guess more than one statement.  Even if one " +
                            "had been true, the false statements cancel " +
                            "the marks you gain.";
                        certainadvice=
                            "You should do some study to see where you went wrong.";
                    }
                }
                if ( right>0 ) // one or more wrong and one or more right
                {
                    if ( wrong>=right )
                    {
                        guessadvice=
                            "If you gained marks on statements you were sure about " +
                            "you lost them by guessing others. ";
                        certainadvice=
                            "You were wrong this time but you shouldn't be afraid to " +
                            "select multiple statements when you are sure about it.";
                    }
                    if ( wrong<right )
                    {
                        guessadvice=
                            "If you gained marks on statements you were sure about " +
                            "you lost them by guessing others. ";
                        certainadvice=
                            "You were partly wrong this time but you shouldn't be afraid to " +
                            "select multiple statements when you are sure about it.";
                    }
                }
            }
        }
        else if ( type == McqQuestion.TYPE_MULTIPLE_TF )
        {
            if ( wrong==0 && right==0 )
            {
                guessadvice=
                    "You didn't select any statements and missed the chance of gaining marks. " +
                    "With this question type marks are never deducted.";
                certainadvice=
                    "Statements are always either true or false and you will never gain " +
                    "a mark by leaving a statement blank." ;
            }
            else
            {
                if ( (wrong+right<5) )
                {
                    guessadvice=
                        "On this type of question you cannot lose marks so you should not have left " +
                        "statements unanswered. ";
                    certainadvice=
                        "Statements are always either true or false and you will never gain " +
                        "a mark by leaving statements blank. " ;
                }
                if ( right <= 2 )
                {
                    guessadvice+=
                        "This time guessing resulted in less than the expected 50% mark but this was " +
                        "just bad luck. Remember in this type of " +
                        "question no deductions are made for wrong answers";
                    certainadvice+=
                        "You are having problems with this topic and should revise it and/or seek help." ;
                }
                else if ( right < 5 )
                {
                    guessadvice+=
                        "This time guessing resulted in more than the expected 50% mark but this was " +
                        "just good luck - you may not do so well another time. Remember in this type of " +
                        "question no deductions are made for wrong answers";
                    certainadvice+=
                        "You should revise the items you got wrong but you did better than a student who just guessed would expect." ;
                }
                else if ( right == 5 )
                {
                    guessadvice+=
                        "This time guessing resulted in full marks more than the expected 50% mark but this was " +
                        "just good luck - you may not do so well another time. Remember in this type of " +
                        "question no deductions are made for wrong answers";
                    certainadvice+=
                        "You should revise the items you got wrong but you did better than a student who just guessed would expect." ;
                }
            }
        }
        else if ( type == McqQuestion.TYPE_MULTIPLE_TFD )
        {
            guessadvice=
                "With this type of question it is not a good idea to guess any statements " +
                "The only way to perform well is to study for the test effectively. ";

            if ( (right-deducted) < 5 )
            {
                certainadvice+=
                    "Your answers were less than perfect and you should study the statements you got " +
                    "wrong or didn't know." ;
            }

            if ( rounding>0 )
            {
                certainadvice+=
                    "Although you made one or more incorrect selections and this affected your mark on the " +
                    "question, this won't affect marks you gained on other questions. With this question type " +
                    "negative marks are always rounded up to zero on each question .";
            }
            
            if ( (right-deducted)==5 )
            {
                certainadvice+=
                    "Well done.";
            }
        }
        else if ( type == McqQuestion.TYPE_STRICT_MULTIPLE_TF )
        {

            if ( (right-deducted)==5 )
            {
                certainadvice+=
                    "Well done.";
                guessadvice=
                    "Your were extremely lucky to get the marks by guessing - that rarely happens with this type of question. ";
            }
            else
            {
                certainadvice+=
                    "If you were certain about your answers you will need to do some further study." ;
                guessadvice=
                    "With this type of question it does no harm to guess but it is very unlikely " +
                    "that you will gain marks that way. ";
            }

        }


        if ( guessadvice.length()>0 && certainadvice.length()>0 )
        {
            out.println( "<h3>Advice on Technique</h3>" );
            out.print( "<p>" );
            out.print( "<strong>Were you confident?</strong> " );
            out.print( certainadvice );
            out.print( "</p><p><strong>Were you guessing?</strong> " );
            out.print( guessadvice );
            out.println( "</p>" );
        }
    }
	
	private void mcqpaper(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq, String insertname )
		throws IOException
		{
		int i;
		
		Vector questions_in_order;
		McqQuestion q;
		Resource resource;
		
		try
			{
			resource = BuildingContext.getContext().getResource();
			if ( insertname.equalsIgnoreCase( "debrief" ) )
				{
				mcqdebrief( req, out, mcq_session, mcq, false );
				return;
				}
			if ( insertname.equalsIgnoreCase( "debrieftech" ) )
				{
				mcqdebrief( req, out, mcq_session, mcq, true );
				return;
				}

			
			questions_in_order = mcq_session.getMcqQuestionsInOrder();
			
			
			if ( insertname.equalsIgnoreCase( "run" ) )
				{
				String can=canrun( req, mcq_session, mcq );
				if ( can!=null )
					{
					out.println( can );
					return;
					}
				out.println( "<FORM METHOD=POST ACTION=bs_template_mcqrecord.html>" );
				}
			
			for ( i=0; i<questions_in_order.size(); i++ )
				{
				q=(McqQuestion)questions_in_order.elementAt( i );
				
				if ( insertname.equalsIgnoreCase( "run" ) )
					{
					out.println( "<HR><TABLE><TR><TD COLSPAN=3><H4>Question " + (i+1) + "</H4></TD></TR>" );
					}
				else
					{
					out.println( "<P></P><TABLE BORDER><TR><TD COLSPAN=2><H4>Question " + 
						(i+1) + " (" + q.getOrdinal() + ")</H4></TD>" );
					out.print( "<TD ALIGN=CENTER><FORM METHOD=POST ACTION=\"" );
					out.print( req.getContextPath() );
					out.print( req.getServletPath() );
					out.print( resource.getFullName()  );
					out.print( q.getMcqQuestionId() + "/bs_template_mcqeditq.html\">" );
					out.println( "<INPUT TYPE=SUBMIT VALUE=\"Edit This Question\"></FORM></TD></TR>" );
					}
				
				questionToHTML( q, null, out, insertname );
				
				out.println( "</TABLE>" );
				}

			if ( i==0 )
				out.println( "<P>There are currently no questions in this test paper.</P>" );
			else
				{
				if ( insertname.equalsIgnoreCase( "run" ) )
					{
					out.println( "<P><INPUT TYPE=SUBMIT VALUE=\"Record Answers Now\"></FORM></P>" );
					}
				}
			}
		catch ( Exception ex )
			{
			out.println( "<HR>A technical problem occurred while trying to fetch marks.<HR>" + ex.getMessage() );
			}
		}


	private void mcqdebrief(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq, boolean technique )
		throws IOException
		{
		String id;
		PrimaryKey resid;
		McqResult mcq_result;
		Hashtable mcq_responses;
		McqResponse mcqresponse;
		McqQuestion q;
		Vector questions_in_order;
		
		
		//must have an id in the parameter part of the URL
		if ( req.getTemplateParameterCount() < 1 )
			{
			out.println( "<HR><I>Improper URL.</I></HR" );
			return;
			}
		id=(String)req.getTemplateParameter( 0 );
		resid= new PrimaryKey( Integer.parseInt(id) );
		
		try
			{
			mcq_result = mcq_session.getMcqResult( resid );
		
			if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE ) )
				{
				if ( !mcq_result.getUserId().equals( req.getUserId() ) || 
						!BuildingContext.getContext().checkPermission( Permission.REVIEW )  )
					{
					out.println( "<P>Sorry, you don't have access to the requested debrief information.</P>" );
					return;
					}
				}
				
			questions_in_order = mcq_session.getMcqQuestionsInOrder();
			
			mcq_responses = mcq_session.getMcqResponses( resid );

			for ( int i=0; i<questions_in_order.size(); i++ )
				{
				q = (McqQuestion)questions_in_order.elementAt( i );
				if ( q==null )
					{
					out.println( "This question wasn't found." );
					continue;
					}
				
				mcqresponse=(McqResponse)mcq_responses.get( q.getMcqQuestionId() );
				if ( mcqresponse == null )
					continue;
					
				out.println( "<HR><H2>Question " + (i+1) + "</H2>" );
				
				questionDebriefToHTML( q, req, out, mcqresponse, technique );
				}
			}
		catch ( Exception ex )
			{
			logException( out, "MCQFacility", "mcqdebrief", 
			    "There was a technical problem retrieving the " +
			             "answers you gave to the questions.",
			    ex );
			}
		
		
		}


	
	private void mcqrecord(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq, String insertname )
		throws IOException, BuildingServerException
		{
		char letter;
		int i, j;
		String param;
		Vector questions_in_order;
		PrimaryKey[] list;
		McqQuestion q;
		int responses[][];
                byte type;
		
                String can_run = canrun(req, mcq_session, mcq );
                if ( can_run != null )
                {
                    out.println( can_run );
                    out.println( "<p><strong>You can't record answers now. If you think this " );
                    out.println( "is in error contact the owner of the MCQ test.</strong></p>" );
                    return;
                }
                
                
		questions_in_order = mcq_session.getMcqQuestionsInOrder();
			
		responses = new int[questions_in_order.size()][];
		list = new PrimaryKey[questions_in_order.size()];
		
		for ( i=0; i<questions_in_order.size(); i++ )
			{
			q=(McqQuestion)questions_in_order.elementAt( i );
			list[i] = q.getMcqQuestionId();
                        type = q.getQuestionType();
		
			responses[i] = new int[5];
			//if not found in form input treat it as not attempted
			for ( j=0; j<5; j++ )
                        {
                                if ( type == McqQuestion.TYPE_MULTIPLE_TFD || 
                                     type == McqQuestion.TYPE_MULTIPLE_TF ||  
                                     type == McqQuestion.TYPE_STRICT_MULTIPLE_TF )
                                    responses[i][j]=McqResponse.RESPONSE_DONT_KNOW;
                                else
                                    responses[i][j]=McqResponse.RESPONSE_FALSE;
                        }
                               
			param=req.getParameter( "Q" + q.getMcqQuestionId() );
			if ( param==null || !param.equalsIgnoreCase( "true" ) )
				continue;
				
                        if ( type == McqQuestion.TYPE_MULTIPLE_TFD || 
                             type == McqQuestion.TYPE_STRICT_MULTIPLE_TF || 
                             type == McqQuestion.TYPE_MULTIPLE_TF )
                        {
                            for (j=0; j<5; j++)
                            {
                                letter=(char)('A'+j);
                                param=req.getParameter("QR" + q.getMcqQuestionId() + letter);
                                if ( param!=null && param.length()>0)
                                {
                                    if ( param.equalsIgnoreCase("TRUE") )
                                        responses[i][j]= McqResponse.RESPONSE_TRUE;
                                    else if ( param.equalsIgnoreCase("FALSE") )
                                        responses[i][j]= McqResponse.RESPONSE_FALSE;
                                }
                            }
                        }
                        else if ( type == McqQuestion.TYPE_ONE_TRUE )
				{
				param=req.getParameter( "QR" + q.getMcqQuestionId() );
				if ( param!=null && param.length()>0)
					{
					for ( j=0; j<5; j++ )
						{
						letter=(char)('A'+j);
						if ( param.charAt( 0 )==letter )
							{
							responses[i][j]=McqResponse.RESPONSE_TRUE;
							break;
							}
						}
					}
				}
			else
				{
				for ( j=0; j<5; j++ )
                                    {
                                    letter=(char)('A'+j);
                                    param=req.getParameter( letter + q.getMcqQuestionId().toString() );
                                    responses[i][j]=  ( param!=null && param.length()>0 )?McqResponse.RESPONSE_TRUE:McqResponse.RESPONSE_FALSE;
                                    }
				}
			
			}
			
			
		McqResult result =  mcq_session.record( list, responses );
		BigDecimal bigmark = result.mark();
		
		if ( mcq.isMarkWitheld() )
			out.println( "<p>You mark has been recorded but the manager of this test has chosen to withold it at this time. " );
		else
			out.println( "<P>Your mark was <B>" + bigmark + "%</B>." );
			
		if ( req.getUserId()==null )
			out.println( "It was recorded anonymously.</P>" );
		else
			{
			User user = (User)BuildingContext.getContext().getUser();
			out.println( "It was recorded under the name " + user.getName() + "</P>" );
			}
			
        Event event = new AssessmentEvent( 
        				AssessmentEvent.EVENT_SUBMISSION,
        				req.getResource().getResourceId(), 
        				req.getUserId(), 
						null,
        				new Integer( result.getMcqResultId().intValue() ), 
        				bigmark  );
        event.save();
		}
	
	private void mcqnewq( Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq, String insertname )
		throws IOException
		{
		try
			{
			mcq_session.createQuestion();
			}
		catch ( Exception ex )
			{
			out.println( "<HR>A technical problem occurred while trying to create a new question.<HR>" + ex.getMessage() );
			}
		}	

	private void mcqitemedit(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq, String insertname )
		throws IOException, BuildingServerException
		{
		String id, field, statement;
		PrimaryKey qid;
		Hashtable questions;
		McqQuestion q;
                byte type;
		
		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 = mcq_session.getMcqQuestions();
		q=(McqQuestion)questions.get( qid );
		type = q.getQuestionType();
                
		if ( q==null )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}

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

		for ( int i=0; i<5; i++ )
			{
			field = "statement_" + (char)('a' + i );
			statement = q.getStatement( i );
			if ( insertname.equalsIgnoreCase( field ) )
				{
				out.print( "<SPAN CLASS='bs-textarea'><TEXTAREA CLASS='text' NAME=" );
				out.print( field );
				out.print( " COLS=30 ROWS=4>" );
				out.print( statement==null?"":statement );
				out.println( "</TEXTAREA></SPAN>" );
				return;
				}
			}

		if ( insertname.equalsIgnoreCase( "explanation" ) )
			{
			out.print( "<SPAN CLASS=bs-textarea><TEXTAREA NAME=explanation COLS=45 ROWS=4>" );
			out.print( q.getExplanation() );
			out.println( "</TEXTAREA></SPAN>" );
			return;
			}
		
		if ( insertname.equalsIgnoreCase( "answer_a" ) )
			{
			out.print( "<INPUT TYPE=CHECKBOX NAME=answer_a VALUE=true " );
			out.println( q.getAnswer(0)?"CHECKED>":">" );
			return;
			}
		if ( insertname.equalsIgnoreCase( "answer_b" ) )
			{
			out.print( "<INPUT TYPE=CHECKBOX NAME=answer_b VALUE=true " );
			out.println( q.getAnswer(1)?"CHECKED>":">" );
			return;
			}
		if ( insertname.equalsIgnoreCase( "answer_c" ) )
			{
			out.print( "<INPUT TYPE=CHECKBOX NAME=answer_c VALUE=true " );
			out.println( q.getAnswer(2)?"CHECKED>":">" );
			return;
			}
		if ( insertname.equalsIgnoreCase( "answer_d" ) )
			{
			out.print( "<INPUT TYPE=CHECKBOX NAME=answer_d VALUE=true " );
			out.println( q.getAnswer(3)?"CHECKED>":">" );
			return;
			}
		if ( insertname.equalsIgnoreCase( "answer_e" ) )
			{
			out.print( "<INPUT TYPE=CHECKBOX NAME=answer_e VALUE=true " );
			out.println( q.getAnswer(4)?"CHECKED>":">" );
			return;
			}
                        
		if ( insertname.equalsIgnoreCase( "type" ) )
			{
                        out.print( "<select name=\"type\">" );
                        
                        out.print( "<option value=\"MCQ\" " );
			if ( type==McqQuestion.TYPE_ONE_TRUE )
                            out.print( " selected=\"selected\" " );
                        out.println( ">Single True MCQ (20%)</option>" );
                        
                        out.print( "<option value=\"MRQ\" " );
			if ( type==McqQuestion.TYPE_MULTIPLE_TRUE )
                            out.print( " selected=\"selected\" " );
                        out.println( ">Multiple True MRQ (20%)</option>" );
                        
                        out.print( "<option value=\"MTF\" " );
			if ( type==McqQuestion.TYPE_MULTIPLE_TF )
                            out.print( " selected=\"selected\" " );
                        out.println( ">Multi True/False (50%-80%)</option>" );
                        
                        out.print( "<option value=\"SMTF\" " );
			if ( type==McqQuestion.TYPE_STRICT_MULTIPLE_TF )
                            out.print( " selected=\"selected\" " );
                        out.println( ">Strict Multi True/False (3%)</option>" );
                        
                        out.print( "<option value=\"MTFD\" " );
			if ( type==McqQuestion.TYPE_MULTIPLE_TFD )
                            out.print( " selected=\"selected\" " );
                        out.println( ">Multi True/False/Don't Know (10%-60%)</option>" );
                        
			out.println( "</select>" );
			return;
			}
		}

	
	private void mcqeditqconfirm(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq, String insertname )
		throws IOException, BuildingServerException
		{
		int n;
		String id, param;
		PrimaryKey qid;
		Hashtable questions;
		McqQuestion 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 = mcq_session.getMcqQuestions();
		q=(McqQuestion)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;
			}

		n=0;
		for ( int i=0; i<5; i++ )
			{
			param=req.getParameter( "answer_" + (char)('a' + i) );
			q.setAnswer( i, param!=null && param.length()>0 );
			if ( q.getAnswer(i) )
				n++;
			}
                
                param=req.getParameter( "type" );
                if (param.equalsIgnoreCase("MCQ"))
                    q.setQuestionType( McqQuestion.TYPE_ONE_TRUE );
                else if (param.equalsIgnoreCase("MRQ"))
                    q.setQuestionType( McqQuestion.TYPE_MULTIPLE_TRUE );
                else if (param.equalsIgnoreCase("MTF"))
                    q.setQuestionType( McqQuestion.TYPE_MULTIPLE_TF );
                else if (param.equalsIgnoreCase("SMTF"))
                    q.setQuestionType( McqQuestion.TYPE_STRICT_MULTIPLE_TF );
                else if (param.equalsIgnoreCase("MTFD"))
                    q.setQuestionType( McqQuestion.TYPE_MULTIPLE_TFD );
                
	
		if ( (param.equalsIgnoreCase("MCQ") || param.equalsIgnoreCase("MRQ")) && n==0 )
			{
			out.println( 
				"<HR>The question wasn't saved because you selected a question type " +
				"that requires at least one true statement.  Backtrack to continue editing." );
			return;
			}
		if ( param.equalsIgnoreCase("MCQ") && n>1 )
			{
			out.println( 
				"<HR>You selected a multiple choice question but you have specified more than one " +
				"true statement, so a multiple response question has been substituted.<HR>" );
                        q.setQuestionType( McqQuestion.TYPE_MULTIPLE_TRUE );
			}
		
		mcq_session.changeQuestionText( q.getMcqQuestionId(), 
										q.getQuestionBigStringId(), 
										req.getParameter( "question" ) );
		for ( int i=0; i<5; i++ )
			mcq_session.changeQuestionText( q.getMcqQuestionId(), 
											q.getStatementBigStringId( i ), 
											req.getParameter( "statement_" + (char)('a' + i) ) );
											
		mcq_session.changeQuestionText( q.getMcqQuestionId(), 
										q.getExplanationBigStringId(), 
										req.getParameter( "explanation" ) );


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

	
	private void mcqmarks( Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq )
		throws IOException, BuildingServerException
		{
		Vector results;
		McqResult result;
		
		results = mcq_session.getResultsOfUser();
		if ( results == null || results.size()<1 )
			{
			out.println( "<P>There is no record of attempts for you on this test.</P>" );
			return;
			}
		
		for ( int i=0; i < results.size(); i++ )
			{
			result=(McqResult)results.elementAt( i );

    		out.print( DateFormatter.formatDate( result.getWhenDone(), DateFormatter.DEFAULT ) );
			out.println( "</H3>" );
			out.println( "<TABLE ALIGN=CENTER WIDTH=\"90%\" CLASS=bs-table-opaque>" );
			out.println( "<TR><TD></TD>" );
			out.println( "<TD><B>Available</B></TD>" );
			out.println( "<TD><B>Gained</B></TD>" );
			out.println( "<TD><B>Deducted</B></TD><TR>" );

			out.println( "<TR><TD><B>Marks:</B></TD>" );
			out.println( "<TD>" + result.getAvailable() + "</TD>" );
			out.println( "<TD>" + result.getRight() + "</TD>" );
			out.println( "<TD>" + result.getDeductions() + "</TD></TR>" );
			out.println( "<TR VALIGN=TOP><TD><B>Percentage:</B></TD>" );
			out.println( "<TD COLSPAN=3 ALIGN=CENTER><H2>" + result.mark() + "%</H2></TD></TR>" );

			out.println( "<TR VALIGN=TOP><TD COLSPAN=4>A question by question " );
			out.print( "<a href=\"" );
			out.print( req.getContextPath() );
			out.print( req.getServletPath() );
			out.print( req.getResource().getFullName() + result.getMcqResultId() );
			out.print( "/bs_template_mcqdebrief.html\">debrief</a> is available. <br />This is also " );
                        out.print( "available <a href=\"" );
			out.print( req.getContextPath() );
			out.print( req.getServletPath() );
			out.print( req.getResource().getFullName() + result.getMcqResultId() );
                        out.print( "/bs_template_mcqdebrieftech.html\">with advice on technique.</a>" );
			out.println( "</TD></TR>" );

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

	private void mcqdeleteq(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq )
		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;
			}

		mcq_session.removeQuestion( qid );
		}

	private String canrun( Request req, McqPaperSession mcq_session, McqPaper mcq )
		throws IOException
		{
		if ( req.getUserId()!=null )
			if ( !BuildingContext.getContext().checkPermission( Permission.EDIT )  )
				{
				if ( mcq.getDeadline()!=null )
					{
					java.util.Date now=new java.util.Date();
					if ( now.after( mcq.getDeadline() ) )
						return "<P>The deadline has passed and you can't run the test</P>";
					}
				if ( mcq.isOnceOnly() )
					if ( onceonlydone( req, mcq_session ) )
						return "<P>You have done the test once and you can't do it again.</P>";
				}
				
		//null indicates that the test can be run
		return null;
		}

	private void mcqrunbutton(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq )
		throws IOException
		{
		String can=canrun( req, mcq_session, mcq );
		
		if ( can!=null )
			out.println( can );
		else	
			out.println( "<INPUT NAME=run TYPE=BUTTON ONCLICK=\"launchtest()\" VALUE=\"Run Test Now\">" );
		
		return;
		}

	
	private void mcqmodify(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq )
		throws IOException, BuildingServerException
		{
		String once, mark_witheld, dead;
		java.util.Date deaddate;
		java.sql.Timestamp sqldeaddate=null;

		once=req.getParameter( "once_only" );
		mark_witheld=req.getParameter( "mark_witheld" );
		dead=req.getParameter( "deadline" );
		if ( dead!=null )
			{
			dead=dead.trim();
			if ( dead.length()>0 )
				{
				deaddate=DateParser.parse( dead );
				if ( deaddate==null )
					{
					out.println( "<HR>The deadline date couldn't be understood.  Please backtrack and review the form.<HR>" );
					return;
					}
				else
					sqldeaddate=new java.sql.Timestamp( deaddate.getTime() );
				}
			}

		mcq_session.setOnceOnly( once!=null && once.length()>0 );
		mcq_session.setMarkWitheld( mark_witheld!=null && mark_witheld.length()>0 );
		mcq_session.setDeadline( sqldeaddate );

		out.println( "<CENTER><P>The requested changes were stored.</P></CENTER>" );
		}

	
	private void mcqanalysis(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq )
		throws IOException, BuildingServerException
		{
		McqAnalysis mcq_analysis=null;
		
		int i=0, j, q, n=0;
		double qmark, mark, sum=0.0, sumsq=0.0;
		boolean filter1, filter2, filter3, itemanalysis, questionanalysis,
			format_print, format_table, format_detail, alt_correlation;
		StringBuffer sql = new StringBuffer();
		String sid, before, after, format, analysis, previd;
		java.util.Date beforedate, afterdate;
		java.sql.Timestamp sqlbeforedate=null, sqlafterdate=null;
		Calendar calendar = Calendar.getInstance();
		Vector sidlist=null;
		Vector altmarklist=null;
		Group allstudents=null;
		PrimaryKey student;
		Vector results;
		McqResult result;

		try
			{
			allstudents = Group.findGroupByName("allstudents");
			}
		catch ( BuildingServerException bsex )
			{
			allstudents=null;
			}

		filter1=req.getParameter( "filter1" )!=null;

		if ( filter1 && allstudents==null )
			{
			out.println( "Unable to find the 'allstudents' group." );
			return;
			}
		
		filter2=req.getParameter( "filter2" )!=null;
		filter3=req.getParameter( "filter3" )!=null;
		
        boolean includePassPhrase = req.getParameter( "include_oxford" ) != null;
        boolean includeWebAuth = req.getParameter( "include_external" ) != null;
        
		sid=req.getParameter( "sid" );
		before=req.getParameter( "before" );
		after=req.getParameter( "after" );
		format=req.getParameter( "format" );
		format_print = format!=null && !format.equals("a");
		format_table  = format==null || format.equals( "b" ) || format.equals( "d" );
		format_detail = format==null || format.equals( "d" ) || format.equals( "e" );
		analysis=req.getParameter( "analysis" );
		questionanalysis= ( analysis!=null && (analysis.equals( "c" ) || analysis.equals( "d" )) );
		itemanalysis= ( analysis!=null && analysis.equals( "d" ) );
		alt_correlation=req.getParameter( "alt_correlation" )!=null;
		
		if ( before!=null )
			{
			before=before.trim();
			if ( before.length()>0 )
				{
				beforedate=DateParser.parse( before );
				if ( beforedate==null )
					{
					out.println( "<HR>The first date couldn't be understood.  Please backtrack and review the form.<HR>" );
					return;
					}
				else
					sqlbeforedate=new java.sql.Timestamp( beforedate.getTime() );
			    calendar.setTime( beforedate );
			    if ( calendar.get( Calendar.YEAR ) < 1000 )
					out.println( "<HR>The first date had insufficient digits in the year.  Please use the full date.<HR>" );
			        
				}
			}
		if ( after!=null )
			{
			after=after.trim();
			if ( after.length()>0 )
				{
				afterdate=DateParser.parse( after );
				if ( afterdate==null )
					{
					out.println( "<HR>The second date couldn't be understood.  Please backtrack and review the form.<HR>" );
					return;
					}
				else
					sqlafterdate=new java.sql.Timestamp( afterdate.getTime() );
			    calendar.setTime( afterdate );
			    if ( calendar.get( Calendar.YEAR ) < 1000 )
					out.println( "<HR>The second date had insufficient digits in the year.  Please use the full date.<HR>" );
				}
			}


		if ( sid!=null )
			{
			String tmp, straltmark;
			Double altmark;
			StringTokenizer tok=new StringTokenizer( sid, "\n" );
			StringTokenizer linetok;
			sidlist=new Vector();
			altmarklist=new Vector();
			log.debug( "Check sids" );
			for ( int line=1; tok.hasMoreTokens(); line++ )
				{
				tmp=tok.nextToken();
				tmp=tmp.trim();
				if ( tmp.length()==0 ) continue;

				linetok = new StringTokenizer( tmp );
				if ( linetok.hasMoreTokens() )
					{
					try
						{
						student=new PrimaryKey( Integer.parseInt( linetok.nextToken() ) );
						}
					catch ( NumberFormatException nfex )
						{
						out.println( "<P><I>Problem: invalid ID in form input (line " + line + ").</I></P>" );
						return;
						}

					sidlist.addElement( student );
					altmark=null;
					if ( alt_correlation )
						{
						straltmark=linetok.nextToken();
						try 
							{altmark = new Double( straltmark );}
						catch ( NumberFormatException nfex )
							{
							out.println( "<P><I>Problem: invalid number in form input (line " + line + ").</I></P>" );
							return;
							}
						altmarklist.addElement( altmark );
						}
					}
				}
			if ( sidlist.size()==0 )
				{
				sidlist=null;
				altmarklist = null;
				}
			}



		sql.append( "((resource_id = " );
		sql.append( req.getResource().getResourceId().toString() );
		sql.append( ") " );
		if ( filter1 )
			{
			sql.append( " AND user_id IN (SELECT user_id FROM users WHERE " );
			sql.append( allstudents.whereMembers() );
			sql.append( " ) " );
			}
		if ( filter2 )
			sql.append( " AND ((wrong + correct) > 0) " );
		if ( sidlist!=null )
			{
			sql.append( " AND (user_id IN ( " );
			for( i=0; i< sidlist.size(); i++ )
				{
				if ( i>0 )
					sql.append( ", " );
				sql.append( sidlist.elementAt( i ).toString() );
				}
			sql.append( " )) " );
			}
		if ( sqlbeforedate!=null )
			{
			sql.append( " AND (when_done > {ts '" );
			sql.append( sqlbeforedate.toString() );
			sql.append( "'}) " );
			}
		if ( sqlafterdate!=null )
			{
			sql.append( " AND (when_done < {ts '" );
			sql.append( sqlafterdate.toString() );
			sql.append( "'}) " );
			}
		sql.append( " ) " );

		if ( analysis !=null && analysis.length()>0 )
			{
			if ( itemanalysis )
				mcq_analysis = new McqAnalysis( McqAnalysis.SUMMARY_QUESTION_AND_ITEM, true );
			else
				{
				if ( questionanalysis )
					mcq_analysis = new McqAnalysis( McqAnalysis.SUMMARY_AND_QUESTION, true );
				else
					mcq_analysis = new McqAnalysis( McqAnalysis.SUMMARY, true );
				}
			}
			
		results = mcq_session.analyse( sql.toString(), filter3, sidlist, altmarklist, mcq_analysis );
	
		if ( format_print )
			{
			Hashtable users = mcq_session.getMcqUsers();
			User user;
			for ( i=0; i< results.size(); i++ )
				{
				result = (McqResult)results.elementAt( i );
				user = (User)users.get( result.getUserId() );
				if ( i==0 )
					{
					if ( format_table )
						{
						out.print( "<TABLE BORDER><TR><TD>Name</TD>" +
									 "<TD>WebLearn User ID</TD>");
                        if (includePassPhrase)
                            out.print( "<TD>External Username</TD>" );
                        if (includeWebAuth)
                            out.print( "<TD>Oxford Username</TD>");
                        out.print( "<TD>Mark</TD>" );
						if ( format_detail )
							out.print( "<TD>Right</TD><TD>Wrong</TD>" +
										 "<TD>Deductions</TD><TD>Date</TD>" );
						out.println( "</TR>" );
						}
					else
						{
						out.println( "<FORM><SPAN CLASS=bs-textarea><TEXTAREA COLS=50 ROWS=10>" );
						out.print( "Name\tWebLearn User ID" );
                        if (includePassPhrase)
                            out.print( "\tExternal Username" );
                        if (includeWebAuth)
                            out.print( "\tOxford Username");
                        out.print("\tMark");
						if ( format_detail )
							out.print( "\tRight\tWrong\tDeductions\tDate" );
						out.print( "\n" );
						}
					}
				if ( format_table )
                	out.print( "<TR><TD><A HREF=\"" + result.getMcqResultId() + "/bs_template_mcqdebrief.html\">" );
                	
                
                	
                //out.print( result.name==null?"<I>unidentified</I>":McqResult.name );
                if ( user == null || user.getName() == null )
                	out.print( "<I>name unknown</I>" );
                else
                	out.print( user.getName() );
                
                
                out.print( format_table?"</A></TD><TD>":"\t" );
                out.print( result.getUserId().toString() );
                out.print( format_table?"</TD><TD>":"\t" );
                if ( includePassPhrase )
                    {
                    PassPhrase passPhrase = PassPhrase.findPassPhrase( user );
                    out.print( (passPhrase!= null && passPhrase.getUserName() != null)?passPhrase.getUserName():"" );
                    out.print( format_table?"</TD><TD>":"\t" );
                    }
                if ( includeWebAuth )
                    {
                    WebAuthUser webAuthUser = WebAuthUser.findWebAuthUser( user );
                    out.print( (webAuthUser!= null && webAuthUser.getUserName() != null)?webAuthUser.getUserName():"" );
                    out.print( format_table?"</TD><TD>":"\t" );
                    }
                out.print( " " + result.mark() + "%");
                if ( format_detail )
                	{
                	out.print( format_table?"</TD><TD>":"\t" );
                	out.print( result.getRight() );
                	out.print( format_table?"</TD><TD>":"\t" );
                	out.print( result.getWrong() );
                	out.print( format_table?"</TD><TD>":"\t" );
                	out.print( result.getDeductions() );
                	out.print( format_table?"</TD><TD>":"\t" );
                	out.print( DateFormatter.formatDate( result.getWhenDone(), DateFormatter.MEDIUM ) );
                	}
                out.println( format_table?"</TD></TR>":"" );
				}
			if ( i==0 )
				out.println( "<H4>There were no records found.</H4>" );
			else
				out.println( format_table?"</TABLE>":"</TEXTAREA></SPAN></FORM>" );
			}

		if ( mcq_analysis !=null )
			{
			mcq_analysis.dumpAnalysis( new PrintWriter( out ) );
			out.flush();
			
			if ( mcq_analysis.getDepth() >= McqAnalysis.SUMMARY_AND_QUESTION )
				{
				Vector questions = mcq_session.getMcqQuestionsInOrder();
				McqQuestion question;
				McqQuestionAnalysis q_analysis;
				for ( i=0; i<questions.size(); i++ )
					{
					question = (McqQuestion)questions.elementAt( i );
					q_analysis = mcq_analysis.getMcqQuestionAnalysis( question.getMcqQuestionId() );
					out.println( "<table><tr><td colspan=3><h2>Question " );
					out.println( (i+1) );
					out.println( "</h2></td></tr>" );
					this.questionToHTML( question, q_analysis, out, "analysis" );
					out.println( "</table><p><br /><br /></p>" );
					}
				}
			
			}
		
		return;
		}

	
	private void mcqfield(  Request req, PrintWriter out, McqPaperSession mcq_session, McqPaper mcq, String name )
		throws IOException
		{
		if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE ) )
			{
			out.println( "<PRE>You don't have permission to manage this mcq test.</PRE>\n" );
			return;
			}

		if ( name.equalsIgnoreCase( "once_only" ) )
			{
			out.print( "<INPUT TYPE=CHECKBOX NAME=once_only " );
			if ( mcq.isOnceOnly() )
				out.print( "CHECKED" );
			out.println( ">" );
			return;
			}

		if ( name.equalsIgnoreCase( "mark_witheld" ) )
			{
			out.print( "<INPUT TYPE=CHECKBOX NAME=mark_witheld " );
			if ( mcq.isMarkWitheld() )
				out.print( "CHECKED" );
			out.println( ">" );
			return;
			}

		if ( name.equalsIgnoreCase( "deadline" ) )
			{
			out.print( "<INPUT CLASS=\"datetime\" NAME=deadline VALUE=\"" );
			if ( mcq.getDeadline()!=null ) out.print( DateFormatter.formatDate( mcq.getDeadline(), DateFormatter.MEDIUM ) );
			out.println( "\">" );
			return;
			}

		return;
		}
	
	private boolean onceonlydone( Request req, McqPaperSession mcq_session )
		{
		try
			{
			Vector results = mcq_session.getResultsOfUser();
		
			return results.size() > 0;
			}
		catch ( Exception ex )
			{
			return false;
			}
		}

	private void exportXml(  Request req, PrintWriter out, McqPaperSession mcq_session )
		throws IOException
		{
		try
		    {
		    mcq_session.exportXml( true, true );
    		out.println( "<P>The XML file has been created and is available for <A HREF=qtiexp.xml>download</A> now.</P>" );
    		out.println( "<P>(Left click to view in browser, right click to save to disk.) " );
		    }
		catch ( BuildingServerException bsex )
		    {
    		out.println( "<PRE>" );
    		out.println( bsex.getMessage() );
    		out.println( "</PRE>" );
		    }
		
		
		return;
		}

	private void importq(  Request req, PrintWriter out, McqPaperSession mcq_session )
		throws IOException
		{
		String file = req.getParameterFileLocation( "file" );
		if ( file == null || file.length() == 0 )
		    {
		    out.println( "<P>Import failed: no file was supplied.</P>" );
		    return;
		    }

		try
		    {
		    mcq_session.importXmlQuestions( file );
    		out.println( "DONE" );
		    }
		catch ( BuildingServerException bsex )
		    {
    		out.println( "<PRE>" );
    		out.println( bsex.getMessage() );
    		out.println( "</PRE>" );
		    }
		
		
		return;
		}

	}


