<?php
	
	include_once( PLOG_CLASS_PATH."class/dao/articles.class.php" );
	include_once( PLOG_CLASS_PATH."class/dao/model.class.php" );
	
	include_once( PLOG_CLASS_PATH."debug.php" );
	
	define( "PLUGIN_KARMA_DEFAULT_NEGATIVE_THRESHOLD", 3 );
	define( "PLUGIN_KARMA_DEFAULT_COEFFICIENT", 1.5 );
	define( "KARMA_SCORE_MODERATE_DOWN", 1 );
	define( "KARMA_SCORE_MODERATE_UP", 2 );	
	
	define( "DEBUG_ENABLED", false );
	
	/**
	 * encapsulates the scoring logic
	 *
	 * At the moment, 50% more negative votes than positive are needed to make the post
	 * "vanish" from the main page. At that point, it will only be found via the "show moderated down"
	 * posts.
	 */
	class KarmaScoreLogic 
	{
		/**
		 * returns whether the post should be moderated up, down or do nothing.
		 *
		 * @param positiveKarma
		 * @param negativeKarma
		 * @param negativeThreshold
		 * @param coef
		 * @returns KARMA_SCORE_MODERATE_DOWN if the post should be moderated down, 
		 * KARMA_SCORE_MODERATE_UP if the post should be moderated up.
		 */
		function karmaScore( $positiveKarma, $negativeKarma, $negativeThreshold = PLUGIN_KARMA_DEFAULT_NEGATIVE_THRESHOLD, $coef = PLUGIN_KARMA_DEFAULT_COEFFICIENT ) 
		{
			// the algorithm only starts working when we have at least 100 votes
			// altogether, or more. 
			// TODO: This should be made configurable!!
			if( $positiveKarma + $negativeKarma >= $negativeThreshold ) {
				if( ($coef * $positiveKarma) <= $negativeKarma )
					return KARMA_SCORE_MODERATE_DOWN;
				else
					return KARMA_SCORE_MODERATE_UP;
			}
			else
				return KARMA_SCORE_MODERATE_UP;
		}
	}
	
	/**
	 * fetches and retrieves karma scores from the database
	 */
	class KarmaScores extends Model 
	{
		function KarmaScores()
		{
			$this->Model();
		}

		/**
		 * records a vote in the database
		 *
		 * @param articleId
		 * @param blogId
		 * @param mode '1' for increasing the positive karma or '2' for increasing
		 * the negative karma
		 * @return True if successful or false otherwise
		 */
		function recordVote( $articleId, $blogId, $mode )
		{
			// load the post
			$articles = new Articles();
			$article = $articles->getBlogArticle( $articleId, $blogId );			
			
			// check if the fields exist
			if( $mode == 1 ) {
				// score a positive vote
				$fieldValue = $article->getFieldObject( "positiveKarma" );
			}
			else if( $mode == 2 ) {
				// score a negative vote
				$fieldValue = $article->getFieldObject( "negativeKarma" );
			}
			
			// get the current value of the field...
			$curValue = $fieldValue->getValue();
			// ...increase it by one...
			if( $curValue == "" ) $curValue = 0;
			$fieldValue->setValue( $curValue+1 );
			// ...and update the post in the db
			$article->setFieldObject( $fieldValue );
			
			return $articles->updateArticle( $article );
		}
		
		/**
		 * Returns all the posts that have been moderated down
		 *
		 * @param blogId The blog id
		 * @return An array of Article objects containing all the posts that have been moderated down
		 * The array might be empty if there are no moderated down posts or 'false' if there was any error
		 */
		function getModeratedDownArticles( $blogId )
		{
			$articles = new Articles();
			$modDownArticles = $articles->getBlogArticles( $blogId, -1, -1, 0, POST_STATUS_MODERATED_DOWN, 0, 0 );
			
			return $modDownArticles;
		}
		
		/** 
		 * resets the karma values for a given post
		 *
		 * @param blogId
		 * @param articleId
		 * @return true if successful or false otherwise
		 */
		function resetPostKarmaScore( $blogId, $articleId ) 
		{
			$articles = new Articles();
			$blogArticle = $articles->getBlogArticle( $articleId, $blogId );
			// get the value of the karma fields
			$positiveKarma = $blogArticle->getFieldObject( "positiveKarma" );
			$positiveKarma->setValue( "0" );
			$negativeKarma = $blogArticle->getFieldObject( "negativeKarma" );
			$negativeKarma->setValue( "0" );
			// set these fields back to the Article object
			$article->setFieldObject( $positiveKarma );
			$article->setFieldObject( $negativeKarma );
			$article->setStatus( POST_STATUS_PUBLISHED );
			
			// update the post in the db
			return $articles->updateArticle( $article );
		}
	}
	
	/**
	 * used to keep track of which users have already voted which articles
	 */
	class KarmaVoters extends Model
	{
		function KarmaVoters()
		{
			$this->Model();
		
			$this->createKarmaTables();
		}

		/**
		 * @private
		 */
		function checkTable( $tableName, $tableSchema )
		{
			$dbPrefix = $this->getPrefix();
			$tableName = $dbPrefix.$tableName;

			// create the data dictionary and create the table if necessary
			$this->_initializeDb();
            $dict = NewPDbDataDictionary( $this->_db );
            $sqlArray = $dict->ChangeTableSQL( $tableName, $tableSchema );
            $result = $dict->ExecuteSQLArray( $sqlArray );
			
			if( $result == 0 )
				$this->_desc = "There was an error initializing the tables.";
		}
		
		/**
		 * Uses ADOdb data dictionaries functionality to create the 
		 * tables needed for this plugin
		 */
		function createKarmaTables()
		{
			// create the table to keep track of the voters, so that people cannot vote
			// more than once
			$table2 = "karma_voters";
			$fields2 = "
			      article_id I(10) NOTNULL,
				  client_ip C(16) NOTNULL,
				  date T(14) DEFDATE
				  ";
			$this->checkTable( $table2, $fields2 );
			
            return true;		
		}		
		

		
		/**
		 * returns true if the given user ip has alrady tried to vote
		 *
		 * @param clientId
		 * @param articleId
		 * @return Returns true if succecssful or false otherwise
		 */
		function clientHasVoted( $clientIp, $articleId )
		{
			$query = "SELECT * FROM ".$this->getPrefix()."karma_voters
			          WHERE client_ip = '$clientIp' AND
					  article_id = $articleId";
			$this->_db->debug=false;
			$result = $this->Execute( $query );
			
			if( !$result )
				return false;
				
			if( $result->RecordCount() > 0 )
				return true;
			else
				return false;
		}
		
		/**
		 * Stores a user vote
		 */
		function storeClientVote( $clientIp, $articleId )
		{
			$query = "INSERT INTO ".$this->getPrefix()."karma_voters
			          (article_id, client_ip) VALUES
					  ($articleId, '$clientIp')";
					  
			$result = $this->Execute( $query );
			
			_debug("storing user vote ($clientIp) for blog=$blogId and article=$articleId<br/>");
			
			return $result;
		}
	}
?>