<?php
 /**
  * Parent Model
  *
  * This is the parent model used to extend our models.
  *
  * @author Innoxess Spain, S.L. <hola@innoxess.es>
  * @copyright 2015-2018 Innoxess Spain, S.L.
  * @package Application
  * @subpackage Models
  *
  */
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
 /**
  * Class MY_Model - Model
  *
  * Parent Model
  */
class MY_Model extends CI_Model
{
	 /** @var string This is the name of the primary key field for our model. */
	protected $primary_key = 'id';
	 /** @var string This is the database table. */
	protected $table = NULL;

	/**
	  * Constructor
	  */
	function __construct()
	{
		parent::__construct();
	}

	/**
	  * Fetch a single record based on the primary key. Return an array
	  *
	  * @param int $id ID for primary key
	  *
	  * @return array
	  */
	public function get_one($id)
	{
		$query				= $this->db->select('*, '.$this->table.'.'.$this->primary_key.' AS '.$this->primary_key)
							->where($this->table.'.'.$this->primary_key, $id)
							->get($this->table);

		return $this->_get_virtuals($id, $query->row_array());
	}

	/**
	  * Fetch all the records in the table.
	  *
	  * @return array
	  */
	public function get_all()
	{
		return $this->db->get($this->table)->result_array();
	}

	/**
	  * Retrieve and generate a form_dropdown friendly array
	  *
	  * @param string $text_field Column field used to get text data
	  * @param array $filter Used to apply a filter
	  *
	  * @return array
	  */
	public function get_select($text_field, $filter=NULL)
	{
		if(!empty($filter))
		{
			$this->db->where($filter);
		}
		$query				= $this->db->select($this->table.'.'.$this->primary_key.' as value', FALSE)
							->select($text_field.' as text', FALSE)
							->order_by($this->primary_key)
							->get($this->table);

		return $query->result_array();
	}

	/**
	  * Helper for get_one method to concat extra information for return
	  *
	  * @param int $id ID for primary key
	  * @param array $data Array
	  */
	protected function _get_virtuals($id, $data)
	{
		return $data;
	}

	/**
	  * Manage $data array to insert or update if $id exists
	  *
	  * @param int $id ID for primary key
	  * @param array $data Array
	  *
	  * @return false|int
	  */
	public function save($id=null, $data)
	{
		$this->_before_save($id, $data);
		if(empty($id))
		{
			return $this->insert($data);
		} else {
			return $this->update($id, $data);
		}
	}

	/**
	  * This callback runs before save of the crud.
	  * The callback takes as a 1st parameter the primary key value and as 2nd the post array.
	  * With this opportunity you can add or unset some post variables.
	  *
	  * @param int $id Primary key
	  * @param array $data Array
	  */
	protected function _before_save($id, &$data)
	{
	}

	/**
	  * This callback runs before the insert of the crud.
	  * The callback takes as a 1st parameter the post array.
	  * With this opportunity you can add or unset some post variables.
	  *
	  * @param array $data Array
	  */
	protected function _before_insert(&$data)
	{
	}

	/**
	  * Insert a new row into the table. $data should be an associative array of data to be inserted. Returns newly created ID.
	  *
	  * @param array $data Array
	  *
	  * @return int
	  */
	public function insert($data)
	{
		$this->_before_insert($data);
		$this->db->insert($this->table, $data);

		$id					= $this->db->insert_id();
		
		$this->_after_insert($id);
		$this->_after_save($id);

		return $id;
	}

	/**
	  * This callback runs before the update of the crud.
	  * The callback takes as a 1st parameter the primary key value and as 2nd the post array.
	  * With this opportunity you can add or unset some post variables.
	  *
	  * @param int $id Primary key
	  * @param array $data Array
	  */
	protected function _before_update($id, &$data)
	{
	}

	/**
	  * Updated a record based on the primary value.
	  *
	  * @param int $id Primary key
	  * @param array $data Array
	  *
	  * @return false|int
	  */
	public function update($id, $data)
	{
		$this->_before_update($id, $data);

		$this->db->where($this->primary_key, $id)->update($this->table, $data);

		$affected_rows		= $this->db->affected_rows();
		if($affected_rows==0)
		{
			$item				= $this->get_one($id);
			$distinct			= FALSE;
			foreach($data as $name=>$value)
			{
				if(!isset($item[$name]) || $item[$name]!=$value)
				{
					$distinct			= TRUE;
					break;
				}
			}

			if($distinct)
			{
				$id				= 0;
			}
		}

		$ret1				= $this->_after_update($id);
		$ret2				= $this->_after_save($id);

		return ($ret1===false || $ret2===false) ? false : $id;
	}

