//
// C++ Interface: complexscorecalculationstrategy
//
// Description: 
//
//
// Author: Benjamin Mesing <bensmail@gmx.net>, (C) 2005
//
// Copyright: See COPYING file that comes with this distribution
//
//
#ifndef __NAPT_COMPLEXSCORECALCULATIONSTRATEGY_H_2005_08_06
#define __NAPT_COMPLEXSCORECALCULATIONSTRATEGY_H_2005_08_06

#include <aptsearchscorecalculationstrategy.h>

namespace NApt {

class IPackageDB;
class IPackage;

/** @brief Calculates the scores for each package.
  *
  * The scores will be calculated based on the search of the patterns in the 
  * names and descriptions of the packages. 
  *
  * For each package, a score based on the names and one based on the description 
  * is calculated, each being being in [0..1]. They are combined by as a weighted 
  * average, where the name scores are weighted with 3, and the description scores
  * with 1.
  *
  * The include patterns are searched in the package descriptions and the package names.
  * Each type of match is assigned a score, where a Scores::DIRECT_CS_MATCH
  * gives the highest score, and Scores::INNER_MATCH the lowest.
  *
  * For the name scores, for each include pattern the highest match type is determined and 
  * those values are accumulated (a no match means 0). For the description scores, 
  * all matches are accumulated and divided by the number of characters.
  * Finally both, description and name scores, will be normalized and combined
  * as described above.
  *
  * @author Benjamin Mesing
  */
class ComplexScoreCalculationStrategy : public AptSearchScoreCalculationStrategy
{
	struct Scores
	{
		/** @brief Scores if the search pattern equals the package name (case sensitive).
			*
			* Only used in descriptions.
			*/
		static const float DIRECT_CS_MATCH;
		/** @brief Scores if the search pattern equals the package name (case insensitive).
			*
			* Only used in descriptions.
			*/
		static const float DIRECT_MATCH;
		/** @brief Scores if the search pattern matches a whole word in the package name or
			* description (case sensitive).
			*/
		static const float WHOLE_CS_WORD;
		/** @brief Scores if the search pattern matches a whole word in the package name or
			* description (case insensitive).
			*/
		static const float WHOLE_WORD;
		/** @brief Scores if the search pattern matches the borders of a word in the package 
			* name or description (case insensitive).
			*
			* Example: <br>
			* Search pattern: apt<br>
			* Word: <b>apt</b>itude
			*/
		static const float BORDER_MATCH;
		/** @brief Scores if the search pattern matches inside a word.
			*
			* Example: <br>
			* Search pattern: apt<br>
			* Word: l<b>apt</b>op
			*/
		static const float INNER_MATCH;
	};
	/** @brief Holds the interface used to access the package database directly. */
	IPackageDB* _pPackageDb;
public:
	ComplexScoreCalculationStrategy(IPackageDB* pPackageDb);

	virtual ~ComplexScoreCalculationStrategy();


	/** @name IScoreCalculationStrategy interface
	  * 
	  * Implementation of the IScoreCalculationStrategy interface
	  */
	//@{
	/** @brief Calculates the scores for the handed set of packages.
	  *
	  * Old calculations will be cleared.
	  * @pre _includePatterns.size() != 0
	  * @see setIncludePatterns()
	  */
	virtual void calculateScore(const set<string>& packages);
	//@}
	float getNameScore(const IPackage& package, const QString& pattern) const;
	float getDescriptionScore(const IPackage& package, const QString& pattern) const;

protected:
	/** Information used to calculate the scores for a package. */
	struct ScoreInformation
	{
	public:
		ScoreInformation(const string& package)	
		{
			_package = package;
			_nameScore = 0;
			_descriptionScore = 0;
		}
		
		const string& package() const	{ return _package; }
		float getNameScore() const	{ return _nameScore; }
		/** Accumulated matches divided by the number of characters in the description. */
		float getDescriptionScore() const	{ return _descriptionScore; }
		void clearScore();
		void addNameScore(float score)	{ _nameScore += score; }
		void addDescriptionScore(float score)	
		{ 
			_descriptionScore += score; 
			if (_descriptionScore > _maximumDescriptionScore)
				_maximumDescriptionScore = _descriptionScore;
		}
		static void clearMaximumDescriptionScore()	{ _maximumDescriptionScore = 0; }
		static float getMaximumDescriptionScore()	{ return  _maximumDescriptionScore; }
	private:
	
		string _package;
		/** @brief The score calculated for the name. */
		float _nameScore;
		/** @brief The score calculated for the description. */
		float _descriptionScore;
		/** @brief Holds the maximum description scores reached. */
		static float _maximumDescriptionScore;
	};

	/** @brief Structure to count the matches for one word in a text.
	  *
	  * Meaning of members as in Scores.
	  */
	struct Matches
	{
		int wholeWordCsMatches;
		int wholeWordMatches;
		int borderMatches;
		int innerMatches;
		Matches() :
			wholeWordCsMatches(0),
			wholeWordMatches(0), 
			borderMatches(0),
			innerMatches(0)
		{
		}
	};
	
	/** @brief Searchs for pattern in string.
	  *
	  * @returns the result of the search as Matches structure.
	  */
	Matches findMatches(const QString& string, const QString& pattern) const;
	
	
	/** @brief Returns the score information for the handed package. 
	  *
	  * @throws NPlugin::PackageNotFoundException if no data was available for the handed package.
	  */
	ScoreInformation getScoreInformation(const string& package, bool cs) const;

};

}

#endif	// __NAPT_COMPLEXSCORECALCULATIONSTRATEGY_H_2005_08_06
