<?php

if (!defined('BASEPATH')) {
    exit('No direct script access allowed');
}

/**
 * CRUD library
 *
 * @author Igor Ratsun
 */
class rtgrid_api
{

    const VIEW_MODEL = 'rtgrid_model';
    const EDIT_MODEL = 'rtgrid_edit_model';

    protected $CI;
    protected $_adapter;

    public function __construct()
    {
        $this->CI = & get_instance();
        $this->CI->load->library('gettext_language');
        $this->CI->gettext_language->load(__DIR__ . '/../language/' . $this->CI->config->item('language') . '.po');

        $adapter_model = ($this->CI->uri->get_next_segment('edit') == 'add' || intval($this->CI->uri->get_next_segment('edit')) !== 0) ? self::EDIT_MODEL : self::VIEW_MODEL;

        // Add adapter
        $this->CI->load->model('rtgrid/' . $adapter_model);
        $this->_adapter = $this->CI->{$adapter_model};

        // Configurate if edit page
        if (!$this->is_grid()) {
            // Add primery key value
            $this->set_primery_key_value(intval($this->CI->uri->get_next_segment('edit')));
            $back_link = preg_replace('/\/edit\/[add\d]*$/', '', $this->CI->uri->uri_string());

            // Add default actions
            $this->CI->load->model('rtgrid/rtgrid_edit_action_model');
            $btn_template = '<input type="submit" class="btn btn-success" name="{action_name}" value="{action_text}" />';
            if ($this->CI->uri->get_next_segment('edit') == 'add') {
                $this->add_action(new rtgrid_edit_action('add', $this->CI->lang->line('Add'), array($this->CI->rtgrid_edit_action_model, 'add'), '', $btn_template));
                $this->add_action(new rtgrid_edit_action('add_and_next', $this->CI->lang->line('Add and next'), array($this->CI->rtgrid_edit_action_model, 'add_and_next'), '', $btn_template));
            } else {
                $this->add_action(new rtgrid_edit_action('save', $this->CI->lang->line('Save changes'), array($this->CI->rtgrid_edit_action_model, 'save'), '', $btn_template));
                $this->add_action(new rtgrid_edit_action('save_and_back_to_list', $this->CI->lang->line('Save and back to list'), array($this->CI->rtgrid_edit_action_model, 'save_and_back_to_list'), '', $btn_template));

                // add clone button
                $this->add_action(new rtgrid_edit_action('clone', $this->CI->lang->line('Clone'), '', '', '<a href="#" id="clone-entity" data-href="/' . $back_link . '/edit/add?is_cloning_entity=true" class="btn btn-primary">{action_text}</a>'));
            }
            $this->add_action(new rtgrid_edit_action('back_to_list', $this->CI->lang->line('Cancel'), '', '', '<a href="/' . $back_link . '" class="btn">{action_text}</a>'));
        }

        // Load bootstrap
        $this->CI->ciext->load_bootstrap();
    }

    /**
     * Set base table
     * @param string $table table name
     * @return void
     */
    public function set_table($table)
    {
        $this->_adapter->set_table($table);

        // Set primery key
        $this->set_primery_key('id_' . $table);
    }

    /**
     * Set primery key
     * @param type $primery_key primery key of base table
     * @return void
     */
    public function set_primery_key($primery_key)
    {
        $this->_adapter->set_primery_key($primery_key);
    }

    /**
     * Set alias to column/field
     * @param string $field column/field name
     * @param string $value alias
     * @return void
     */
    public function display_as($field, $value)
    {
        $this->_adapter->display_as($field, $value);
    }

    /**
     * Set tooltip to column/field
     * @param string $field column/field name
     * @param string $html html or text
     * @param bool $grid show in grid or not
     * @return void
     */
    public function tooltip($field, $html, $grid = false)
    {
        $this->_adapter->tooltip($field, $html, $grid);
    }

    /**
     * Add where condition
     * @param mixed $key
     * @param string $value
     * @param boolean $escape
     * @return void
     */
    public function where($key, $value = NULL, $escape = TRUE)
    {
        $this->_adapter->where($key, $value, $escape);
    }

    /**
     * Add where_in condition
     * @param mixed $key
     * @param string $value
     * @param boolean $escape
     * @return void
     */
    public function where_in($key, $value = NULL, $escape = TRUE)
    {
        $this->_adapter->where_in($key, $value, $escape);
    }

