package de.uka.ipd.consensus.foundation.rating;

import java.util.Collection;
import java.util.Properties;

import de.uka.ipd.consensus.foundation.ConsensusFoundationModule;
import de.uka.ipd.consensus.foundation.logging.LoggingManager;
import de.uka.ipd.consensus.foundation.ontology.OntologyManager;
import de.uka.ipd.consensus.foundation.rights.DynamicRightsManagerException;
import de.uka.ipd.consensus.foundation.schema.Association;
import de.uka.ipd.consensus.foundation.schema.Attribute;
import de.uka.ipd.consensus.foundation.schema.Rating;
import de.uka.ipd.consensus.foundation.schema.Topic;
import de.uka.ipd.consensus.foundation.schema.User;
import de.uka.ipd.consensus.foundation.user.UserManager;

/**
 * The RatingManager manages (stores) the ratings for all rateable elements of the ontology. It must ensure that
 * each rating can only be assigned to at most one rateable element and that each rateable element has not more than one rating per user.
 * <p>The RatingManager can only manage ratings for concepts and users, although there are other rateable interfaces
 * (see <code>OntologyManager.checkInternalRateable()</code>) - this is why there are several explicit methods for the managed types instead of
 * one polymorphic method with a <code>Rateable</code> parameter.
 * 
 * <p>The RatingManager implementation can be configured with the key <code>cf.ratingmanager</code>
 * in the <code>ConsensusFoundation.properties</code> configuration file. It's an optional component.</p>
 * 
 * <p>The implementation is loaded by the <code>ConsensusFoundation</code> constructor. For successful instantiation,
 * the RatingManager implementation must provide a default constructor without arguments.</p>
 * 
 * @author  Thomas Much
 * @version 2006-06-14
 * @see de.uka.ipd.consensus.foundation.ConsensusFoundation
 * @see de.uka.ipd.consensus.foundation.schema.Rateable
 * @see de.uka.ipd.consensus.foundation.ontology.OntologyManager#checkInternalRateable(Rateable)
 */
public interface RatingManager extends ConsensusFoundationModule {

