<?php

namespace Ciext\Model;

abstract class AbstractTableModel extends AbstractEventModel
{

    protected $table = '';
    protected $pkey = '';
    protected $select = 't.*';
    protected $select_escape = null;
    protected $predicates = [];

    /**
     * Set select string
     * @param string $select
     * @param mixed $escape
     * @return $this
     */
    public function set_select($select = 't.*', $escape = null)
    {
        $this->select = $select;
        $this->select_escape = $escape;
        return $this;
    }

    /**
     * Get select string
     * @return string
     */
    public function get_select()
    {
        return $this->select;
    }

    /**
     * Fetch one record
     * @param integer $id
     * @return array
     */
    public function fetch($id = 0)
    {
        $res = $this->db
                ->where($this->get_predicates())
                ->select($this->select, $this->select_escape)
                ->where('t.' . $this->pkey, (int) $id)
                ->get($this->table . ' t')
                ->row_array();

        // Notify all observers
        $this->notify(__FUNCTION__, ['id' => $id]);

        return $res;
    }

    /**
     * Fetch all records
     * @return array
     */
    public function fetch_all()
    {
        $res = $this->db
                ->select($this->select)
                ->where($this->get_predicates())
                ->get($this->table . ' t')
                ->result_array();
        
        // Notify all observers
        $this->notify(__FUNCTION__, ['ids' => array_column($res, $this->pkey)]);

        return $res;
    }

    /**
     * Fetch records of page
     * @param integer $page
     * @param integer $limit
     * @return array
     */
    public function fetch_page($page = 1, $limit = 1)
    {
        $offset = ($page <= 1) ? 0 : $page - 1;
        $this->db->limit((int) $limit)->offset(intval($offset * $limit));

        return $this->fetch_all();
    }

    /**
     * Records count
     * @return integer
     */
    public function count()
    {
        $res = $this->db
                ->where($this->get_predicates())
                ->count_all_results($this->table . ' t');

        return $res;
    }

    /**
     * Add record to table
     * @param array $data
     */
    public function add($data = [])
    {
        $this->db->insert($this->table, $data);

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

        // Notify all observers
        $this->notify(__FUNCTION__, ['id' => $id]);

        return $id;
    }

    /**
     * Update record
     * @param int $id
     * @param array $data
     */
    public function update($id, $data = [])
    {
        // Save data for observers
        $before = $this->fetch($id);
        
        $this->db
                ->where($this->get_predicates())
                ->where('t.' . $this->pkey, (int) $id)
                ->update($this->table . ' t', $data);

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

        // Notify all observers
        $this->notify(__FUNCTION__, [
            'id'     => $id,
            'result' => $result,
            'before' => $before,
        ]);

        return $result;
    }

    public function delete($ids = [])
    {
        // Prepare data
        if (!is_array($ids)) {
            $ids = [(int) $ids];
        } else {
            $ids = array_map('intval', $ids);
        }

        // Save data for observers
        $this->db->where_in('t.' . $this->pkey, $ids);
        $records = $this->fetch_all();

        // Delete records
        $this->db
                ->where_in($this->pkey, $ids)
                ->delete($this->table, $this->get_predicates());
        $affected_rows = $this->db->affected_rows();

        // Notify all observers
        $this->notify(__FUNCTION__, [
            'records'       => $records,
            'affected_rows' => $affected_rows,
        ]);

        return $affected_rows;
    }

    /**
     * Get predicates
     * @return array
     */
    public function get_predicates()
    {
        return $this->predicates;
    }

    /**
     * Set predicates
     * @param array $predicates
     * @return $this
     */
    public function set_predicates($predicates = [])
    {
        $this->predicates = $predicates;
        return $this;
    }

}