    /**
     * Join other table
     * @param string $table
     * @param string $cond
     * @param string $type
     * @return void
     */
    public function join($table, $cond, $type = 'left')
    {
        $this->_adapter->join($table, $cond, $type);
    }

    /**
     * Default callback for field in grid and edit
     * @param string $field field name
     * @param mixed $default_callback callback for field
     * @param array $extra_data extra data for callback
     * @return void
     */
    public function callback($field, $default_callback, $extra_data = [])
    {
        $model = 'rtgrid_field_callback';
        $adapter = 'callback_field';

        if ($this->is_grid()) {
            $model = 'rtgrid_column_callback';
            $adapter = 'callback_column';
        }
        
        // Fix issue with backward compatibility
        if ($default_callback == 'date' || $default_callback == 'datetime') {
            $default_callback = new \Rtgc_date(['with_time' => $default_callback == 'datetime']);
        }

        // Add callback if instanceof rtgc_callback
        if ($default_callback instanceof rtgc_callback) {
            // Inject current crud and field
            if ($default_callback instanceof Abstract_rtgc_callback) {
                $default_callback->set_crud($this)->set_field_name($field);
            }
            $method = str_replace('callback_', '', $adapter);
            $this->_adapter->{$adapter}($field, [$default_callback, $method]);
            return;
        }

        // Load default callback model
        $this->CI->load->model('rtgrid/' . $model);
        if (method_exists($this->CI->{$model}, $default_callback)) {
            $this->_adapter->{$adapter}($field, [$this->CI->{$model}, $default_callback]);
        } elseif (strpos($default_callback, 'rtgc_') !== FALSE) {
            $this->CI->load->library('rtgrid/callback/' . $default_callback, $extra_data);
            if ($this->CI->{$default_callback} instanceof Abstract_rtgc_callback) {
                $callback = new $default_callback($extra_data);
                // Inject current crud and field
                $callback->set_crud($this)->set_field_name($field);

                $method = str_replace('callback_', '', $adapter);
                $this->_adapter->{$adapter}($field, [$callback, $method]);
            }
        }
    }

    /**
     * Add callback for field in edit
     * @param string $field field name
     * @param mixed $callback callback for field
     * @return void
     */
    public function callback_field($field, $callback)
    {
        if (!$this->is_grid()) {
            $this->_adapter->callback_field($field, $callback);
        }
    }

    /**
     * Add callback for column in grid
     * @param string $column column name
     * @param mixed $callback callback for field
     * @return void
     */
    public function callback_column($column, $callback)
    {
        if ($this->is_grid()) {
            $this->_adapter->callback_column($column, $callback);
        }
    }

    /**
     * Add action
     * @param rtgrid_top_action $rtgrid_action
     * @return void
     */
    public function add_action(rtgrid_top_action $rtgrid_action)
    {
        if ($rtgrid_action instanceof rtgrid_edit_action && !$this->is_grid()) {
            $this->_adapter->add_action($rtgrid_action);
        }

        if ($rtgrid_action instanceof rtgrid_top_action && $this->is_grid()) {
            $this->_adapter->add_action($rtgrid_action);
        }
    }

    /**
     * Remove action
     * @param string $rtgrid_action actino name
     * @return void
     */
    public function unset_action($action_name)
    {
        if ($this->is_grid()) {
            $this->_adapter->unset_top_action($action_name);
        }

        $this->_adapter->unset_action($action_name);

        // If unset edit and is edit page then show 404
        if ($action_name == 'edit' && !empty($this->CI->{self::EDIT_MODEL}) && $this->_adapter == $this->CI->{self::EDIT_MODEL}) {
            show_404('', FALSE);
        }
    }

    /**
     * Is grid page now?
     * @return boolean
     */
    public function is_grid()
    {
        $view = self::VIEW_MODEL;

        return ($this->_adapter instanceof $view);
    }

    /**
     * Add columns list
     * @param string $columns - Like 'name, surname, age'
     * @return void
     */
    public function columns($columns)
    {
        if ($this->is_grid()) {
            $this->_adapter->columns($columns);
        }
    }

    /**
     * Get columns
     * @return string
     */
    public function get_columns()
    {
        return ($this->is_grid()) ? $this->_adapter->get_columns() : '';
    }

    /**
     * Add fields list
     * @param string $fields - Like 'name, surname, age'
     * @return void
     */
    public function fields($fields)
    {
        if (!$this->is_grid()) {
            $this->_adapter->fields($fields);
        }
    }

    /**
     * Get fields
     * @return string
     */
    public function get_fields()
    {
        return (!$this->is_grid()) ? $this->_adapter->get_fields() : '';
    }

