<?php
/**
 * meta
 *
 * Simple hierarchial data management
 *
 * @package		meta
 * @author 		Timothy J. Warren
 * @copyright	Copyright (c) 2012
 * @link 		https://github.com/aviat4ion/meta
 * @license 	http://philsturgeon.co.uk/code/dbad-license
 */

// --------------------------------------------------------------------------

namespace Meta\Model;
use \Meta\Base\db;

/**
 * Main Model for DB interaction
 */
class Data extends \Meta\Base\Model {

	/**
	 * Reference to database connection
	 *
	 * @var Query_Builder
	 */
	protected $db;

	/**
	 * Reference to session
	 *
	 * @var Session
	 */
	protected $session;

	/**
	 * Initialize the model
	 */
	public function __construct()
	{
		parent::__construct();
		$this->db = db::get_instance();
	}

	// --------------------------------------------------------------------------
	// ! Data Manipulation
	// --------------------------------------------------------------------------

	/**
	 * Delete a genre/category/section or data item
	 *
	 * @param string $type
	 * @param int $id
	 * @return bool
	 */
	public function delete($type, $id)
	{
		try
		{
			$this->db->where('id', (int) $id)
				->delete($type);
		}
		catch (\PDOException $e)
		{
			return FALSE;
		}

		return TRUE;
	}

	// --------------------------------------------------------------------------

	/**
	 * Move a category/section/data item to another parent
	 *
	 * @param string
	 * @param int
	 * @param int
	 */
	public function move($type, $type_id, $parent_id)
	{
		$parent_type = array(
			'data' => 'section',
			'section' => 'category',
			'category' => 'genre'
		);

		$parent_field = "{$parent_type[$type]}_id";

		$this->db->set($parent_field, $parent_id)
			->where('id', (int) $type_id)
			->update($type);
	}

	// --------------------------------------------------------------------------

	/**
	 * Rename a genre/category/section
	 *
	 * @param string
	 * @param int
	 * @param string
	 * @return bool
	 */
	public function update($type, $id, $name)
	{
		$query = $this->db->set($type, $name)
			->where('id', (int) $id)
			->update($type);

		return (bool) $query;
	}

	// --------------------------------------------------------------------------

	/**
	 * Update the data
	 *
	 * @param int
	 * @param string
	 * @param string
	 * @return bool
	 */
	public function update_data($data_id, $key, $val)
	{
		// Save the data
		$query = $this->db->set('key', $key)
			->set('value', $val)
			->where('id', (int) $data_id)
			->update('data');

		return (bool) $query;
	}

	// --------------------------------------------------------------------------
	// ! Adding data
	// --------------------------------------------------------------------------

	/**
	 * Add genre
	 *
	 * @param string
	 * @return bool
	 */
	public function add_genre($genre)
	{
		// Check for duplicates
		$query = $this->db->from('genre')
			->where('genre', $genre)
			->limit(1)
			->get();

		$res = $query->fetch();

		if (count($res) < 1)
		{
			$id = $this->get_next_id("genre");
			$this->db->set('id', $id)
				->set('genre', $genre)
				->insert('genre');

			return TRUE;
		}

		return FALSE;

	}

	// --------------------------------------------------------------------------

	/**
	 * Add category to genre
	 *
	 * @param string
	 * @param int
	 * @return bool
	 */
	public function add_category($cat, $genre_id)
	{
		// Check for duplicates
		$query = $this->db->from('category')
			->where('genre_id', $genre_id)
			->where('category', $cat)
			->limit(1)
			->get();

		$res = $query->fetch();

		if (count($res) < 1)
		{
			$id = $this->get_next_id('category');
			$this->db->set('id', $id)
				->set('category', $cat)
				->set('genre_id', $genre_id)
				->insert('category');

			return TRUE;
		}

		return FALSE;
	}

	// --------------------------------------------------------------------------

	/**
	 * Add a section to a category
	 *
	 * @param string
	 * @param int
	 */
	public function add_section($section, $category_id)
	{
        // Check if the section exists
        $q = $this->db->from('section')
        	->where('category_id', $category_id)
        	->where('section', $section)
        	->limit(1)
        	->get();

        // Fetch the data as a workaround
		// for databases that do not support
		// grabbing result counts (SQLite / Firebird)
        $array = $q->fetchAll();
        if (count($array) < 1)
        {
        	$id = $this->get_next_id('section');
			$this->db->set('id', $id)
				->set('section', $section)
				->set('category_id', (int) $category_id)
				->insert('section');

			return TRUE;
        }

		return FALSE;
	}

	// --------------------------------------------------------------------------

