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

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

import org.bodington.database.PrimaryKey;
import org.bodington.server.*;

import java.sql.*;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;
import java.math.BigDecimal;
import java.math.BigInteger;

import org.bodington.server.*;
import org.bodington.assessment.*;
import org.bodington.server.realm.User;
import org.bodington.server.events.*;
import org.bodington.database.*;
import org.bodington.text.BigString;

public class TextQSessionImpl
	extends org.bodington.server.SingleResourceSession 
	implements TextQSession 
	{

	int max_ordinal=0;
	
	public TextQSessionImpl()
		{
		super();
		}

	public TextQPaper getTextQPaper() 
		throws BuildingServerException
		{
		TextQPaper paper = TextQPaper.findTextQPaper( getResource().getResourceId() );
		return paper;
		}
	
	public void setMaximumMark( int m )
		throws BuildingServerException
		{
		TextQPaper paper = TextQPaper.findTextQPaper( getResource().getResourceId() );
		if ( paper == null )
			throw new BuildingServerException( "Can't find paper." );
		paper.setMaximumMark( m );
		paper.save();
		}
	
	public void setGeneralAssessment( int available, BigDecimal weight, String notes )
		throws BuildingServerException
		{
		TextQPaper paper = TextQPaper.findTextQPaper( getResource().getResourceId() );
		if ( paper == null )
			throw new BuildingServerException( "Can't find paper." );
		paper.setGeneralAvailable( available );
		paper.setGeneralWeight( weight );
		paper.setGeneralNotes( notes );
		paper.save();
		}

	
	public void setDates( Timestamp open, Timestamp deadline, Timestamp penalty )
		throws BuildingServerException
		{
		TextQPaper paper = TextQPaper.findTextQPaper( getResource().getResourceId() );
		if ( paper == null )
			throw new BuildingServerException( "Can't find paper." );
		paper.setDateOpen( open );
		paper.setDateDeadline( deadline );
		paper.setDatePenalty( penalty );
		paper.save();
		}
	
	public Vector getTextQuestionIds()
		throws BuildingServerException
		{
		Vector list = new Vector();
		
		TextQuestion question;
		Enumeration enumeration = TextQuestion.findTextQuestions( "resource_id = " + getResource().getResourceId().toString(), "ordinal" );
		while ( enumeration.hasMoreElements() )
			{
			question = (TextQuestion)enumeration.nextElement();
			list.addElement( question.getTextQuestionId() ); 
			if ( question.getOrdinal() > max_ordinal )
				max_ordinal = question.getOrdinal();
			}
		
		return list;
		}

	
	public Hashtable getTextQuestions()
		throws BuildingServerException
		{
		Hashtable table = new Hashtable();
		
		TextQuestion question;
		Enumeration enumeration = TextQuestion.findTextQuestions( "resource_id = " + getResource().getResourceId().toString() );
		while ( enumeration.hasMoreElements() )
			{
			question = (TextQuestion)enumeration.nextElement();
			table.put( question.getTextQuestionId(), question );
			if ( question.getOrdinal() > max_ordinal )
				max_ordinal = question.getOrdinal();
			}
		
		return table;
		}
	
	public Vector getTextQuestionsInOrder()
		throws BuildingServerException
		{
		Vector list = new Vector();
		
		TextQuestion question;
		Enumeration enumeration = TextQuestion.findTextQuestions( "resource_id = " + getResource().getResourceId().toString(), "ordinal" );
		while ( enumeration.hasMoreElements() )
			{
			question = (TextQuestion)enumeration.nextElement();
			list.addElement( question ); 
			if ( question.getOrdinal() > max_ordinal )
				max_ordinal = question.getOrdinal();
			}
		
		return list;
		}
	
	public Hashtable getStrings()
		throws BuildingServerException
		{
		return null;
		}

	public TextQuestion createQuestion()
		throws BuildingServerException
		{
		TextQuestion question = new TextQuestion();
		
		question.setResourceId( getResource().getResourceId() );
		question.setQuestion( "The question goes here." );
		question.setNotes( "Notes for marker here." );
		question.setExplanation( "Explanation for model answer." );
		question.setOrdinal( max_ordinal+100 );
		question.setAvailable( 1 );
		question.setWeight( new BigDecimal( "1.0" ) );
		question.save();
		
		return question;
		}
	
	public void removeQuestion( PrimaryKey id )
		throws BuildingServerException
		{
		TextQuestion question =  TextQuestion.findTextQuestion( id );
		if ( question == null )
			throw new BuildingServerException( "Question not found." );

		if ( !question.getResourceId().equals( getResource().getResourceId() ) )
			throw new BuildingServerException( "Question doesn't belong in this paper." );

		question.delete();
		}
	
	public void changeQuestion( PrimaryKey id, TextQuestion model )
		throws BuildingServerException
		{
		TextQuestion question =  TextQuestion.findTextQuestion( id );
		if ( question == null )
			throw new BuildingServerException( "Question not found." );

		if ( !question.getResourceId().equals( getResource().getResourceId() ) )
			throw new BuildingServerException( "Question doesn't belong in this MCQ paper." );

		question.setOrdinal( model.getOrdinal() );
		question.setAvailable( model.getAvailable() );
		question.setWeight( model.getWeight() );
		question.save();

		if ( question.getOrdinal() > max_ordinal )
			max_ordinal = question.getOrdinal();
		}
	
	public void changeQuestionText( PrimaryKey qid, PrimaryKey tid, String new_text )
		throws BuildingServerException
		{
		TextQuestion question =  TextQuestion.findTextQuestion( qid );
		if ( question == null )
			throw new BuildingServerException( "Question not found." );
			
		if ( !question.getResourceId().equals( getResource().getResourceId() ) )
			throw new BuildingServerException( "Question doesn't belong in this paper." );

		boolean found = false;
		found = tid.equals( question.getQuestionBigStringId() );
		found = found || tid.equals( question.getExplanationBigStringId() );
		found = found || tid.equals( question.getNotesBigStringId() );

		if ( !found )
			throw new BuildingServerException( "Can't identify qhich part of question to edit." );

		BigString big = BigString.findBigString( tid );
		big.setString( new_text );
		big.save();
		}

	
	public boolean canSubmit() throws BuildingServerException
		{
		return canSubmit( null, new StringBuffer() );
		}
		
	private boolean canSubmit( PrimaryKey uid, StringBuffer message ) throws BuildingServerException
		{
		TextQPaper paper = getTextQPaper();
		TextQResult entry;
		if ( uid==null )
			entry = getTextQResult();
		else
			entry = getTextQResult( uid );
		
		if ( entry == null )
			{
			entry = new TextQResult();
			entry.setStatus( TextQResult.STATUS_UPLOAD_BY_DATE );
			}
			
		java.util.Date now					=new java.util.Date();
		java.util.Date now_minus_24_hours	=new java.util.Date( now.getTime()-(24L*60L*60L*1000L) );
		
		switch ( entry.getStatus() )
			{
			case TextQResult.STATUS_UPLOAD_BY_DATE:
				if ( paper.getDateOpen() !=null && paper.getDateOpen().after( now ) )
					{
					message.append( " the short answer paper is not yet available for use " );
					return false;
					}
				
				if ( paper.getDateDeadline() == null )
					return true;
				if ( paper.getDateDeadline().after( now ) )
					return true;
				if ( paper.getDatePenalty() == null )
					{
					message.append( " the deadline for uploading to this short answer paper has passed and uploading files after the deadline is not allowed " );
					return false;
					}
				if ( paper.getDatePenalty().before( now ) )
					{
					message.append( " the penalty period deadline for uploading to this short answer paper has passed " );
					return false;
					}
				// at this point we know that "now" lies between deadline and penalty
				// allowing them to upload depends on whether and when they previously
				// uploaded a file.

				// if never uploaded it is OK to do so between deadline and penalty date
				if ( entry.getWhenSaved() == null )
					return true;

				// if user uploaded before deadline they can't change it
				if ( entry.getWhenSaved().before( paper.getDateDeadline() ) )
					{
					message.append( " a file was uploaded before the deadline and one can't change a file now the deadline has passed " );
					return false;
					}

				// if user uploaded during the penalty period but more than
				// 24 hours ago they can't change it.
				if ( entry.getWhenSaved().before( now_minus_24_hours ) )
					{
					message.append( " a file was uploaded during the penalty period but more than 24 hours ago " );
					return false;
					}
						
				// if we reached here then the user must have uploaded within the
				// last 24 hours and they can replace their work now and get another
				// 24 hours.
				return true;


			case TextQResult.STATUS_UPLOAD_CHANGEABLE:
				return true;

			case TextQResult.STATUS_UPLOAD_LOCKED:
			case TextQResult.STATUS_UPLOAD_PROCESSED:
			case TextQResult.STATUS_UPLOAD_MARKED:
			case TextQResult.STATUS_UPLOAD_RETURNED:
				message.append( " the manager of this short answer paper has locked your file " );
				return false;			

			default:
				message.append( " a technical problem has been detected " );
				return false;			
			}
		}
		
	public boolean canSubmit( PrimaryKey user_id ) throws BuildingServerException
		{
		return canSubmit( user_id, new StringBuffer() );
		}
	
	public boolean canSeeMark() throws BuildingServerException
		{
		return canSeeMark( null );
		}
		
	public boolean canSeeMark( PrimaryKey uid ) throws BuildingServerException
		{
		TextQPaper paper = getTextQPaper();
		TextQResult entry;
		if ( uid==null )
			entry = getTextQResult();
		else
			entry = getTextQResult( uid );
		
		if ( entry == null )
			return false;
			
		return entry.getStatus() == TextQResult.STATUS_UPLOAD_RETURNED;
		}
		
	
	public String denySubmitMessage() throws BuildingServerException
		{
		StringBuffer message = new StringBuffer();
		canSubmit( null, message );
		return message.toString();
		}
		
	public String denySubmitMessage( PrimaryKey user_id ) throws BuildingServerException
		{
		StringBuffer message = new StringBuffer();
		canSubmit( user_id, message );
		return message.toString();
		}
	
	public TextQResult record( PrimaryKey[] text_question_ids, String[] responses )
		throws BuildingServerException
		{
		// forces uppload to sub directory named after user id
		// only allows one file per user.  (Fixes file name?)
		User user = (User)BuildingContext.getContext().getUser();
		PrimaryKey uid = user.getUserId();
		TextQResult entry;
		TextQResponse response;
		Hashtable response_table;

		if ( !canSubmit() )
			throw new BuildingServerException( "You are not allowed to submite answers at this time." );
			
		entry = getTextQResult();
		if ( entry == null )
			{
			entry = new TextQResult();
			entry.setResourceId( getResource().getResourceId() );
			entry.setUserId( uid );
			entry.setFeedback( "" );
			entry.setComments( "" );
			}

		entry.setWhenSaved( new Timestamp( System.currentTimeMillis() ) );
		entry.save();
		
		response_table = this.getTextQResponses( entry.getTextQResultId() );
		
		for ( int i=0; i< text_question_ids.length; i++ )
			{
			response = (TextQResponse)response_table.get( text_question_ids[i] );
			if ( response == null )
				{
				response= new TextQResponse();
				response.setTextQResultId( entry.getTextQResultId() );
				response.setTextQuestionId( text_question_ids[i] );
				response.setFeedback( "" );
				}
			response.setResponse( responses[i] );
			response.save();
			}
		
		return entry;
		}
		
	public TextQResult recordStatus( PrimaryKey entry_id, int status )
		throws BuildingServerException
		{
		TextQResult entry = TextQResult.findTextQResult( entry_id );
		if ( entry==null )
			throw new BuildingServerException( "Can't find entry." );
			
		entry.setStatus( status );
		entry.save();
		
		return entry;
		}
		
		
	public TextQResult recordMarks( PrimaryKey entry_id, PrimaryKey[] text_question_ids, int[] marks, String[] feedback, String general_feedback, String comments )
		throws BuildingServerException
		{
		int i;
		TextQPaper tq = getTextQPaper();
		Hashtable responses = getTextQResponses( entry_id );
		TextQResult entry = TextQResult.findTextQResult( entry_id );
		TextQResponse response;
		Hashtable questions = getTextQuestions();
		TextQuestion question;
		
		double total_mark=0.0;
		double total_weight=0.0;
		boolean incomplete=false;
		
		for ( i=0; i<text_question_ids.length; i++ )
			{
			question = (TextQuestion)questions.get( text_question_ids[i] );
			response = (TextQResponse)responses.get( text_question_ids[i] );
			if ( response == null )
				{
				response = new TextQResponse();
				response.setTextQResultId( entry_id );
				response.setTextQuestionId( text_question_ids[i] );
				}
			if ( marks[i]<0 )
				{
				response.setMark( null );
				incomplete=true;
				}
			else
				{
				response.setMark( new Integer( marks[i] ) );
				total_mark += question.getWeight().doubleValue() * (double)marks[i] / (double)question.getAvailable();
				total_weight += question.getWeight().doubleValue();
				}
			
			if ( feedback[i] == null )
				response.setFeedback("");
			else
				response.setFeedback( feedback[i].trim() );
				
			response.save();
			}
		
		if ( tq.getGeneralAvailable()>0 )
			{
			if ( marks[i]<0 )
				{
				entry.setGeneralMark( null );
				incomplete=true;
				}
			else
				{
				entry.setGeneralMark( new Integer( marks[i] ) );
				total_mark += tq.getGeneralWeight().doubleValue() * (double)marks[i] / (double)tq.getGeneralAvailable();
				total_weight += tq.getGeneralWeight().doubleValue();
				}
			}
		
		if ( incomplete )
			entry.setMark( null );
		else
			entry.setMark( new Integer( (int)Math.round((double)tq.getMaximumMark() * total_mark/total_weight )) );
		
		entry.setStatus( TextQResult.STATUS_UPLOAD_PROCESSED );
		entry.setWhenMarked( new Timestamp( System.currentTimeMillis() ) );
		User user = (User)BuildingContext.getContext().getUser();
		entry.setMarkerUserId( user.getUserId() );
		entry.setFeedback( general_feedback );
		entry.setComments( comments );
		entry.save();
		
		return entry;
		}

	
	
	public TextQResult recordAdjustedMark( PrimaryKey entry_id, int adjusted_mark, String general_feedback, String comments )
		throws BuildingServerException
		{
		TextQPaper tq = getTextQPaper();
		TextQResult entry = TextQResult.findTextQResult( entry_id );
		
		entry.setAdjustedMark( new Integer( adjusted_mark ) );
		entry.setFeedback( general_feedback );
		entry.setComments( comments );
		entry.save();
		
		return entry;
		}
	
	public TextQResult getTextQResult()
		throws BuildingServerException
		{
		User user = (User)BuildingContext.getContext().getUser();
		return getTextQResult( user.getUserId() );
		}
		
	public TextQResult getTextQResult( PrimaryKey uid )
		throws BuildingServerException
		{
		TextQResult entry = 
			TextQResult.findTextQResult( 
				"resource_id = " + getResource().getResourceId() + " AND user_id = " + uid );
		
		return entry;
		}
		
	public Hashtable getTextQResults() throws BuildingServerException
		{
		Hashtable table = new Hashtable();
		TextQResult entry;
		
		Enumeration enumeration= 
			TextQResult.findTextQResults( "resource_id = " + getResource().getResourceId() );
		
		while ( enumeration.hasMoreElements() )
			{
			entry = (TextQResult)enumeration.nextElement();
			table.put( entry.getUserId(), entry );
			}

		return table;
		}
		
		
	public Vector getTextQUsers() throws BuildingServerException
		{
		Vector users= new Vector();
		
		Enumeration enumeration = 
			User.findUsers(
				"user_id IN (SELECT user_id FROM textq_results WHERE resource_id = " +
				getResource().getResourceId() +
				")",
				"surname, initials" );
		
		while ( enumeration.hasMoreElements() )
			{
			users.addElement( enumeration.nextElement() );
			}
		
		return users;
		}


	public Hashtable getTextQResponses( PrimaryKey entryid )
		throws BuildingServerException
		{
		Hashtable table = new Hashtable();
		TextQResponse response;
		
		Enumeration enumeration = TextQResponse.findTextQResponses( "textq_result_id = " + entryid );
		while ( enumeration.hasMoreElements() )
			{
			response = (TextQResponse)enumeration.nextElement();
			table.put( response.getTextQuestionId(), response );
			}
		
		return table;
		}
	
	// returns enumeration of results found and if analysis is non null it is filled in with
	// statistical analysis.
	public Enumeration analyse( String where_clause, TextQAnalysis analysis )
		throws BuildingServerException
		{
		return null;
		}
	
	
	}
	
