<?php

/**
 * Elefant CMS - http://www.elefantcms.com/
 *
 * Copyright (c) 2011 Johnny Broadway
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

namespace blog;

/**
 * Blog Post model.
 *
 * Fields:
 *
 * id
 * title
 * ts
 * author
 * published
 * body
 * tags
 * extra
 */
class Post extends \ExtendedModel {
	/**
	 * The database table name.
	 */
	public $table = '#prefix#blog_post';

	/**
	 * The `extra` field can contain an arbitrary number of additional
	 * user-defined properties.
	 */
	public $_extended_field = 'extra';
	
	/**
	 * Display name for this model type.
	 */
	public static $display_name = 'Blog Post';
	
	/**
	 * Plural display name for this model type.
	 */
	public static $plural_name = 'Blog Posts';
	
	/**
	 * Link format for version history.
	 */
	public static $versions_link = '/blog/edit?id={{id}}';

	/**
	 * Fields to display as links in version history.
	 */
	public static $versions_display_fields = [
		'title' => 'Title'
	];

	public static function _publish_queued ($posts) {
		if (! is_array ($posts)) {
			$posts = [];
		}

		foreach (array_keys ($posts) as $k) {
			if ($posts[$k]->published === 'que') {
				$posts[$k]->published = 'yes';
				$posts[$k]->put ();
				\Versions::add ($posts[$k]);
			}
		}

		return $posts;
	}

	/**
	 * Get the most recently published posts.
	 */
	public static function latest ($limit = 10, $offset = 0) {
		$posts = self::query ()
			->where ('published', 'yes')
			->or_where (function ($q) {
				$q->where ('published', 'que');
				$q->where ('ts <= ?', gmdate ('Y-m-d H:i:s'));
			})
			->order ('ts desc')
			->fetch ($limit, $offset);

		$posts = self::_publish_queued ($posts);
		
		return $posts;
	}

	/**
	 * Get posts by the specified author.
	 */
	public static function by ($author, $limit = 10, $offset = 0) {
		return self::query ()->where ('published', 'yes')->where ('author', $author)->order ('ts desc')->fetch ($limit, $offset);
	}

	/**
	 * Get the latest headlines only.
	 */
	public static function headlines ($limit = 10) {
		return self::query (array ('id', 'ts', 'title', 'slug'))->where ('published', 'yes')->order ('ts desc')->fetch ($limit);
	}

	/**
	 * Get posts by a certain tag.
	 */
	public static function tagged ($tag, $limit = 10, $offset = 0) {
		$ids = \DB::shift_array ('select post_id from #prefix#blog_post_tag where tag_id = ?', $tag);

		if (! is_array ($ids) || count ($ids) === 0) {
			return array ();
		}
		return self::query ()->where ('id in(' . join (',', $ids) . ')')->where ('published', 'yes')->order ('ts desc')->fetch ($limit, $offset);
	}

	/**
	 * Count posts by a certain tag.
	 */
	public static function count_by_tag ($tag, $limit = 10, $offset = 0) {
		$ids = \DB::shift_array ('select post_id from #prefix#blog_post_tag where tag_id = ?', $tag);

		if (! is_array ($ids) || count ($ids) === 0) {
			return array ();
		}

		return self::query ()->where ('id in(' . join (',', $ids) . ')')->where ('published', 'yes')->order ('ts desc')->count ();
	}

	/**
	 * Get posts by a certain year and month.
	 */
	public static function archive ($year, $month, $limit = 10, $offset = 0) {
		if (! is_numeric ($year)) {
			error_log ('Year must be numeric');
			return [];
		}
		
		if (! is_numeric ($month)) {
			error_log ('Month must be numeric');
			return [];
		}
		
		$start = $year . '-' . $month . '-01 00:00:00.000';
		$end = $year . '-' . $month . '-' . cal_days_in_month (CAL_GREGORIAN, $month, $year) . ' 23:59:59.000';
		
		return self::query ()
			->where ('ts between "' . $start . '" and "' . $end . '"')
			->where ('published', 'yes')
			->order ('ts desc')
			->fetch_orig ($limit, $offset);
	}