    /**
     * Set relation 1:n
     * @param string $field_name field in base table
     * @param string $related_table related table
     * @param string $related_title_field field from relater table
     * @param mixed $where framework where. Optional
     * @param string $order_by ASC or DESC. Optional
     * @return void
     */
    public function set_relation($field_name, $related_table, $related_title_field, $where = NULL, $order_by = 'ASC')
    {
        $this->_adapter->set_relation($field_name, $related_table, $related_title_field, $where, $order_by);
    }

    /**
     * Set relation n:n
     * @param string $field_name
     * @param string $mapping_table
     * @param string $selected_table
     * @param string $selected_table_alias_field
     * @param array $selected_table_where
     */
    public function set_relation_n_n($field_name, $mapping_table, $selected_table, $selected_table_alias_field, $selected_table_where = array('active' => true))
    {
        $this->_adapter->set_relation_n_n($field_name, $mapping_table, $selected_table, $selected_table_alias_field, $selected_table_where);
    }

    /**
     * Add observer
     * @param SplObserver $obs
     * @return void
     */
    public function attach(SplObserver $obs)
    {

        $this->_adapter->attach($obs);
    }

    /**
     * Remove observer
     * @param SplObserver $obs
     * @return void
     */
    public function detach(SplObserver $obs)
    {

        $this->_adapter->detach($obs);
    }

    /**
     * Add multilang field
     * @param multilang $multilang
     * @return boolean
     */
    public function multilang(multilang $multilang)
    {

        return $this->_adapter->multilang($multilang);
    }

    /**
     * Add photogallery tab in edit
     * @param photogallery $photogallery photogallery configuration
     * @return void
     */
    public function add_photogallery(photogallery $photogallery)
    {

        $this->_adapter->add_photogallery($photogallery);
    }

    /**
     * Set base order by
     * @param string $column
     * @param string $direction
     */
    public function set_order_by($column, $direction = 'ASC')
    {
        if ($this->is_grid()) {

            $this->_adapter->set_order_by($column, $direction);
        }
    }

    /**
     * Add action before edit
     * @param callback $callback
     */
    public function callback_before_edit($callback)
    {
        if (!$this->is_grid()) {

            $this->_adapter->callback_before_edit($callback);
        }
    }

    /**
     * Set readonly field
     * @param string $field
     */
    public function readonly($field)
    {

        if (!$this->is_grid()) {
            $this->_adapter->readonly($field);
        }
    }

    /**
     * Set hidden field
     * @param string $field
     * @param mixed $value
     */
    public function hidden($field, $value = 0)
    {
        if (!$this->is_grid()) {
            $this->_adapter->hidden($field, $value);
        }
    }

    public function set_image_upload(image_upload $data)
    {

        $this->_adapter->set_image_upload($data);
    }

    public function required_fields()
    {
        if (!$this->is_grid()) {
            $args = func_get_args();
            if (!empty($args)) {
                $this->_adapter->required_fields($args);
            }
        }
    }

    /**
     * Set check for unique field value
     */
    public function unique_fields()
    {
        if (!$this->is_grid()) {
            $args = func_get_args();
            if (!empty($args)) {
                $this->_adapter->unique_fields($args);
            }
        }
    }

    /**
     * Render CRUD
     * @return string ready html
     */
    public function render()
    {

        return $this->_adapter->render();
    }

    /**
     * Export all grid data
     * @param array $filter data from form field 'filter'. Keys must be encoded!
     * @return array
     */
    public function export($filter = [])
    {
        if ($this->is_grid()) {

            return $this->_adapter->export($filter);
        }

        return [];
    }

    /**
     * Call other function of model
     * @param string $name
     * @param array $arguments
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        if (method_exists($this->_adapter, $name)) {

            return call_user_func_array(array($this->_adapter, $name), $arguments);
        } else {

            show_error(sprintf($this->CI->lang->line('Method %s not found.'), $name), 500);
        }
    }

    /**
     * Set message. It is display in grid.
     * 
     * @param string $type
     * @param string $msg
     * @return boolean
     */
    public function set_flashmessage($type = 'success', $msg = '')
    {
        if (!empty($msg)) {
            $template = '<div class="alert alert-%s"><a class="close" data-dismiss="alert">×</a>%s</div>';
            get_instance()->session->set_flashdata('session_crud_message', sprintf($template, $type, $msg));

            return true;
        } else {

            return false;
        }
    }

}
