
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;

import com.crowdlang.exceptions.CrowdLangException;
import com.crowdlang.messages.Answer;
import com.crowdlang.messages.AnswerSet;
import com.crowdlang.messages.CrowdMarketEnum;
import com.crowdlang.messages.FreeTextQuestion;
import com.crowdlang.messages.MturkGeneralComponents;
import com.crowdlang.messages.MturkQuestionSetType;
import com.crowdlang.messages.Question;
import com.crowdlang.messages.QuestionSet;
import com.crowdlang.messages.SelectionIdentifier;
import com.crowdlang.messages.SelectionQuestion;
import com.crowdlang.messages.SelectionStyleSuggestion;
import com.mturk.MTurkConnection;
import com.mturk.MTurkReceiver;
import com.mturk.MTurkSender;


public class NucleiCount {
	
	private static String questionID1 = "q1";
	private static String questionID2 = "q2";
	
	public static void main(String[] args) throws CrowdLangException, InterruptedException{
		
		System.out.println("Start");
		
		//Create a Connection to the Amazon's Mechanical Turk Market Place
		//true => sends to the sandbox environment for testing
		//false => sends to the real market place
		boolean sandbox = true;
				
		//TODO: insert your AWS keys here
		//String accessKeyID = "";
		//String secretAccessKey = "";
		String accessKeyID = "";
		String secretAccessKey = "";
		
		MTurkConnection connection = MTurkConnection.prepareMTurkConnection(accessKeyID, secretAccessKey, sandbox);
		System.out.println("Connection available");
		
		//The creation of a QuestionSetType (This is the container which
		//can be reused for submitting HIT's to the same place
		//for Step 1
		long assignmentDurationInSeconds = 60 * 5;
		String containerName = "Nuclei count";
		String desc = "Count the number of nuclei in an image";
		String keywords = "";
		long autoApprovalInSeconds = 60 *60* 5; // 5 h
		
		//TODO: define reward
		double reward = 0.01;
		MturkQuestionSetType type = new MturkQuestionSetType(containerName, desc, keywords, reward, assignmentDurationInSeconds, autoApprovalInSeconds, null);
		connection.registerHITType(type);
		if(sandbox)	System.out.println("Link to the group: https://workersandbox.mturk.com/mturk/preview?groupId=" + type.getQuestionTypeID());
		
		//The creation of a QuestionSetType (This is the container which
		//can be reused for submitting HIT's to the same place
		//for Step 2
		//TODO: define reward
		reward = 0.01;
		MturkQuestionSetType type2 = new MturkQuestionSetType(containerName, desc, keywords, reward, assignmentDurationInSeconds, autoApprovalInSeconds, null);
		connection.registerHITType(type2);
			
		MTurkSender sender = connection.generateSender();
		MTurkReceiver receiver = connection.generateReceiver();

		/**
		 * Step 1 Counting the number of nuclei
		 */
		//TODO: decide on how many workers you want to ask to count the nuclei
		int numAssignments = 1;
		QuestionSet qs = generateQuestion(type, numAssignments);
		qs.setHitID(sender.sendQuestion(qs).getHITId());
		System.out.println("Hit-ID: " + qs.getHitID());
		
		boolean done = false;
		AnswerSet answerSet = null;
		
		while(!done){
			Thread.sleep(10000l);
			done = receiver.isCompleted(qs);
			System.out.println("State: " + qs.getHitID() + ";" + done);
			System.out.println("Fraction solved: "+receiver.getPercentageSolved(qs));

			//If all assignments of a task are solved
			if(done){
				//Receive the answers for the HIT 
				answerSet = receiver.checkForAnswers(qs);			
			}
		}

	
		
		/** TODO
		 * 	implement a rule to accept or reject the submissions
		 *	e.g. negative values -> reject
		 *	or, less than 5 seconds used -> reject
		 *   or based on average working time +/- upper / lower boundaries -> reject
		 */
		Vector<Answer> acceptedAnswers = new Vector<Answer>();
		
		for (Answer answer : answerSet.getSolutionsForQuestion(questionID1)) {
			if(true) {			
				acceptedAnswers.add(answer);
				receiver.approveAssignment(answer.getAssignmentID(), "Thank you.");
			}
			else {
				receiver.rejectAssignment(answer.getAssignmentID(), "Sorry.");
			}
		}

		
		/**
		 * Step 2 Validating the answers from Step 1
		 */
		ArrayList<QuestionSet> tasksInTwo = new ArrayList<QuestionSet>();
		//links the answer of step 1 to the generated question in step 2
		HashMap<QuestionSet, Answer> answerQuestionMapping = new HashMap<QuestionSet, Answer>();
		
		
		//TODO: decide on how many workers you want to ask to count the nuclei
		numAssignments = 1;
		for (Answer answer : acceptedAnswers) {
			QuestionSet q2 = generateQuestion2(type2, numAssignments, answer.getAnswerValue());	
			q2.setHitID(sender.sendQuestion(q2).getHITId());
			tasksInTwo.add(q2);
			answerQuestionMapping.put(q2, answer);
		}
		if (sandbox) System.out.println("Link to the group: https://workersandbox.mturk.com/mturk/preview?groupId=" + type2.getQuestionTypeID());
		
		int estimate = 0;
		int count = 0;

		//we store which HIT has already been solved here
		final int numHIT = tasksInTwo.size();
		boolean finished[] = new boolean[numHIT];

		int numCompleted = 0;
		done = false;
		
		while(numHIT > numCompleted){
			Thread.sleep(10000l);


			for (int i=0; i< numHIT; i++) {
				QuestionSet questionSet = tasksInTwo.get(i);
				done = receiver.isCompleted(questionSet);
				System.out.println("State: " + questionSet.getHitID() + ";" + done);
				if(!done) System.out.println("Fraction solved: "+receiver.getPercentageSolved(questionSet));

				//If all assignments of a task are solved
				if(done && !finished[i]){
					finished[i]= true;
					numCompleted++;
					//Receive the answers for the HIT 	
					answerSet = receiver.checkForAnswers(questionSet);	
					
					//number of nuclei determined in step 1
					int numNuclei = Integer.valueOf(answerQuestionMapping.get(questionSet).getAnswerValue());
					
					//TODO aggregate the information to compute your estimate of the number of nuclei
					for (Answer answer : answerSet.getSolutionsForQuestion(questionID2)) {
						//accept all submissions
						receiver.approveAssignment(answer.getAssignmentID(), "Thank you very much");
						if (answer.getAnswerValue().equals("1")){ //numNuclei was accepted by the validation worker
							
						}

					}
				}
			}
		}

	}
	