	/**
	 * <p>This method is called by the <code>ConsensusFoundation</code> constructor to initialize the RatingManager.
	 * @param config all entries from <code>ConsensusFoundation.properties</code>
	 * @param logging the successfully initialized LoggingManager instance
	 * @param users the the successfully initialized UserManager instance
	 * @param ontology the the successfully initialized OntologyManager instance
	 * @throws Exception if any error occurs (startup of the application is aborted in this case)
	 * @see de.uka.ipd.consensus.foundation.ConsensusFoundation#ConsensusFoundation()
	 */
	public void init(Properties config, LoggingManager logging, UserManager users, OntologyManager ontology) throws Exception;
	
	
	/**
	 * Creates a new rating with the given user as the creator ("owner" of the rating).
	 * The rating is not assigned to any rateable element yet, this is done using the various <code>addRating()</code> methods
	 * or - better - the <code>Rateable</code> interface.
	 * @param creator the user who wants to create the rating
	 * @param value the value for the newly created rating (-1..1, including both values)
	 * @return the newly created rating
	 * @throws RatingManagerException if the rating could not be created
	 * @throws DynamicRightsManagerException if the user is not allowed to create ratings
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#addRating(User, double)
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager#mayCreateRating(User)
	 */
	public Rating createRating(User creator, double value) throws RatingManagerException,DynamicRightsManagerException;

	
	/**
	 * Adds the given rating to the given topic.
	 * <p>The implementation must ensure that each rating can be added to one rateable element only,
	 * that each user can rate each topic only once and that historical ratings cannot be added at all.</p>
	 * @param rated the topic to which the rating will be added
	 * @param rating the rating to be added
	 * @throws RatingManagerException if the rating cannot be added to the given topic
	 * @throws DynamicRightsManagerException if the user is not allowed to add the rating
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#isHistorical()
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager#mayAddRating(Rating, Rateable)
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#addRating(User, double)
	 */
	public void addRating(Topic rated, Rating rating) throws RatingManagerException,DynamicRightsManagerException;
	
	
	/**
	 * Adds the given rating to the given attribute.
	 * <p>The implementation must ensure that each rating can be added to one rateable element only,
	 * that each user can rate each attribute only once and that historical ratings cannot be added at all.</p>
	 * @param rated the attribute to which the rating will be added
	 * @param rating the rating to be added
	 * @throws RatingManagerException if the rating cannot be added to the given attribute
	 * @throws DynamicRightsManagerException if the user is not allowed to add the rating
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#isHistorical()
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager#mayAddRating(Rating, Rateable)
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#addRating(User, double)
	 */
	public void addRating(Attribute rated, Rating rating) throws RatingManagerException,DynamicRightsManagerException;
	
	
	/**
	 * Adds the given rating to the given association.
	 * <p>The implementation must ensure that each rating can be added to one rateable element only,
	 * that each user can rate each association only once and that historical ratings cannot be added at all.</p>
	 * @param rated the association to which the rating will be added
	 * @param rating the rating to be added
	 * @throws RatingManagerException if the rating cannot be added to the given association
	 * @throws DynamicRightsManagerException if the user is not allowed to add the rating
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#isHistorical()
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager#mayAddRating(Rating, Rateable)
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#addRating(User, double)
	 */
	public void addRating(Association rated, Rating rating) throws RatingManagerException,DynamicRightsManagerException;
	
	
	/**
	 * Adds the given rating to the given user.
	 * <p>The implementation must ensure that each rating can be added to one rateable element only,
	 * that each user can rate each other user only once and that historical ratings cannot be added at all.</p>
	 * @param rated the user to which the rating will be added
	 * @param rating the rating to be added
	 * @throws RatingManagerException if the rating cannot be added to the given user
	 * @throws DynamicRightsManagerException if the user (who created the rating) is not allowed to add the rating to the given user
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#isHistorical()
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager#mayAddRating(Rating, Rateable)
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#addRating(User, double)
	 */
	public void addRating(User rated, Rating rating) throws RatingManagerException,DynamicRightsManagerException;

	
	/**
	 * Removes a rating and its history if it is assigned to a rateable element (otherwise this call is ignored).
	 * @param remover the user who wants to remove the rating
	 * @param rating the rating to be removed
	 * @throws RatingManagerException if the rating could not be removed
	 * @throws DynamicRightsManagerException if the user is not allowed to remove the rating
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#getRateable()
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager#mayRemoveRating(User, Rating)
	 */
	public void removeRating(User remover, Rating rating) throws RatingManagerException,DynamicRightsManagerException;

	
	/**
	 * Returns a collection of all Rating objects that have been added to the given topic.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param topic the topic for which the ratings are returned
	 * @return a possibly empty collection, never <code>null</code>
	 * @see de.uka.ipd.consensus.foundation.schema.Rating
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRatings()
	 */
	public Collection getRatings(Topic topic);
	
	
	/**
	 * Returns a collection of all Rating objects that have been added to the given attribute.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param attribute the attribute for which the ratings are returned
	 * @return a possibly empty collection, never <code>null</code>
	 * @see de.uka.ipd.consensus.foundation.schema.Rating
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRatings()
	 */
	public Collection getRatings(Attribute attribute);
	
	
	/**
	 * Returns a collection of all Rating objects that have been added to the given association.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param association the association for which the ratings are returned
	 * @return a possibly empty collection, never <code>null</code>
	 * @see de.uka.ipd.consensus.foundation.schema.Rating
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRatings()
	 */
	public Collection getRatings(Association association);
	
	
	/**
	 * Returns a collection of all Rating objects that have been added by other users to the given user.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param user the user for whom the ratings are returned
	 * @return a possibly empty collection, never <code>null</code>
	 * @see de.uka.ipd.consensus.foundation.schema.Rating
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRatings()
	 */
	public Collection getRatings(User user);

	
	/**
	 * Returns the number of ratings that have been added to the given topic.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param topic the topic for which the number of ratings shall be returned
	 * @return the number of ratings for the given topic
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRatingsCount()
	 */
	public int getRatingsCount(Topic topic);
	
	
	/**
	 * Returns the number of ratings that have been added to the given attribute.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param attribute the attribute for which the number of ratings shall be returned
	 * @return the number of ratings for the given attribute
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRatingsCount()
	 */
	public int getRatingsCount(Attribute attribute);
	
	
	/**
	 * Returns the number of ratings that have been added to the given association.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param association the association for which the number of ratings shall be returned
	 * @return the number of ratings for the given association
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRatingsCount()
	 */
	public int getRatingsCount(Association association);
	
	
	/**
	 * Returns the number of ratings that have been added by other users to the given user.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param user the user for whom the number of ratings shall be returned
	 * @return the number of other user's ratings for the given user
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRatingsCount()
	 */
	public int getRatingsCount(User user);

	
	/**
	 * Returns the average (arithmetic mean) of all ratings for the given topic.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param topic the topic for which the average rating value shall be computed
	 * @return the arithmetic mean of all ratings or <code>Rating.NEUTRAL</code> if the topic isn't rated yet
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#NEUTRAL
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getAverageRatingValue()
	 */
	public double getAverageRatingValue(Topic topic);
	
	
	/**
	 * Returns the average (arithmetic mean) of all ratings for the given attribute.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param attribute the attribute for which the average rating value shall be computed
	 * @return the arithmetic mean of all ratings or <code>Rating.NEUTRAL</code> if the attribute isn't rated yet
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#NEUTRAL
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getAverageRatingValue()
	 */
	public double getAverageRatingValue(Attribute attribute);
	
	
	/**
	 * Returns the average (arithmetic mean) of all ratings for the given association.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param association the association for which the average rating value shall be computed
	 * @return the arithmetic mean of all ratings or <code>Rating.NEUTRAL</code> if the association isn't rated yet
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#NEUTRAL
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getAverageRatingValue()
	 */
	public double getAverageRatingValue(Association association);

	
	/**
	 * Returns the average (arithmetic mean) of all ratings that other users have added to the given user.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param user the user for whom the average rating value shall be computed
	 * @return the arithmetic mean of all ratings or <code>Rating.NEUTRAL</code> if the user isn't rated yet
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#NEUTRAL
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getAverageRatingValue()
	 */
	public double getAverageRatingValue(User user);

	
	/**
	 * Returns the rating object that the given user has added to the given topic (or <code>null</code> if the user hasn't rated the topic yet).
	 * @param topic the topic from which the rating is returned
	 * @param rater the user who added the rating
	 * @return the Rating object or <code>null</code>
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRating(User)
	 */
	public Rating getRating(Topic topic, User rater);
	
	
	/**
	 * Returns the rating object that the given user has added to the given attribute (or <code>null</code> if the user hasn't rated the attribute yet).
	 * @param attribute the attribute from which the rating is returned
	 * @param rater the user who added the rating
	 * @return the Rating object or <code>null</code>
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRating(User)
	 */
	public Rating getRating(Attribute attribute, User rater);
	
	
	/**
	 * Returns the rating object that the given user has added to the given association (or <code>null</code> if the user hasn't rated the association yet).
	 * @param association the association from which the rating is returned
	 * @param rater the user who added the rating
	 * @return the Rating object or <code>null</code>
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRating(User)
	 */
	public Rating getRating(Association association, User rater);
	
	
	/**
	 * Returns the rating object that the given user (parameter "rater") has added to another user (parameter "user")
	 * - or <code>null</code> if the user hasn't rated the other user yet.
	 * @param user the user from which the rating is returned
	 * @param rater the user who added the rating
	 * @return the Rating object or <code>null</code>
	 * @see de.uka.ipd.consensus.foundation.schema.Rateable#getRating(User)
	 */
	public Rating getRating(User user, User rater);

	
	/**
	 * Returns a collection of all Rating objects that the given user has added to other rateable elements.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param user the user from whom the ratings are returned
	 * @return a possibly empty collection, never <code>null</code>
	 * @see de.uka.ipd.consensus.foundation.schema.Rating
	 * @see de.uka.ipd.consensus.foundation.schema.User#getOwnRatings()
	 */
	public Collection getUsersOwnRatings(User user);
	
	
	/**
	 * Returns the number of ratings that the given user has added to other rateable elements.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param user the user from whom the number of ratings shall be returned
	 * @return the number of ratings the given user has added to other elements
	 * @see de.uka.ipd.consensus.foundation.schema.User#getOwnRatingsCount()
	 */
	public int getUsersOwnRatingsCount(User user);
	
	
	/**
	 * Returns the average (arithmetic mean) of all ratings that the given user has added to other rateable elements.
	 * <p>Only current ratings are taken into account, not historical ones.</p>
	 * @param user the user from whose ratings the average rating value shall be computed
	 * @return the arithmetic mean of all ratings or <code>Rating.NEUTRAL</code> if the user hasn't rated other elements yet
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#NEUTRAL
	 * @see de.uka.ipd.consensus.foundation.schema.User#getAverageOwnRatingsValue()
	 */
	public double getUsersAverageOwnRatingsValue(User user);

	
	/**
	 * If a topic is deleted permanently from the ontology, this method can be used
	 * to remove the ratings that were added to the topic, too (including historical values).
	 * <p>This method will be used by the EvolutionManager.</p>
	 * @param topic the topic object for which the corresponding ratings shall be deleted
	 * @throws RatingManagerException if the ratings could not be deleted
	 * @see de.uka.ipd.consensus.foundation.evolution.EvolutionManager#deleteTopic(User, Topic)
	 * @see de.uka.ipd.consensus.foundation.evolution.EvolutionManager#deleteTopicAndSubtopics(User, Topic)
	 */
	public void deleteRatings(Topic topic) throws RatingManagerException;
	
	
	/**
	 * If an attribute is deleted permanently from the ontology, this method can be used
	 * to remove the ratings that were added to the attribute, too (including historical values).
	 * <p>This method will be used by the EvolutionManager.</p>
	 * @param attribute the attribute object for which the corresponding ratings shall be deleted
	 * @throws RatingManagerException if the ratings could not be deleted
	 * @see de.uka.ipd.consensus.foundation.evolution.EvolutionManager#deleteAttribute(User, Topic, Attribute)
	 */
	public void deleteRatings(Attribute attribute) throws RatingManagerException;
	
	
	/**
	 * If an association is deleted permanently from the ontology, this method can be used
	 * to remove the ratings that were added to the association, too (including historical values).
	 * <p>This method will be used by the EvolutionManager.</p>
	 * @param association the association object for which the corresponding ratings shall be deleted
	 * @throws RatingManagerException if the ratings could not be deleted
	 * @see de.uka.ipd.consensus.foundation.evolution.EvolutionManager#deleteAssociation(User, Association)
	 */
	public void deleteRatings(Association association) throws RatingManagerException;
	
	
	/**
	 * If a user is deleted permanently from the ontology and user database, this method can be used
	 * to remove the ratings that were added to the user, too (including historical values).
	 * <p>This method will be used by the UserManager.</p>
	 * @param user the user object for which the corresponding ratings shall be deleted
	 * @throws RatingManagerException if the ratings could not be deleted
	 * @see de.uka.ipd.consensus.foundation.user.UserManager#deleteUser(User, String)
	 */
	public void deleteRatings(User user) throws RatingManagerException;

	
	/**
	 * As applications will have to store rating values as strings (e.g. in the presentation layer), this method
	 * converts a rating value into a string. If the RatingManager supports discrete values, this method should try to
	 * return suitable string for these concrete values instead of simply turning the value number into a string
	 * (e.g. "YES", "NO", "NEUTRAL" instead of "1", "-1", "0").
	 * <p>The values are intended to be valid until application shutdown only
	 * and may not be compatible between different implementations of the RatingManager interface!</p>
	 * return the given rating value converted to a suitable string
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#getValue()
	 * @see #stringToRatingValue(String)
	 */
	public String ratingValueToString(double value);
	
	
	/**
	 * Converts a string returned by <code>ratingValueToString()</code> back into a rating value.
	 * <p>If an unknown string is given, <code>Rating.NEUTRAL</code> should be returned.</p>
	 * @param str the string to be converted
	 * @return the converted rating value
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#setValue(double)
	 * @see de.uka.ipd.consensus.foundation.schema.Rating#NEUTRAL
	 * @see #ratingValueToString(double)
	 */
	public double stringToRatingValue(String str);

	
	/**
	 * With this method the RatingManager signals if ratings, once added, can be changed afterwards.
	 * @return <code>true</code> if existing ratings can be changed, <code>false</code> otherwise
	 */
	public boolean changesAllowed();

	
	/**
	 * Registers a new RatingListener.
	 * @param listener the RatingListener to be added
	 */
	public void addRatingListener(RatingListener listener);
	
	
	/**
	 * Removes a RatingListener, if it is registered with the RatingManager (otherwise this call is ignored).
	 * @param listener the RatingListener to be removed
	 */
	public void removeRatingListener(RatingListener listener);

}
