<?php
/*****************************************************************************
 * Tag.php
 * Contains key class Tag.
 *
 * Kabuki CMS (C) 2013-2015, Aaron van Geffen
 *****************************************************************************/

class Tag
{
	public $id_tag;
	public $id_parent;
	public $id_asset_thumb;
	public $id_user_owner;
	public $tag;
	public $slug;
	public $description;
	public $kind;
	public $count;

	protected function __construct(array $data)
	{
		foreach ($data as $attribute => $value)
			$this->$attribute = $value;
	}

	public static function fromId($id_tag, $return_format = 'object')
	{
		$db = Registry::get('db');

		$row = $db->queryAssoc('
			SELECT *
			FROM tags
			WHERE id_tag = {int:id_tag}',
			[
				'id_tag' => $id_tag,
			]);

		// Tag not found?
		if (empty($row))
			throw new NotFoundException();

		return $return_format === 'object' ? new Tag($row) : $row;
	}

	public static function fromSlug($slug, $return_format = 'object')
	{
		$db = Registry::get('db');

		$row = $db->queryAssoc('
			SELECT *
			FROM tags
			WHERE slug = {string:slug}',
			[
				'slug' => $slug,
			]);

		// Tag not found?
		if (empty($row))
			throw new NotFoundException();

		return $return_format === 'object' ? new Tag($row) : $row;
	}

	public static function getAll($limit = 0, $return_format = 'array')
	{
		$rows = Registry::get('db')->queryAssocs('
			SELECT *
			FROM tags
			ORDER BY ' . ($limit > 0 ? 'count
			LIMIT {int:limit}' : 'tag'),
			[
				'limit' => $limit,
			]);

		// No tags found?
		if (empty($rows))
			return [];

		// Limited? Make sure the lot is sorted alphabetically.
		if (!empty($limit))
		{
			usort($rows, function($a, $b) {
				return strcmp($a['tag'], $b['tag']);
			});
		}

		if ($return_format === 'object')
		{
			$return = [];
			foreach ($rows as $row)
				$return[$row['id_tag']] = new Tag($row);
			return $return;
		}
		else
			return $rows;
	}

	public static function getAllByOwner($id_user_owner)
	{
		$db = Registry::get('db');
		$res = $db->query('
			SELECT *
			FROM tags
			WHERE id_user_owner = {int:id_user_owner}
			ORDER BY tag',
			[
				'id_user_owner' => $id_user_owner,
			]);

		$objects = [];
		while ($row = $db->fetch_assoc($res))
			$objects[$row['id_tag']] = new Tag($row);

		return $objects;
	}

	public static function getAlbums($id_parent = 0, $offset = 0, $limit = 24, $return_format = 'array')
	{
		$rows = Registry::get('db')->queryAssocs('
			SELECT *
			FROM tags
			WHERE id_parent = {int:id_parent} AND kind = {string:kind}
			ORDER BY tag ASC
			LIMIT {int:offset}, {int:limit}',
			[
				'id_parent' => $id_parent,
				'kind' => 'Album',
				'offset' => $offset,
				'limit' => $limit,
			]);

		if ($return_format === 'object')
		{
			$return = [];
			foreach ($rows as $row)
				$return[$row['id_tag']] = new Tag($row);
			return $return;
		}
		else
			return $rows;
	}

	public static function getPeople($id_parent = 0, $offset = 0, $limit = 24, $return_format = 'array')
	{
		$rows = Registry::get('db')->queryAssocs('
			SELECT *
			FROM tags
			WHERE id_parent = {int:id_parent} AND kind = {string:kind}
			ORDER BY tag ASC
			LIMIT {int:offset}, {int:limit}',
			[
				'id_parent' => $id_parent,
				'kind' => 'Person',
				'offset' => $offset,
				'limit' => $limit,
			]);

		if ($return_format === 'object')
		{
			$return = [];
			foreach ($rows as $row)
				$return[$row['id_tag']] = new Tag($row);
			return $return;
		}
		else
			return $rows;
	}

	public static function byAssetId($id_asset, $return_format = 'object')
	{
		$rows = Registry::get('db')->queryAssocs('
			SELECT *
			FROM tags
			WHERE id_tag IN(
				SELECT id_tag
				FROM assets_tags
				WHERE id_asset = {int:id_asset}
			)
			ORDER BY count DESC',
			[
				'id_asset' => $id_asset,
			]);

		// No tags found?
		if (empty($rows))
			return [];

		if ($return_format === 'object')
		{
			$return = [];
			foreach ($rows as $row)
				$return[$row['id_tag']] = new Tag($row);
			return $return;
		}
		else
			return $rows;
	}

	public static function byPostId($id_post, $return_format = 'object')
	{
		$rows = Registry::get('db')->queryAssocs('
			SELECT *
			FROM tags
			WHERE id_tag IN(
				SELECT id_tag
				FROM posts_tags
				WHERE id_post = {int:id_post}
			)
			ORDER BY count DESC',
			[
				'id_post' => $id_post,
			]);

		// No tags found?
		if (empty($rows))
			return [];

		if ($return_format === 'object')
		{
			$return = [];
			foreach ($rows as $row)
				$return[$row['id_tag']] = new Tag($row);
			return $return;
		}
		else
			return $rows;
	}