	private static QuestionSet generateQuestion(MturkQuestionSetType type, int numberOfAssignments) throws CrowdLangException{
		//Creation of the HIT
		FreeTextQuestion q1 = new FreeTextQuestion("How many", MturkGeneralComponents.createText("Please count the number of nuclei in the picture") +
				MturkGeneralComponents.createImageReference("jpg", "http://www.ifi.uzh.ch/ce/teaching/spring2012/nuclei1.jpg", "nuclei")				
				//you could add a picture here
				//+ MturkGeneralComponents.createImageReference("jpg", "http://www.ifi.uzh.ch/ce/teaching/spring2012/nuclei1.jpg", "nuclei")
				, questionID1, true);
		//Guarantees that the answer is a number
		q1.setNumeric(true);
		
		QuestionSet questionSet = new QuestionSet("processID", "taskID", CrowdMarketEnum.MTURK, type, numberOfAssignments, 26100, "Count Nuclei", "Please count the number of nuclei in the picture");
		questionSet.addQuestion(q1);
		
		return questionSet;
	}
	
	private static QuestionSet generateQuestion2(MturkQuestionSetType type, int numberOfAssignments, String number) throws CrowdLangException{
		SelectionQuestion q2 = new SelectionQuestion("Please count the number of nuclei in the picture", MturkGeneralComponents.createImageReference("jpg", "http://www.ifi.uzh.ch/ce/teaching/spring2012/nuclei1.jpg", "nuclei"), questionID2, true, SelectionStyleSuggestion.radiobutton);
		SelectionIdentifier si1 = new SelectionIdentifier(MturkGeneralComponents.createText("Close (+/-10) of " + number), "1");
		SelectionIdentifier si2 = new SelectionIdentifier(MturkGeneralComponents.createText("Further away: 10 more or less than " + number), "0");
		q2.addSuggestion(si1, 0);
		q2.addSuggestion(si2, 1);
			
		QuestionSet questionSet = new QuestionSet("processID", "taskID", CrowdMarketEnum.MTURK, type, numberOfAssignments, 26100, "Count Nuclei", "Please count the number of nuclei in the picture");
		questionSet.addQuestion(q2);
		
		return questionSet;
	}
}
