/*
 * This source code is made available under the terms of the
 * Common Public License Version 1.0 distibuted along with the
 * source code package.
 */
package de.uka.cmtm.serviceregistry.query.protege;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import edu.stanford.smi.protegex.owl.model.OWLClass;
import edu.stanford.smi.protegex.owl.model.OWLIndividual;

/**
 * Calculates the relevance with using correlation between result vector and
 * parameter vector using the weights set by the constants below. All different
 * types of classes in the search parameter and result specification are equally
 * weighted.
 * 
 * @author tilmann
 */
public class SimpleRelevanceCalculator implements RelevanceCalculator {

	private static final double DIREKT_HIT_WEIGHT = 1.0;

	private static final double CONFORM_HIT_WEIGHT = 0.5;

	private static final double DHW_SQUARE = DIREKT_HIT_WEIGHT
			* DIREKT_HIT_WEIGHT;

	private static final double CHW_SQUARE = CONFORM_HIT_WEIGHT
			* CONFORM_HIT_WEIGHT;

	private double weightProductSum = 0.0;

	private double resultSquareWeightSum = 0.0;

	private double parameterSquareWeightSum = 0.0;

	/**
	 * Calculates the relevance of the search result info regarding the given
	 * search parameter and conform classes
	 * 
	 * @param info
	 *            the search result
	 * @param parameter
	 *            the search parameter
	 * @param conformClasses
	 *            the conform classes
	 * @return the relevance of the result-parameter-set
	 */
	public double calculateRelevance(ProfileIndividualInfo info,
			ProfileIndividualParameter parameter,
			ParameterConformClasses conformClasses) {

		weightProductSum = 0.0;
		resultSquareWeightSum = 0.0;
		parameterSquareWeightSum = 0.0;

		updateWeightSums(info.getPreconditions(), parameter.getPreconditions(),
				conformClasses.getPreconditions());
		updateWeightSums(info.getEffects(), parameter.getEffects(),
				conformClasses.getEffects());
		updateWeightSums(info.getLogicalInputs(), parameter.getLogicalInputs(),
				conformClasses.getLogicalInputs());
		updateWeightSums(info.getLogicalOutputs(), parameter
				.getLogicalOutputs(), conformClasses.getLogicalOutputs());
		updateWeightSums(info.getUserRoles(), parameter.getUserRoles(),
				conformClasses.getUserRoles());
		if (parameter.getCategory() != null) {
			updateWeightSums(Collections.singleton(info.getProfile()),
					Collections.singleton(parameter.getCategory()),
					conformClasses.getCategories());
		} else {
			updateWeightSums(Collections.singleton(info.getProfile()),
					Collections.EMPTY_SET, conformClasses.getCategories());
		}

		double denominator = Math.sqrt(resultSquareWeightSum)
				* Math.sqrt(parameterSquareWeightSum);

		if (denominator == 0.0)
			return 0.0;
		else
			return weightProductSum / denominator;
	}

	/**
	 * Increase the weight sums regarding the given results, parameters and
	 * conform classes
	 * 
	 * @param resultIndividuals
	 *            the results
	 * @param parameterClasses
	 *            the parameters
	 * @param conformClasses
	 *            the conform classes
	 */
	private void updateWeightSums(Collection<OWLIndividual> resultIndividuals,
			Collection<OWLClass> parameterClasses,
			Collection<OWLClass> conformClasses) {

		// Get Classes of results
		Collection<OWLClass> resultClasses = getClassesOfIndividuals(resultIndividuals);

		// System.out.println("Individuals: " + resultClasses);
		// System.out.println("Parameters: " + parameterClasses);
		// System.out.println("Conforms: " + conformClasses);
		// System.out.println();

		for (OWLClass resultClass : resultClasses) {

			resultSquareWeightSum += 1.0;

			if (parameterClasses.contains(resultClass))
				weightProductSum += DIREKT_HIT_WEIGHT;
			else if (conformClasses.contains(resultClass))
				weightProductSum += CONFORM_HIT_WEIGHT;
		}

		parameterSquareWeightSum += DHW_SQUARE * parameterClasses.size();
		for (OWLClass conformClass : conformClasses) {
			if (!parameterClasses.contains(conformClass))
				parameterSquareWeightSum += CHW_SQUARE;
		}
	}

	/**
	 * Retrieves all Classes of the given Individuals
	 * 
	 * @param individuals
	 *            the individuals to retrieve classes for
	 * @return a Collection with all classes
	 */
	private Collection<OWLClass> getClassesOfIndividuals(
			Collection<OWLIndividual> individuals) {

		Set<OWLClass> classes = new HashSet<OWLClass>();
		for (OWLIndividual individual : individuals)
			classes.add((OWLClass) individual.getRDFType());
		return classes;
	}

}