	public static function recount(array $id_tags = [])
	{
		return Registry::get('db')->query('
			UPDATE tags AS t SET count = (
				SELECT COUNT(*)
				FROM `assets_tags` AS at
				WHERE at.id_tag = t.id_tag
			)' . (!empty($id_tags) ? '
			WHERE t.id_tag IN({array_int:id_tags})' : ''),
			['id_tags' => $id_tags]);
	}

	public static function createNew(array $data, $return_format = 'object')
	{
		$db = Registry::get('db');

		if (!isset($data['id_parent']))
			$data['id_parent'] = 0;

		if (!isset($data['description']))
			$data['description'] = '';

		if (!isset($data['count']))
			$data['count'] = 0;

		$res = $db->query('
			INSERT IGNORE INTO tags
			(id_parent, tag, slug, kind, description, count)
			VALUES
			({int:id_parent}, {string:tag}, {string:slug}, {string:kind}, {string:description}, {int:count})
			ON DUPLICATE KEY UPDATE count = count + 1',
			$data);

		if (!$res)
			trigger_error('Could not create the requested tag.', E_USER_ERROR);

		$data['id_tag'] = $db->insert_id();
		return $return_format === 'object' ? new Tag($data) : $data;
	}

	public function getUrl()
	{
		return BASEURL . '/' . $this->slug . '/';
	}

	public function save()
	{
		return Registry::get('db')->query('
			UPDATE tags
			SET
				id_parent = {int:id_parent},
				id_asset_thumb = {int:id_asset_thumb},' . (isset($this->id_user_owner) ? '
				id_user_owner = {int:id_user_owner},' : '') . '
				tag = {string:tag},
				slug = {string:slug},
				description = {string:description},
				count = {int:count}
			WHERE id_tag = {int:id_tag}',
			get_object_vars($this));
	}

	public function delete()
	{
		$db = Registry::get('db');

		$res = $db->query('
			DELETE FROM assets_tags
			WHERE id_tag = {int:id_tag}',
			[
				'id_tag' => $this->id_tag,
			]);

		if (!$res)
			return false;

		return $db->query('
			DELETE FROM tags
			WHERE id_tag = {int:id_tag}',
			[
				'id_tag' => $this->id_tag,
			]);
	}

	public function resetIdAsset()
	{
		$db = Registry::get('db');
		$new_id = $db->queryValue('
			SELECT MAX(id_asset) as new_id
			FROM assets_tags
			WHERE id_tag = {int:id_tag}',
			[
				'id_tag' => $this->id_tag,
			]);

		return $db->query('
			UPDATE tags
			SET id_asset_thumb = {int:new_id}
			WHERE id_tag = {int:id_tag}',
			[
				'new_id' => $new_id ?? 0,
				'id_tag' => $this->id_tag,
			]);
	}

	public static function match($tokens)
	{
		if (!is_array($tokens))
			$tokens = explode(' ', $tokens);

		return Registry::get('db')->queryPair('
			SELECT id_tag, tag
			FROM tags
			WHERE LOWER(tag) LIKE {string:tokens}
			ORDER BY tag ASC',
			['tokens' => '%' . strtolower(implode('%', $tokens)) . '%']);
	}

	public static function matchPeople($tokens)
	{
		if (!is_array($tokens))
			$tokens = explode(' ', $tokens);

		return Registry::get('db')->queryPairs('
			SELECT id_tag, tag, slug
			FROM tags
			WHERE LOWER(tag) LIKE {string:tokens} AND
				kind = {string:person}
			ORDER BY tag ASC',
			[
				'tokens' => '%' . strtolower(implode('%', $tokens)) . '%',
				'person' => 'Person',
			]);
	}

	public static function exactMatch($tag)
	{
		if (!is_string($tag))
			throw new InvalidArgumentException('Expecting a string!');

		return Registry::get('db')->queryPair('
			SELECT id_tag, tag
			FROM tags
			WHERE tag = {string:tag}',
			['tag' => $tag]);
	}

	public static function matchSlug($slug)
	{
		if (!is_string($slug))
			throw new InvalidArgumentException('Expecting a string!');

		return Registry::get('db')->queryValue('
			SELECT id_tag
			FROM tags
			WHERE slug = {string:slug}',
			['slug' => $slug]);
	}

	public static function matchAll(array $tags)
	{
		return Registry::get('db')->queryPair('
			SELECT tag, id_tag
			FROM tags
			WHERE tag IN ({array_string:tags})',
			['tags' => $tags]);
	}

	public static function getCount($only_active = 1, $kind = '')
	{
		$where = [];
		if ($only_active)
			$where[] = 'count > 0';
		if (!empty($kind))
			$where[] = 'kind = {string:kind}';

		if (!empty($where))
			$where = 'WHERE ' . implode(' AND ', $where);
		else
			$where = '';

		return Registry::get('db')->queryValue('
			SELECT COUNT(*)
			FROM tags ' . $where,
			['kind' => $kind]);
	}

	public function __toString()
	{
		return $this->tag;
	}
}