	/**
	 * Count posts by a certain year and month.
	 */
	public static function count_by_month ($year, $month, $limit = 10, $offset = 0) {
		if (! is_numeric ($year)) {
			error_log ('Year must be numeric');
			return 0;
		}
		
		if (! is_numeric ($month)) {
			error_log ('Month must be numeric');
			return 0;
		}
		
		$start = $year . '-' . $month . '-01 00:00:00.000';
		$end = $year . '-' . $month . '-' . self::days_in_month ($month, $year) . ' 23:59:59.000';
		
		return self::query ()
			->where ('ts between "' . $start . '" and "' . $end . '"')
			->where ('published', 'yes')
			->count ();
	}
	
	private static function days_in_month ($month, $year) {
		return date ('t', mktime (0, 0, 0, $month, 1, $year));
	}

	/**
	 * Get a list of tags and the number of posts they've been used on.
	 */
	public static function tags () {
		return \DB::pairs ('select tag_id, count(*) as posts from #prefix#blog_post_tag group by tag_id order by tag_id asc');
	}

	/**
	 * Get a list of archive years, months, and count of posts.
	 */
	public static function archive_months ($published = true, $limit = 0) {
		$db = \DB::get_connection (1);
		$dbtype = $db->getAttribute (\PDO::ATTR_DRIVER_NAME);

		$q = '';
		
		if ($published) {
			$q .= 'where published = "yes"';
		}
		
		$limit = (int) $limit;
		
		if ($limit > 0) {
			if ($q == '') {
				$q = 'where ';
			} else {
				$q .= ' and ';
			}
			
			$q .= ' ts >= "' . gmdate ('Y-m', strtotime ('-' . $limit . ' month')) . '-01 00:00:00"';
		}

		switch ($dbtype) {
			case 'pgsql':
				$res = \DB::fetch (
					'select extract(year from ts) as year, extract(month from ts) as month, count(*) as total
					 from #prefix#blog_post
					 ' . $q . '
					 group by year, month
					 order by year desc, month desc'
				);
				break;
			case 'mysql':
				$res = \DB::fetch (
					'select year(ts) as year, month(ts) as month, count(*) as total
					 from #prefix#blog_post
					 ' . $q . '
					 group by year, month
					 order by year desc, month desc'
				);
				break;
			case 'sqlite':
				$res = \DB::fetch (
					'select strftime(\'%Y\', ts) as year, strftime(\'%m\', ts) as month, count(*) as total
					 from #prefix#blog_post
					 ' . $q . '
					 group by year, month
					 order by year desc, month desc'
				);
				break;
		}
		
		foreach ($res as $k => $row) {
			$res[$k]->month = str_pad ($row->month, 2, '0', STR_PAD_LEFT);
			$res[$k]->date = $row->year . '-' . $res[$k]->month;
		}

		return $res;
	}

	/**
	 * Generate a list of pages for the sitemaps app.
	 */
	public static function sitemap () {
		$posts = self::query ()
			->where ('published', 'yes')
			->fetch_orig ();
		
		$urls = array ();
		foreach ($posts as $post) {
			if ($post->slug == '') {
				$post->slug = URLify::filter ($post->title);
				$post->put ();
			}
			
			$urls[] = '/blog/post/' . $post->id . '/' . $post->slug;
		}
		return $urls;
	}

	/**
	 * Generate a list of posts for the search app,
	 * and add them directly via `Search::add()`.
	 */
	public static function search () {
		$posts = self::query ()
			->where ('published', 'yes')
			->fetch ();
		
		foreach ($posts as $i => $post) {
			if ($post->slug == '') {
				$post->slug = URLify::filter ($post->title);
				$post->put ();
			}
			
			$url = 'blog/post/' . $post->id . '/' . $post->slug;
			if (! \Search::add (
				$url,
				array (
					'title' => $post->title,
					'text' => $post->body,
					'url' => '/' . $url
				)
			)) {
				return array (false, $i);
			}
		}
		return array (true, count ($posts));
	}
}
