<?php

	include_once( PLOG_CLASS_PATH."class/dao/model.class.php" );
	include_once( PLOG_CLASS_PATH."class/dao/articles.class.php" );	
	include_once( PLOG_CLASS_PATH."class/dao/articlecommentstatus.class.php" );
	include_once( PLOG_CLASS_PATH."class/dao/daocacheconstants.properties.php" );	

	/** 
	 * different orders that comments can have
	 */
	define( "COMMENT_ORDER_OLDEST_FIRST", 1 );
	define( "COMMENT_ORDER_NEWEST_FIRST", 2 );
	
	/**
	 * whether we'd like to fetch a comment, trackback or anything
	 */
	define( "COMMENT_TYPE_COMMENT", 1 );
	define( "COMMENT_TYPE_TRACKBACK", 2 );	
	define( "COMMENT_TYPE_ANY", -1 );
	
    /**
	 * \ingroup DAO
	 *
     * Since comments and trackbacks are now in the same table, this class contains all the
	 * common code needed to deal with these items. Most of the methods are exactly the same in both
	 * ArticleComments and Trackbacks except that they take an additional parameter called 'status'
	 * which can either be
	 *
	 * - COMMENT_TYPE_COMMENT
	 * - COMMENT_TYPE_TRACKBACK
	 * - COMMENT_TYPE_ANY
	 *
	 * Depending on whether we'd like to retrieve a trackback or a comment.
     */
	class CommentsCommon extends Model 
	{

		var $_blogSettings;
		var $blogSettings;
		var $timeDiff;

    	function CommentsCommon()
        {
        	$this->Model();
			$this->table = $this->getPrefix()."articles_comments";
        }

        /**
         * Adds a comment to an article
		 *
		 * @param comment the UserComment object that we're going to add.
		 * @return Returns true if successful or false if error. Also in case of success, it will modify the UserComment
		 * object passed by reference and include its new id.
         */
		function addComment( &$comment ) 
        {
        	if(( $result = $this->add( $comment ))) {
        		include_once( PLOG_CLASS_PATH."class/dao/articles.class.php" );
				$this->_cache->removeData( $comment->getArticleId(), CACHE_ARTICLE_COMMENTS_BYARTICLE );
				// update the article comments
				$article = $comment->getArticle();
				$blog = $article->getBlogInfo();
				if( $comment->getType() == COMMENT_TYPE_COMMENT ) {
        			$article->setNumComments( $this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                          'article_id = '.$article->getId().
                                                                  ' AND status = '.COMMENT_STATUS_NONSPAM.
        		        	                                      ' AND type = '.$comment->getType()));
        			$article->setTotalComments($this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                 'article_id = '.$article->getId().' AND type = '.$comment->getType()));
					$blog->setTotalComments($this->getNumItems( $this->getPrefix().'articles_comments', 
					                                            'blog_id = '.$blog->getId().' AND type = '.$comment->getType()));
				}
				else {
        			$article->setNumTrackbacks( $this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                          'article_id = '.$article->getId().
                                                                  ' AND status = '.COMMENT_STATUS_NONSPAM.
        		        	                                      ' AND type = '.$comment->getType()));
        			$article->setTotalTrackbacks($this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                 'article_id = '.$article->getId().' AND type = '.$comment->getType()));
					$blog->setTotalTrackbacks($this->getNumItems( $this->getPrefix().'articles_comments', 
					                                              'blog_id = '.$blog->getId().' AND type = '.$comment->getType()));					
				}
				$articles = new Articles();
				$articles->updateArticle( $article );
				$blogs = new Blogs();
				$blogs->updateBlog( $blog );
        	}
        	return( $result );
        }

		/**
		 * Retrieves all the comments for a post
		 *
		 * @param artid The article identifier
		 * @param order The order in which comments should be retrieved
		 * @param status The status that the comment should have, use COMMENT_STATUS_ALL for
		 * all possible statuses
		 * @param page
		 * @param itemsPerPage
		 * @return False if error or an array of ArticleComments objects
		 */
		function getPostComments( $artid, 
		                          $order = COMMENT_ORDER_NEWEST_FIRST, 
		                          $status = COMMENT_STATUS_ALL, 
		                          $type = COMMENT_TYPE_ANY, 
		                          $page = -1, 
		                          $itemsPerPage = DEFAULT_ITEMS_PER_PAGE )
		{
			if( $order == COMMENT_ORDER_NEWEST_FIRST )
				$order = Array( "date" => "DESC" );
			else
				$order = Array( "date" => "ASC" );
		
			$comments = $this->getMany( "article_id",
			                            $artid,
			                            CACHE_ARTICLE_COMMENTS_BYARTICLE,
			                            Array(),
			                            $order,
										"",
			                            $page,
			                            $itemsPerPage );			                           
			                            
			$result = Array();
		
			if( $comments ) {
				// load the post to get the blog in order to get the time difference
				$articles = new Articles();
				$article = $articles->getArticle( $artid );
				$blog = $article->getBlogInfo();
				$blogSettings = $blog->getSettings();
				$timeDiff = $blogSettings->getValue( "time_offset" );
				include_once( PLOG_CLASS_PATH."class/data/timestamp.class.php" );
				foreach( $comments as $comment ) {
					if( $this->check( $comment, $type, $status )) {
						$date = Timestamp::getDateWithOffset( $comment->getDate(), $timeDiff );
						$comment->setDate( $date );
						$result[] = $comment;
					}
				}
			}
			else
				$result = Array();
				
				
			return( $result );			               
		}
		
		/**
		 * @param comment
		 * @param type
		 * @param status
		 * @private
		 */
		function check( $comment, $type, $status )
		{
			if( $type != COMMENT_TYPE_ANY ) {
				if( $comment->getType() != $type ) {
					return false;
				}
			}
					
			if( $status != COMMENT_STATUS_ALL )
				if( $comment->getStatus() != $status )
					return false;
				
			return( true );
		}
		
        /**
         * Returns the total number of comments for a post
		 *
		 * @param artId the post id
		 * @param status
		 * @return The number of comments
         */
        function getNumPostComments( $artId, $status = COMMENT_STATUS_ALL, $type = COMMENT_TYPE_ANY )
        {
        	include_once( PLOG_CLASS_PATH."class/dao/articles.class.php" );
        	$numComments = 0;
			$articles = new Articles();
			$article = $articles->getArticle( $artId );        	
        
			if( $type == COMMENT_TYPE_COMMENT ) {
				if( $status == COMMENT_STATUS_ALL && $article ) {
					$numComments = $article->getTotalComments();
				}
				elseif( $status == COMMENT_STATUS_NONSPAM && $article ) {
					$numComments = $article->getNumComments();
				}
				else {
					$numComments = $this->getPostComments( $artId, COMMENT_ORDER_NEWEST_FIRST, $status, $type );
				}
			}
			else {
				if( $status == COMMENT_STATUS_ALL && $article ) {
					$numComments = $article->getTotalTrackBacks();
				}
				elseif( $status == COMMENT_STATUS_NONSPAM && $article ) {
					$numComments = $article->getNumTrackBacks();
				}
				else {
					$numComments = $this->getPostComments( $artId, COMMENT_ORDER_NEWEST_FIRST, $status, $type );
				}			
			}
			
			return( $numComments );
        }
        
        /**
         * returns the number of comments that a blog has
         *
         * @param blogId
         * @param status
         * @param type
		 * @param searchTerms
         * @return The number of comments, or 0 if error or no comments
         */
        function getNumBlogComments( $blogId, $status = COMMENT_STATUS_ALL, $type = COMMENT_TYPE_ANY, $searchTerms = "" )
        {
        	if( $status == COMMENT_STATUS_ALL && $type != COMMENT_TYPE_ANY && $searchTerms == "" ) {
        		// fast case, we can load the blog and query one of its intrinsic fields
        		include_once( PLOG_CLASS_PATH."class/dao/blogs.class.php" );
        		$blogs = new Blogs();
        		$blogInfo = $blogs->getBlogInfo( $blogId );
        		if( !$blogInfo )
        			$numComments = 0;
        		else {
        			if( $type == COMMENT_TYPE_COMMENT )
	        			$numComments = $blogInfo->getTotalComments();
	        		else
	        			$numComments = $blogInfo->getTotalTrackbacks();
        		}
        	}
        	else {			
				// create the table name
				$prefix = $this->getPrefix();
				$table = "{$prefix}articles_comments c";
				// and the condition if any...
				$cond = "c.blog_id = '".Db::qstr($blogId)."'";
				if( $status != COMMENT_STATUS_ALL )
					$cond .= " AND c.status = '".Db::qstr($status)."'";
				if( $type != COMMENT_TYPE_ANY )
					$cond .= " AND c.type = '".Db::qstr($type)."'";
                if( $searchTerms != "" )
                    $cond .= " AND ".$this->getSearchConditions( $searchTerms );
                    //print("type = ".$type." - cond = $cond");
				$numComments = $this->getNumItems( $table, $cond );
        	}
        	
        	return( $numComments );
        }

		/**
		 * Removes a comment from a post. It also updates all the other posts that
         * have this one as the parent and makes them look as if they were 'top level'
         * comments with no parent.
         *
         * @param artid The article identifier.
         * @param commentid The comment identifier.
		 */
		function deleteComment( $commentid )
		{
			$comment = $this->getComment( $commentid );
			if( $comment )  {
				$this->delete( "id", $commentid );
	            // update all the other posts
    	        $query = "UPDATE ".$this->getPrefix()."articles_comments SET parent_id = 0 WHERE parent_id = '".
				         Db::qstr($commentid)."' AND article_id = '".
						 Db::qstr( $comment->getArticleId())."'";
        	    $result = $this->Execute( $query );
        	    $this->_cache->removeData( $comment->getArticleId(), CACHE_ARTICLE_COMMENTS_BYARTICLE );
        	    $this->_cache->removeData( $comment->getId(), CACHE_ARTICLE_COMMENTS );
        	    
        	    // update the blog and the article counters
        	    include_once( PLOG_CLASS_PATH."class/dao/blogs.class.php" );
        	    include_once( PLOG_CLASS_PATH."class/dao/articles.class.php" );

        	    $article = $comment->getArticle();
        	    $blog = $article->getBlogInfo();
        	    $type = $comment->getType();
        	    if( $type == COMMENT_TYPE_COMMENT ) {
        			$article->setNumComments( $this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                          'article_id = '.$article->getId().' AND status = '.COMMENT_STATUS_NONSPAM.
        		        	                                      ' AND type = '.$comment->getType()));
        			$article->setTotalComments( $this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                            'article_id = '.$article->getId().' AND type = '.$comment->getType()));
					$blog->setTotalComments($this->getNumItems( $this->getPrefix().'articles_comments', 
					                                            'blog_id = '.$blog->getId().' AND type = '.$comment->getType()));
	        	}
	        	else {
        			$article->setNumTrackbacks( $this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                          'article_id = '.$article->getId().' AND status = '.COMMENT_STATUS_NONSPAM.
        		        	                                      ' AND type = '.$comment->getType()));
        			$article->setTotalTrackbacks($this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                          'article_id = '.$article->getId().' AND type = '.$comment->getType()));
					$blog->setTotalTrackbacks($this->getNumItems( $this->getPrefix().'articles_comments',
   					                                              'blog_id = '.$blog->getId().' AND type = '.$comment->getType()));
	        	}
        	    $blogs = new Blogs();
	        	$blogs->updateBlog( $blog );
        	    $articles = new Articles();
        	    $articles->updateArticle( $article );        	    
			}
			else {
				return false;
			}
				
			return( true );
		}		

        /**
         * Updates a comment. It also takes into account status changes and updates counters in
         * the blogs and articles table accordingly.
         *
         * @param comment An UserComment object
         * @return true if update successful or false otherwise.
         */
        function updateComment( $comment )
        {    
        	if(($result = $this->update( $comment ))) {
	        	// reset the cache
        		$this->_cache->removeData( $comment->getArticleId(), CACHE_ARTICLE_COMMENTS_BYARTICLE );
        		$this->_cache->removeData( $comment->getId(), CACHE_ARTICLE_COMMENTS );
        		// update counters in the articles table according to the status
        		include_once( PLOG_CLASS_PATH."class/dao/articles.class.php" );
        		$articles = new Articles();
        		// load the article
        		$article = $comment->getArticle();
        		if( $comment->getType() == COMMENT_TYPE_COMMENT ) {
        			$article->setNumComments( $this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                          'article_id = '.$article->getId().' AND status = '.COMMENT_STATUS_NONSPAM.
        		        	                                      ' AND type = '.$comment->getType()));
        			$article->setTotalComments( $this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                          'article_id = '.$article->getId().' AND type = '.$comment->getType()));
    			}
    			elseif( $comment->getType() == COMMENT_TYPE_TRACKBACK ) {
        			$article->setNumTrackbacks( $this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                          'article_id = '.$article->getId().' AND status = '.COMMENT_STATUS_NONSPAM.
        		        	                                      ' AND type = '.$comment->getType()));
        			$article->setTotalTrackbacks( $this->getNumItems( $this->getPrefix().'articles_comments', 
        		    	                                          'article_id = '.$article->getId().' AND type = '.$comment->getType()));
    			}
    			$articles->updateArticle( $article );
        	}
        	
        	return( $result );
        }

        /**
         * removes all comments marked as spam from the database
		 *
		 * @param type
         */
        function purgeSpamComments( $type = COMMENT_TYPE_ANY )
        {
			// load the ids of the articles whose spam comments we're going to purge
			$query = "SELECT article_id, type, COUNT(*) AS total_spam FROM ".$this->getPrefix()."articles_comments 
			          WHERE status = ".COMMENT_STATUS_SPAM;
			if( $type != COMMENT_TYPE_ANY )
				$query .= " AND type = '".Db::qstr($type)."'";
			$query .= " GROUP BY article_id, type";
			$resultArticles = $this->Execute( $query );				
			
			// delete the spam comments
        	$query = "DELETE FROM ".$this->getPrefix()."articles_comments 
			          WHERE status = ".COMMENT_STATUS_SPAM;
			if( $type != COMMENT_TYPE_ANY )
				$query .= " AND type = '".Db::qstr($type)."'";
            if( !$this->Execute( $query ))
				return false;
				
			// and update article counters of comments
			include_once( PLOG_CLASS_PATH."class/dao/articles.class.php" );
			include_once( PLOG_CLASS_PATH."class/dao/blogs.class.php" );			
			$articles = new Articles();
			$blogs = new Blogs();
			while( $row = $resultArticles->FetchRow()) {
				$articleId = $row["article_id"];
				$type = $row["type"];
				$spam = $row["total_spam"];
				
				// update article counters
				$article = $articles->getArticle( $articleId );
				if( $article ) {
					$blog = $article->getBlogInfo();
					if( $type == COMMENT_TYPE_COMMENT ) {
		    			$article->setNumComments( $this->getNumItems( $this->getPrefix().'articles_comments', 
		    		    	                                          'article_id = '.$article->getId().
		                                                              ' AND status = '.COMMENT_STATUS_NONSPAM.
		    		        	                                      ' AND type = '.$type ));
		    			$article->setTotalComments($this->getNumItems( $this->getPrefix().'articles_comments', 
		    		    	                                           'article_id = '.$article->getId().
		                                                               ' AND type = '.$type ));
						$blog->setTotalComments($this->getNumItems( $this->getPrefix().'articles_comments', 
						                                            'blog_id = '.$blog->getId().' AND type = '.$type ));
					}
					else {
	        			$article->setNumTrackbacks( $this->getNumItems( $this->getPrefix().'articles_comments', 
	        		    	                                          'article_id = '.$article->getId().
	                                                                  ' AND status ='.COMMENT_STATUS_NONSPAM.
	        		        	                                      ' AND type = '.$type ));
	        			$article->setTotalTrackbacks($this->getNumItems( $this->getPrefix().'articles_comments', 
	        		    	                                          'article_id = '.$article->getId().
	                                                                  ' AND type = '.$type ));
						$blog->setTotalTrackbacks($this->getNumItems( $this->getPrefix().'articles_comments',
	   					                                              'blog_id = '.$blog->getId().' AND type = '.$type ));
					}
						
					// update the article
					$articles->updateArticle( $article );
					$blogs->updateBlog( $blog );
					// and clean the cache data
					$this->_cache->removeData( $articleId, CACHE_ARTICLE_COMMENTS_BYARTICLE );					
				}
			}
			
			return( true );			
        }		
		
		/**
		 * returns a single comment, identified by its identifier
		 *
		 * @param type
		 */
		function getComment( $id, $type = COMMENT_TYPE_ANY )
		{
			$comment = $this->get( "id" , $id, CACHE_ARTICLE_COMMENTS );
			if( !$comment )
				return false;
			if( $type != COMMENT_TYPE_ANY ) {
				if( $comment->getType() != $type ) {
					return false;
				}
			}
				
			return( $comment );
		}
		
		/**
		 * returns the comments received in the blog
		 *
		 * @param blogId
		 * @param order
		 * @param status
		 * @param type
		 * @param searchTerms
		 * @param page
		 * @param itemsPerPage
		 * @return An array of ArticleComment objects
		 */
		function getBlogComments( $blogId, 
		                          $order = COMMENT_ORDER_NEWEST_FIRST, 
								  $status = COMMENT_STATUS_ALL, 
								  $type = COMMENT_TYPE_ANY, 
								  $searchTerms = "", 
								  $page = -1, 
								  $itemsPerPage = DEFAULT_ITEMS_PER_PAGE )
		{
			/**
			 * :TODO:
			 * - implement this method in a better way!
			 */
			$prefix = $this->getPrefix();
			$query = "SELECT c.*
			          FROM {$prefix}articles_comments c
			          WHERE c.blog_id = '".Db::qstr( $blogId )."'";
            if( $status != COMMENT_STATUS_ALL )
            	$query .= " AND status = $status";
			if( $type != COMMENT_TYPE_ANY )
				$query .= " AND type = '".Db::qstr($type)."'";
			if( $searchTerms != "" ) {
				$query .= " AND ".$this->getSearchConditions( $searchTerms );
			}

			// check in which order we should display those comments
			if( $order == COMMENT_ORDER_NEWEST_FIRST )
				$query .= " ORDER BY date DESC";
			else
				$query .= " ORDER BY date ASC";
			
			$result = $this->Execute( $query, $page, $itemsPerPage );
			
			if( !$result )
				return false;
				
			if( $result->RowCount() == 0 ){
                $result->Close();			
				return Array();
            }
				
			$comments = Array();
			$articles = new Articles();
			while( $row = $result->FetchRow()) {
				$comments[] = $this->mapRow( $row );
			}
            $result->Close();			

			/*if( $page > -1 && $amount > -1 )
				$comments = array_slice( $comments,	($page-1) * $amount, $amount );*/
        				 
			return( $comments );
		}
		
		/**
		 * @private
		 * @see Model::mapRow()
		 */
		function mapRow( $row )
		{
            require_once( PLOG_CLASS_PATH."class/dao/usercomment.class.php" );
            require_once( PLOG_CLASS_PATH."class/data/timestamp.class.php" );

            $prefix = $this->getPrefix();
            $date = $row["date"];
            $articleId = $row["article_id"];
            $blogId = $row["blog_id"];
            
            $blogs =  new Blogs();
            $blogInfo = $blogs->getBlogInfo( $blogId );
            $blogSettings = $blogInfo->getSettings();
            $timeDiff = $blogSettings->getValue( "time_offset" );
            $date = Timestamp::getDateWithOffset( $date, $timeDiff );

        	$comment = new UserComment( $row["article_id"],
        	                            $row['blog_id'],
            							$row["parent_id"],
                                        $row["topic"],
                                        $row["text"],
                                        $date,
                                        $row["user_name"],
                                        $row["user_email"],
                                        $row["user_url"],
                                        $row["client_ip"],
                                        $row["spam_rate"],
                                        $row["status"],
                                        $row["id"] );
										
			// set the normalized text and topic
			$comment->setNormalizedText( $row['normalized_text'] );
			$comment->setNormalizedTopic( $row['normalized_topic'] );
			$comment->setType( $row['type'] );

            return $comment;
		}
		
		/**
		 * @see Model::getSearchConditions
		 *
		 * @param searchTerms
		 */
		function getSearchConditions( $searchTerms )
		{
			include_once( PLOG_CLASS_PATH."class/dao/searchengine.class.php" );
			
			$query = SearchEngine::adaptSearchString($searchTerms);

            // MARKWU: I also need to take care when there are multiplu search term

            // Split the search term by space
            $query_array = explode(' ',$query);
		
            // For each search terms, I should make a like query for it   
            $where_string = "(";
            $where_string .= "((c.normalized_topic LIKE '%{$query_array[0]}%') OR (c.normalized_text LIKE '%{$query_array[0]}%'))";
            for ( $i = 1; $i < count($query_array); $i = $i + 1) {
                $where_string .= " AND ((c.normalized_topic LIKE '%{$query_array[$i]}%') OR (c.normalized_text LIKE '%{$query_array[$i]}%'))";
            }
            $where_string .= " OR ((c.normalized_topic LIKE '%{$query}%') OR (c.normalized_text LIKE '%{$query}%'))";
            $where_string .= ")";

			return( $where_string );		
		}
	}
?>
