package de.ugoe.cs.swe.exercises.exercisesheet;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

import de.ugoe.cs.swe.exercises.exercise.Exercise;
import de.ugoe.cs.swe.exercises.lectureevent.LectureEvent;
import de.ugoe.cs.swe.exercises.misc.CKeyword;
import de.ugoe.cs.swe.exercises.misc.QuickSort;

public class SheetGenerator {

	private int desiredAmount;
	private int maxComplexity;
	private LectureEvent lectureEvent;
	private ArrayList<CKeyword> keywords;
	private ArrayList<Exercise> exercises;

	public SheetGenerator() {

	}

	public SheetGenerator(LectureEvent lectureEvent,
			ArrayList<CKeyword> keywords) {
		this.lectureEvent = lectureEvent;
		this.keywords = keywords;
	}

	public SheetGenerator(int maxComplexity, LectureEvent lectureEvent,
			ArrayList<CKeyword> keywords, int desiredAmount) {
		this.maxComplexity = maxComplexity;
		this.lectureEvent = lectureEvent;
		this.keywords = keywords;
		this.desiredAmount = desiredAmount;
	}

	public ArrayList<Exercise> getExercises() {
		return exercises;
	}

	public int getMaxComplexity() {
		return maxComplexity;
	}

	public LectureEvent getLectureEvent() {
		return lectureEvent;
	}

	public ArrayList<CKeyword> getKeywords() {
		return keywords;
	}

	public ArrayList<Exercise> generateExercises() {

		int currentComplexity = 0;

		ArrayList<ExerciseSheet> exerciseSheetList = ExerciseSheet.all();
		ArrayList<Exercise> exerciseList = Exercise.all();
		ArrayList<Exercise> validExerciseList = new ArrayList<Exercise>();
		ArrayList<Integer> score = new ArrayList<Integer>();

		Iterator<Exercise> exerciseListIterator = exerciseList.iterator();
		while (exerciseListIterator.hasNext()) {
			Exercise currentExercise = exerciseListIterator.next();
			ArrayList<CKeyword> keys = CKeyword.getKeywordsForModel(
					currentExercise.getId(), CKeyword.EXERCISE);

			Iterator<CKeyword> keysIterator = keys.iterator();
			while (keysIterator.hasNext()) {
				CKeyword currentKey = keysIterator.next();

				for (int i = 0; i < keywords.size(); i++) {
					if (keywords.get(i).getKey().equals(currentKey.getKey())) {
						if (!validExerciseList.contains(currentExercise))
							validExerciseList.add(currentExercise);
						break;
					}
				}

			}
		}

		Iterator<Exercise> validExerciseListIterator = validExerciseList
				.iterator();
		int position = 0;

		while (validExerciseListIterator.hasNext()) {
			Exercise currentValidExercise = validExerciseListIterator.next();
			score.add(0);

			Iterator<ExerciseSheet> exerciseSheetIterator = exerciseSheetList
					.iterator();
			while (exerciseSheetIterator.hasNext()) {
				ExerciseSheet currentExerciseSheet = exerciseSheetIterator
						.next();

				for (int i = 0; i < currentExerciseSheet.getExercises().size(); i++) {
					if (currentExerciseSheet.getExercises().get(i).getId() == currentValidExercise
							.getId()) {
						if (lectureEvent.getId() == currentExerciseSheet
								.getLectureEvent().getId()) {
							// Make sure that exercises used in the same
							// lectureEvent are a last resort
							score.set(position, score.get(position) + 100);
						}

						score.set(position, score.get(position) + 10);
					}
				}

			}

			ArrayList<CKeyword> keys = CKeyword.getKeywordsForModel(
					currentValidExercise.getId(), CKeyword.EXERCISE);
			Iterator<CKeyword> keysIterator = keys.iterator();
			while (keysIterator.hasNext()) {
				CKeyword currentKey = keysIterator.next();
				for (int i = 0; i < keywords.size(); i++) {
					if (keywords.get(i).getKey().equals(currentKey.getKey())) {

						score.set(position, score.get(position) - 10);
						break;
					}
				}

			}

			position++;

		}

		Exercise[] validExerciseListArray = new Exercise[validExerciseList
				.size()];
		Integer[] scoreArray = new Integer[score.size()];

		for (int i = 0; i < validExerciseListArray.length; i++) {
			validExerciseListArray[i] = validExerciseList.get(i);
		}

		for (int i = 0; i < scoreArray.length; i++) {
			scoreArray[i] = score.get(i);
		}

		QuickSort sorter = new QuickSort();

		sorter.quickSort(scoreArray, 0, scoreArray.length - 1,
				validExerciseListArray);

		validExerciseList.clear();

		for (int i = 0; i < sorter.getExercises().length; i++) {
			validExerciseList.add(sorter.getExercises()[i]);
		}

		ArrayList<Integer> validExerciseListComplexities = new ArrayList<Integer>();

		for (int i = 0; i < validExerciseListArray.length; i++) {
			validExerciseListComplexities.add(validExerciseListArray[i]
					.getComplexity());
		}

		exercises = new ArrayList<Exercise>();

		// Stupid linear method of exercise accumulation
		/*
		 * for (int i = 0 ; i < validExerciseList.size() && exercises.size() <
		 * desiredAmount ; i++) { if(currentComplexity +
		 * validExerciseList.get(i).getComplexity() <= maxComplexity) {
		 * currentComplexity += validExerciseList.get(i).getComplexity();
		 * exercises.add(validExerciseList.get(i)); } }
		 */

		// A.T.T.R.A.C.T.I.V.E.
		// Awesome totally truthfully random algorithm of counterbalanced task integration for varying entries

		Random rand = new Random();
		double rightBoundaryFactor = 0.01;

		while (rightBoundaryFactor <= 1.0001) {
			int run = 0;

			while (run <= 10) {
				ArrayList<Exercise> validExerciseListTemp = validExerciseList;
				while (currentComplexity < maxComplexity
						&& ((int) validExerciseListTemp.size() * rightBoundaryFactor) > 0
						&& exercises.size() < desiredAmount && run <= 10) {

					int elementIndex = 0;
					if ((int) ((validExerciseListTemp.size() - 1) * rightBoundaryFactor) > 1) {
						rand
								.nextInt((int) ((validExerciseListTemp.size() - 1) * rightBoundaryFactor));
					}
					if (currentComplexity
							+ validExerciseListTemp.get(elementIndex)
									.getComplexity() > maxComplexity) {
						validExerciseListTemp.remove(elementIndex);
						continue;
					}
					exercises.add(validExerciseListTemp.get(elementIndex));
					currentComplexity += validExerciseListTemp
							.get(elementIndex).getComplexity();
					validExerciseListTemp.remove(elementIndex);
				}
				if (exercises.size() == desiredAmount
						&& (double) currentComplexity >= 0.75 * maxComplexity) {
					break;
				}
				run++;
			}
			if (exercises.size() == desiredAmount
					&& (double) currentComplexity >= 0.75 * maxComplexity) {
				break;
			}

			currentComplexity = 0;
			rightBoundaryFactor = rightBoundaryFactor + 0.01;
		}

		return exercises;
	}