	/**
	 * Add key/value data to a section
	 *
	 * @param int
	 * @param mixed object/array
	 */
	public function add_data($section_id, $data)
	{
		foreach($data as $key => $val)
		{
            // See if the data exists
        	$q = $this->db->from('data')
        		->where('section_id', $section_id)
        		->where('key', $key)
        		->get();

        	$res = $q->fetch();

        	if (count($res) > 0) return FALSE;

			// Save the data
			$id = $this->get_next_id('data');
			$this->db->set('id', $id)
				->set('key', $key)
				->set('value', $val)
				->set('section_id', (int) $section_id)
				->insert('data');
		}

		return TRUE;
	}

	// --------------------------------------------------------------------------
	// ! Data Retrieval
	// --------------------------------------------------------------------------

	/**
	 * Get breadcrumb data for section
	 *
	 * @param section_id
	 * @return array
	 */
	public function get_path_by_section($section_id)
	{
		$query = $this->db->select('genre, genre_id, category, category_id')
			->from('section s')
			->join('category c', 'c.id=s.category_id', 'inner')
			->join('genre g', 'g.id=c.genre_id', 'inner')
			->where('s.id', $section_id)
			->get();

		return $query->fetch(\PDO::FETCH_ASSOC);
	}

	// --------------------------------------------------------------------------

	/**
	 * Gets the list of genres
	 *
	 * @return array
	 */
	public function get_genres()
	{
		$genres = array();
		$query = $this->db->select('id, genre')
			->from('genre')
			->orderBy('genre', 'asc')
			->get();

		while($row = $query->fetch(\PDO::FETCH_ASSOC))
		{
			$genres[$row['id']] = $row['genre'];
		}

		return $genres;
	}

	// --------------------------------------------------------------------------

	/**
	 * Gets the name of the genre from its id
	 *
	 * @param int
	 * @return string
	 */
	public function get_genre_by_id($id)
	{
		$query = $this->db->select('genre')
			->from('genre')
			->where('id', (int) $id)
			->get();

		$row = $query->fetch(\PDO::FETCH_ASSOC);

		return $row['genre'];
	}

	// --------------------------------------------------------------------------

	/**
	 * Get the genre name by category id
	 *
	 * @param int
	 * @return array
	 */
	public function get_genre_by_category($cat_id)
	{
		$query = $this->db->select('g.id, genre')
			->from('genre g')
			->join('category c', 'c.genre_id=g.id', 'inner')
			->where('c.id', (int)$cat_id)
			->get();

		$row = $query->fetch(\PDO::FETCH_ASSOC);

		return $row;
	}

	// --------------------------------------------------------------------------

	/**
	 * Get the categories for the specified genre
	 *
	 * @param int
	 * @return array
	 */
	public function get_categories($genre_id)
	{
		$cats = array();

		$query = $this->db->select('id, category')
			->from('category')
			->where('genre_id', (int) $genre_id)
			->orderBy('category', 'asc')
			->get();

		while($row = $query->fetch(\PDO::FETCH_ASSOC))
		{
			$cats[$row['id']] = $row['category'];
		}

		return $cats;
	}

	// --------------------------------------------------------------------------

	/**
	 * Gets the name of the category from its id
	 *
	 * @param int
	 * @return string
	 */
	public function get_category_by_id($id)
	{
		$query = $this->db->select('category')
			->from('category')
			->where('id', (int) $id)
			->get();

		$row = $query->fetch(\PDO::FETCH_ASSOC);

		return $row['category'];
	}

	// --------------------------------------------------------------------------

	/**
	 * Get the sections for the specified category id
	 *
	 * @param int
	 * @return array
	 */
	public function get_sections($category_id)
	{
		$sections = array();

		$query = $this->db->select('id, section')
			->from('section')
			->where('category_id', (int) $category_id)
			->orderBy('section', 'asc')
			->get();

		while($row = $query->fetch(\PDO::FETCH_ASSOC))
		{
			$sections[$row['id']] = $row['section'];
		}

		return $sections;
	}

	// --------------------------------------------------------------------------

	/**
	 * Gets the name of the section from its id
	 *
	 * @param int
	 * @return string
	 */
	public function get_section_by_id($id)
	{
		$query = $this->db->select('section')
			->from('section')
			->where('id', (int) $id)
			->get();

		$row = $query->fetch(\PDO::FETCH_ASSOC);

		return $row['section'];
	}

	// --------------------------------------------------------------------------

	/**
	 * Get the data from the section
	 *
	 * @param int
	 * @return array
	 */
	public function get_data($section_id)
	{
		$data = array();

		$query = $this->db->select('id, key, value')
			->from('data')
			->where('section_id', (int) $section_id)
			->orderBy('key', 'asc')
			->get();

		while($row = $query->fetch(\PDO::FETCH_ASSOC))
		{
			$data[$row['id']] = array($row['key'] => $row['value']);
		}

		return $data;
	}

	// --------------------------------------------------------------------------

	/**
	 *  Gets the data for the specified id
	 *
	 * @param int $id
	 * @return array
	 */
	public function get_data_by_id($id)
	{
		$query = $this->db->select('key, value')
			->from('data')
			->where('id', (int) $id)
			->get();

		$row = $query->fetch(\PDO::FETCH_ASSOC);

		return $row;
	}