	/**
	  * This callback runs after the insert of the crud.
	  * The callback takes as a 1st parameter the primary key value.
	  * With this opportunity you can do other actions after insert.
	  *
	  * @param int $id Primary key
	  */
	protected function _after_insert($id)
	{
	}

	/**
	  * This callback runs after the update of the crud.
	  * The callback takes as a 1st parameter the primary key value.
	  * With this opportunity you can do other actions after update.
	  *
	  * @param int $id Primary key
	  */
	protected function _after_update($id)
	{
	}

	/**
	  * This callback runs after the insert/update of the crud.
	  * The callback takes as a 1st parameter the primary key value.
	  * With this opportunity you can do other actions after insert/update.
	  *
	  * @param int $id Primary key
	  */
	protected function _after_save($id)
	{
	}

	/**
	  * Return number of affected rows on last query.
	  *
	  * @param int
	  */
	public function affected_rows()
	{
		return $this->db->affected_rows();
	}

	/**
	  * Delete a row from the table by the primary value
	  *
	  * @param int $id Primary key
	  *
	  * @return int
	  */
	public function delete($id)
	{
		$this->_before_delete($id);
		
		$this->db->where($this->primary_key ,$id)->delete($this->table);

		$affected_rows		= $this->db->affected_rows();

		$this->_after_delete();

		return $affected_rows;
	}

	/**
	  * This callback runs after the delete of the crud.
	  * With this opportunity you can do other actions after delete.
	  *
	  * @param int $id Primary key
	  */
	protected function _after_delete()
	{
	}

	/**
	  * This callback runs before the delete of the crud.
	  * The callback takes as a 1st parameter the primary key value.
	  * With this opportunity you can do other actions before delete.
	  *
	  * @param int $id Primary key
	  */
	protected function _before_delete($id)
	{
	}

	/**
	  * This callback runs on the read_table method.
	  * With this opportunity you can implement other database instructions like join.
	  *
	  */
	protected function _callback_table()
	{
	}

	/**
	  * Return data for table of the crud.
	  * Recive some params to make database query & return data
	  *
	  * @param array $items Contain columns names to get from database
	  * @param array $or_like -
	  * @param array $order Specifies data order, use a simple array using key for column & value for direction
	  * @param int $Pagesize Specifies the maximum number of rows to return
	  * @param int $Offset Specifies the offset of the first row to return 
	  * @param true|false $count
	  * @param null|array $filter -
	  */
	public function read_table($items, $or_like=array(), $order=array(), $Pagesize=0, $Offset=0, $count=FALSE, $filter=NULL)
	{
		$this->db->from($this->table);
		$this->_callback_table();
		
		foreach($items as $key=>$item)
		{
			if(!isset($item['field']))
			{
				unset($items[$key]);
			}
		}

		if(!$count)
		{
			foreach ($items as $item)
			{
				if(!isset($item['alias']))
				{
					$this->db->select($item['field'], TRUE);
				} else {
					$this->db->select($item['field'].' AS '.$item['alias'], FALSE);
				}
			}
		} else {
			$this->db->select('count('.$this->table.'.'.$this->primary_key.') AS total', FALSE);
		}

		$where				= "";
		if(!empty($or_like)) 
		{
			foreach($or_like as $column => $string)
			{
				$wildcard = "%";
				if(strpos($string,'*') !== false || strpos($string,'?') !== false)
				{
					$string				= str_replace('*','%',$string);
					$string				= str_replace('?','_',$string);
					$wildcard			= "";
				}
				if(!empty($filter))
				{
					$where				.= " OR (".$filter." AND LOWER(".$column.") LIKE LOWER('".$this->db->escape_str($wildcard.$string.$wildcard)."'))";
				}else{
					$where				.= " OR (LOWER(".$column.") LIKE LOWER('".$this->db->escape_str($wildcard.$string.$wildcard)."'))";
				}
			}
		}

		if(!empty($where))
		{
			$this->db->where("TRUE = FALSE ".$where, NULL, FALSE);
		} else {
			if(!empty($filter))
			{
				$this->db->where($filter, NULL, FALSE);
			}
		}

		foreach ($order as $column=>$direction)
		{
			$this->db->order_by($column, $direction);
		}

		if(!$count)
		{
			if ($Pagesize!=0)
			{
				$this->db->limit($Pagesize, $Offset);
			}
		}

		$query				= $this->db->get();
		if($count)
		{
			$num_rows			= $query->num_rows();
			if($num_rows > 0)
			{
				$row				= $query->row_array();
				if($num_rows==1)
				{
					return $row['total'];
				} else {
					return $num_rows;
				}
			} else {
				return 0;
			}
		} else {
			if($query->num_rows() > 0)
			{
				return $query->result();
			} else {
				return array();
			}
		}
	}
}
