package de.ugoe.cs.swe.memos;

import java.util.Vector;

import de.ugoe.cs.swe.memos.database.DBSearcher;
import de.ugoe.cs.swe.memos.datamodel.IntegerMap;
import de.ugoe.cs.swe.memos.datamodel.Memo;

public class RelevanceRanker {

	public static Vector<Memo> rankMemos(Vector<Memo> toBeRanked,
			Vector<String> rankBy) {

		if (toBeRanked == null || toBeRanked.size() == 0 || rankBy == null
				|| rankBy.size() == 0)
			return toBeRanked;

		final int size = toBeRanked.size();

		int[] memoAge = new int[size];
		int[] overallCounter = new int[size];
		int[] overallFreeCounter = new int[size];

		boolean[] isFromMe = new boolean[size];
		boolean[] isDraft = new boolean[size];
		boolean[] inTitle = new boolean[size];
		boolean[] inContent = new boolean[size];
		boolean[] inAuthor = new boolean[size];

		for (int i = 0; i < size; i++) {
			Memo curMemo = toBeRanked.get(i);

			isDraft[i] = curMemo.isDraft();
			isFromMe[i] = (SettingsManager.getInstance().getUsername() == curMemo
					.getAuthor());
			memoAge[i] = (int) ((new java.util.Date()).getTime() - curMemo
					.getTimestamp().getTime())
					/ (1000 * 60 * 60);

			for (int j = 0; j < rankBy.size(); j++) {
				int counter = 0, counter2 = 0;
				String curTag = rankBy.get(j);

				counter = countAppearances(curMemo.getAuthor(), curTag, false);
				counter2 = countAppearances(curMemo.getAuthor(), curTag, true);
				if (counter > 0)
					inAuthor[i] = true;
				overallCounter[i] += counter;
				overallFreeCounter[i] += counter2;

				counter = countAppearances(curMemo.getTitle(), curTag, false);
				counter2 = countAppearances(curMemo.getTitle(), curTag, true);
				if (counter > 0)
					inTitle[i] = true;
				overallCounter[i] += counter;
				overallFreeCounter[i] += counter2;

				counter = countAppearances(curMemo.getContent(), curTag, false);
				counter2 = countAppearances(curMemo.getContent(), curTag, true);
				if (counter > 0)
					inContent[i] = true;
				overallCounter[i] += counter;
				overallFreeCounter[i] += counter2;

				overallCounter[i] += countAppearances(
						curMemo.getTagsAsString(), curTag, false);
				overallFreeCounter[i] += countAppearances(curMemo
						.getTagsAsString(), curTag, true);

				overallCounter[i] += countAppearances(curMemo.getTimestamp()
						.toString(), curTag, false);
				overallFreeCounter[i] += countAppearances(curMemo
						.getTimestamp().toString(), curTag, true);
			}
		}

		IntegerMap[] points = new IntegerMap[size];
		IntegerMap[] compare = new IntegerMap[size];

		// calculate points for each memo
		for (int i = 0; i < size; i++)
			points[i] = new IntegerMap(i, 0);

		// rank memos by total occurances of tags
		for (int i = 0; i < size; i++)
			compare[i] = new IntegerMap(i, overallCounter[i]);
		java.util.Arrays.sort(compare);
		// add points to memo account according to place in ranking
		// balance: one point per rank
		for (int i = 0; i < size; i++)
			points[compare[i].index].value += i + 1;

		// rank memos by occurances of tags as single word
		for (int i = 0; i < size; i++)
			compare[i] = new IntegerMap(i, overallFreeCounter[i]);
		java.util.Arrays.sort(compare);
		// add points to memo account according to place in ranking
		// balance: three points per rank
		for (int i = 0; i < size; i++)
			points[(Integer) compare[i].index].value += (i + 1) * 3;

		// rank memos by age. older memos rank worse than younger ones.
		for (int i = 0; i < size; i++)
			compare[i] = new IntegerMap(i, memoAge[i]);
		java.util.Arrays.sort(compare);
		// add points to memo account according to place in ranking
		// balance: two points per rank
		for (int i = 0; i < size; i++)
			points[(Integer) compare[i].index].value += (size - i) * 2;

		// value or devalue according to different factors as accurances in
		// title, author, content and wether memo is from searcher or a draft
		for (int i = 0; i < size; i++) {
			// devalue if memo is a draft
			if (toBeRanked.get(i).isDraft())
				points[i].value *= 0.4;
			// value if memo is from searcher itself
			if (toBeRanked.get(i).getAuthor() == SettingsManager.getInstance()
					.getUsername())
				points[i].value *= 1.8;
			// value if keyword accures in title, author oder content
			if (inTitle[i] || inAuthor[i] || inContent[i])
				points[i].value *= 1.4;
			// value if file has attachements
			if (toBeRanked.get(i).getFiles().size() > 0)
				points[i].value *= 1.6;
		}

		Vector<Memo> returnVector = new Vector<Memo>(size);

		// generate return vector and fetch memos ordered by final balance
		java.util.Arrays.sort(points);
		for (int i = size - 1; i >= 0; i--)
			returnVector.add(toBeRanked.get(points[i].index));

		return returnVector;
	}

	public static int countAppearances(String source, String search,
			boolean onlyFreeAppearances) {

		if (source == null || search == null)
			return 0;

		int current = 0, counter = 0;
		boolean isFree;

		// walk source string for positive matches with search string
		for (current = 0; current <= source.length() - search.length(); current++) {
			String substring;

			// test for equality; ignore different cases
			substring = source.substring(current, current + search.length());
			if (substring.toLowerCase().equals(search.toLowerCase())) {
				isFree = true;

				// if match should be freestanding, test here
				if (onlyFreeAppearances) {
					// TEST LEFT SIDE
					// but only if there is at least one more character leftside
					if (current != 0
							&& Character.isLetterOrDigit(source.substring(
									current - 1, current).toCharArray()[0]))
						// if left neighbour is alphanumeric it is no match
						isFree = false;

					// TEST RIGHT SIDE
					// but only if there's at least one more character rightside
					if (current + search.length() < source.length()
							&& Character.isLetterOrDigit(source.substring(
									current + search.length(),
									current + search.length() + 1)
									.toCharArray()[0]))
						// if right neighbour is alphanumeric it is no match
						isFree = false;
				}

				// if its still a match till here, increment counter
				if (isFree) {
					counter++;
					current = current + search.length() - 1;
				}
			}
		}

		return counter;
	}

	public static void main(String[] args) {
		Vector<Memo> fetch = (new DBSearcher("xprakt2/nominiert/organe/der"))
				.execute();

		Vector<String> tags = new Vector<String>();
		tags.add("xprakt2");
		tags.add("nominiert");
		tags.add("organe");
		tags.add("der");

		rankMemos(fetch, tags);

		return;
	}
}