	// --------------------------------------------------------------------------

	/**
	 * Get sections and data for a general data outline
	 *
	 * @param int $category_id
	 * @return array
	 */
	public function get_category_outline_data($category_id)
	{
		// Get the sections
		$s_query = $this->db->from('section')
			->where('category_id', (int) $category_id)
			->orderBy('section', 'asc')
			->get();

		$sections = array();

		while($row = $s_query->fetch(\PDO::FETCH_ASSOC))
		{
			$sections[$row['id']] = $row['section'];
		}

		// Get the data for the sections
		$d_array = array();

		if ( ! empty($sections))
		{
			$d_query = $this->db->from('data')
				->whereIn('section_id', array_keys($sections))
				->orderBy('key', 'asc')
				->get();

			while($row = $d_query->fetch(\PDO::FETCH_ASSOC))
			{
				$d_array[$row['section_id']][$row['id']] = array($row['key'] => $row['value']);
			}
		}

		// Reorganize the data
		$data = array();

		foreach($sections as $section_id => $section)
		{
			$data[$section_id] = (isset($d_array[$section_id]))
				? array($section, $d_array[$section_id])
				: $section;
		}

		return $data;
	}

	// --------------------------------------------------------------------------

	/**
	 * Get data for a full outline
	 *
	 * @return array
	 */
	public function get_outline_data()
	{
		// Get the genres
		$query = $this->db->select('g.id, genre,
			c.id AS cat_id, category,
			s.id AS section_id, section')
			->from('genre g')
			->join('category c', 'c.genre_id=g.id', 'inner')
			->join('section s', 's.category_id=c.id', 'inner')
			->orderBy('genre', 'asc')
			->orderBy('category', 'asc')
			->orderBy('section', 'asc')
			->get();

		$return = array();

		// Create the nested array
		while ($row = $query->fetch(\PDO::FETCH_ASSOC))
		{
			extract($row);
			$return[$id][$genre][$cat_id][$category][$section_id] = $section;
		}

		return $return;
	}

	// --------------------------------------------------------------------------
	// ! Miscellaneous methods
	// --------------------------------------------------------------------------

	/**
	 * Check if a valid type for editing
	 *
	 * @param string
	 * @return bool
	 */
	public function is_valid_type($str)
	{
		$valid = array(
			'genre','category','section','data'
		);

		return in_array(strtolower($str), $valid);
	}

	// --------------------------------------------------------------------------

	/**
	 * Get the id of the last item of the type
	 *
	 * @param string $type
	 * @return int
	 */
	public function get_last_id($type)
	{
		$query = $this->db->select('id')
			->from($type)
			->orderBy('id', 'DESC')
			->limit(1)
			->get();

		$r = $query->fetch(\PDO::FETCH_ASSOC);

		return $r['id'];
	}

	// --------------------------------------------------------------------------

	/**
	 * Get the next id for database insertion
	 *
	 * @param string $table_name
	 * @param string $field
	 * @return int
	 */
	public function get_next_id($table_name, $field="id")
	{

		$query = $this->db->select("MAX($field) as last_id")
			->from($table_name)
			->limit(1)
			->get();

		$row = $query->fetch(\PDO::FETCH_ASSOC);

		if (empty($row))
		{
			$id = 1;
		}
		else
		{
			$id = intval($row['last_id']) + 1;
		}

		return $id;
	}

	// --------------------------------------------------------------------------

	public function create_tables()
	{
		$tables = [
			'genre' =>  $this->db->util->create_table('genre', [
				'id' => 'INT NOT NULL PRIMARY KEY',
				'genre' => 'VARCHAR(255)'
			]),
			'category' => $this->db->util->create_table('category', [
		   		'id' => 'INT NOT NULL PRIMARY KEY',
		   		'genre_id' => 'INT NOT NULL',
		   		'category' => 'VARCHAR(255)'
			],[
				'genre_id' => ' REFERENCES "genre" '
			]),
			'section' => $this->db->util->create_table('section', [
			   'id' => 'INT NOT NULL PRIMARY KEY',
			   'category_id' => 'INT NOT NULL',
			   'section' => 'VARCHAR(255)'
			],[
				'category_id' => ' REFERENCES "category" '
			]),
			'data' => $this->db->util->create_table('data', [
				'id' => 'INT NOT NULL PRIMARY KEY',
				'section_id' => 'INT NOT NULL',
				'key' => 'VARCHAR(255)',
				'value' => 'BLOB SUB_TYPE TEXT'
			],[
				'section_id' => ' REFERENCES "section"'
			])
		];

		foreach($tables as $table => $sql)
		{
			// Add the table
			$this->db->query($sql);
			echo "{$sql};<br />";

		}
	}
}
// End of data.php