	public ArrayList<Exercise> getValidExercises() {
		ArrayList<ExerciseSheet> exerciseSheetList = ExerciseSheet.all();
		ArrayList<Exercise> exerciseList = Exercise.all();
		ArrayList<Exercise> validExerciseList = new ArrayList<Exercise>();
		ArrayList<Integer> score = new ArrayList<Integer>();

		Iterator<Exercise> exerciseListIterator = exerciseList.iterator();
		while (exerciseListIterator.hasNext()) {
			Exercise currentExercise = exerciseListIterator.next();
			ArrayList<CKeyword> keys = CKeyword.getKeywordsForModel(
					currentExercise.getId(), CKeyword.EXERCISE);

			Iterator<CKeyword> keysIterator = keys.iterator();
			while (keysIterator.hasNext()) {
				CKeyword currentKey = keysIterator.next();

				for (int i = 0; i < keywords.size(); i++) {
					if (keywords.get(i).getKey().equals(currentKey.getKey())) {
						if (!validExerciseList.contains(currentExercise))
							validExerciseList.add(currentExercise);
						break;
					}
				}

			}
		}

		Iterator<Exercise> validExerciseListIterator = validExerciseList
				.iterator();
		int position = 0;

		while (validExerciseListIterator.hasNext()) {
			Exercise currentValidExercise = validExerciseListIterator.next();
			score.add(0);

			Iterator<ExerciseSheet> exerciseSheetIterator = exerciseSheetList
					.iterator();
			while (exerciseSheetIterator.hasNext()) {
				ExerciseSheet currentExerciseSheet = exerciseSheetIterator
						.next();

				for (int i = 0; i < currentExerciseSheet.getExercises().size(); i++) {
					if (currentExerciseSheet.getExercises().get(i).getId() == currentValidExercise
							.getId()) {
						if (lectureEvent.getId() == currentExerciseSheet
								.getLectureEvent().getId()) {
							// Make sure that exercises used in the same
							// lectureEvent are a last resort
							score.set(position, score.get(position) + 100);
						}
						score.set(position, score.get(position) + 10);
					}
				}

			}

			ArrayList<CKeyword> keys = CKeyword.getKeywordsForModel(
					currentValidExercise.getId(), CKeyword.EXERCISE);
			Iterator<CKeyword> keysIterator = keys.iterator();
			while (keysIterator.hasNext()) {
				CKeyword currentKey = keysIterator.next();
				for (int i = 0; i < keywords.size(); i++) {
					if (keywords.get(i).getKey().equals(currentKey.getKey())) {
						score.set(position, score.get(position) - 10);
						break;
					}
				}

			}

			position++;

		}

		Exercise[] validExerciseListArray = new Exercise[validExerciseList
				.size()];
		Integer[] scoreArray = new Integer[score.size()];

		for (int i = 0; i < validExerciseListArray.length; i++) {
			validExerciseListArray[i] = validExerciseList.get(i);
		}

		for (int i = 0; i < scoreArray.length; i++) {
			scoreArray[i] = score.get(i);
		}

		QuickSort sorter = new QuickSort();

		sorter.quickSort(scoreArray, 0, scoreArray.length - 1,
				validExerciseListArray);

		if ( sorter.getExercises() == null ) {
			return validExerciseList;
		}
		
		validExerciseList.clear();
		
		for (int i = 0; i < sorter.getExercises().length; i++) {
			validExerciseList.add(sorter.getExercises()[i]);
		}
		return validExerciseList;
	}

	int[] accumulator() {
		return null;
	}

}
