<?php

class Core_model extends CI_Model
{

    const CALLBACK_PARSE_RESOURCE = 'CALLBACK_PARSE_RESOURCE';

    protected $callback = array();

    /**
     * Current language id
     * @var int 
     */
    private $lang_id;

    /**
     * SEO data
     * @var array 
     */
    private $seo_data = array(
        'title'       => '',
        'keywords'    => '',
        'description' => ''
    );

    /**
     * Head data
     * @var string 
     */
    private $head_data = '';

    /**
     * Id of current page
     * @var int 
     */
    private $current_page_id = 0;

    /**
     * Add head data
     * @param string $data
     * @return boolean 
     */
    public function add_head_data($data = '')
    {
        if (empty($data)) {
            return FALSE;
        }

        $this->head_data .= $data;

        return TRUE;
    }

    /**
     * Get head data
     * @return string 
     */
    public function get_head_data()
    {
        return $this->head_data;
    }

    /**
     * Set SEO data
     * @param array $seo_data 
     * @return void
     */
    public function set_seo_data($seo_data = array())
    {
        if (!empty($seo_data['title'])) {
            $this->seo_data['title'] = mb_substr($seo_data['title'], 0, 256);
        }

        if (!empty($seo_data['keywords'])) {
            $this->seo_data['keywords'] = mb_substr($seo_data['keywords'], 0, 256);
        }

        if (!empty($seo_data['description'])) {
            $this->seo_data['description'] = mb_substr($seo_data['description'], 0, 256);
        }
    }

    /**
     * Get SEO html
     *
     * @return string
     */
    public function get_seo_html()
    {
        $metadata = '';

        // set title
        if (!empty($this->seo_data['title'])) {
            $metadata .= '<title>' . $this->seo_data['title'] . '</title>';
        }

        // set keywords
        if (!empty($this->seo_data['keywords'])) {
            $metadata .= '<meta name="Keywords" content="' . $this->seo_data['keywords'] . '" >';
        }

        // set description
        if (!empty($this->seo_data['description'])) {
            $metadata .= '<meta name="Description" content="' . $this->seo_data['description'] . '" >';
        }

        return $metadata;
    }

    /**
     * Get id of current language
     * @return int 
     */
    public function get_lang_id()
    {
        if (empty($this->lang_id)) {
            $res = $this->db->select('id_lan')->
                            where(array(
                                'active'     => '1',
                                'short_name' => $this->uri->current_language
                            ))->get('lan')->row_array();
            $this->lang_id = (!empty($res['id_lan'])) ? $res['id_lan'] : 0;
        }

        return $this->lang_id;
    }

    /**
     * Get page from DB
     * @return string
     */
    public function get_page($page_alias = '')
    {
        if (!empty($page_alias)) {
            $this->uri->current_page = $page_alias;
        }

        if ($this->config->item('breadcrumbs_in_url')) {
            $this->uri->current_page = $this->_prepare_current_page(0, $this->uri->current_page);
        }

        if (empty($this->uri->current_page)) {

            return '';
        }

        $id_lan = $this->get_lang_id();

        // Check for cache
        $this->load->driver('cache', $this->config->item('cache_init_config'));
        $cache_key = $this->uri->current_page . '_' . $id_lan . '_' . md5(serialize($this->input->post()) . serialize($this->input->get()) . $this->uri->uri_string() . (string) $this->load->library('user_agent')->is_mobile());
        if (!$cache = $this->cache->get($cache_key)) {
            // Get page from DB
            $res = $this->db->
                            select('r.id_resource, rtp.route, rtp.id_resource_type_page, rtpt.mime_type, rtp.cache, rtptt.content, rtpd.seo_title, rtpd.seo_keywords, rtpd.seo_description')->
                            where('rtptt.id_resource_type_page_template = rtp.id_resource_type_page_template')->
                            where('rtpt.id_resource_type_page_template = rtp.id_resource_type_page_template')->
                            where('rtptt.id_lan = ' . $id_lan)->
                            where('rtp.id_resource_type_page = rtpd.id_resource_type_page')->
                            where('rtpd.id_lan = ' . $id_lan)->
                            where('r.id_item = rtp.id_resource_type_page AND r.id_resource_type = 3')->
                            where('BINARY(rtp.alias)=' . $this->db->escape($this->uri->current_page), '', FALSE)->
                            where(array(
                                'rtp.active' => 1,
                                'r.deleted'  => 0
                            ))->
                            get('resource_type_page rtp, resource_type_page_template rtpt, resource_type_page_template_translate rtptt, resource r, resource_type_page_data rtpd')->row_array();

            if (empty($res['content'])) {

                return '';
            }

            // Route check
            if (!empty($res['route'])) {
                $pattern = '/' . $res['route'] . '/';
                $subject = str_replace($this->create_page_url($this->uri->current_page), '', base_url() . $this->uri->uri_string());
                $route = rtrim(preg_replace($pattern, '', $subject), '/');
                if (!empty($route)) {

                    return '';
                }
            } else {
                if ($page_alias == '' && $this->create_page_url($this->uri->current_page) != base_url() . $this->uri->uri_string()) {

                    return '';
                }
            }

            // Set SEO data
            $this->seo_data = array(
                'title'       => $res['seo_title'],
                'keywords'    => $res['seo_keywords'],
                'description' => $res['seo_description']
            );

            // Set page id
            $this->current_page_id = $res['id_resource_type_page'];

            // Get page modules
            $page = $this->get_modules($this->_get_resources($res['content'], $res['id_resource']));

            // Add languages
            $page = $this->parser->parse_string($page, $this->lang->language, TRUE);

            // Replace all no use resources if production
            $cache = (ENVIRONMENT == 'production') ? preg_replace('/\{id_([\w_\d]+)}|\<resource\s(.+)\/\>/i', '', $page) : $page;

            // Replace data by parsers
            $cache = $this->load->library('ciext/parser/ciext_parser')->parse($cache);

            // Add seo, head data and assets
            $head = $this->get_seo_html() . $this->get_head_data();
            $assets = $this->js_css->get_css_string() . $this->js_css->get_js_string();

            $cache = str_replace('<head>', '<head>' . $head, str_replace('</body>', $assets . '</body>', $cache));

            // get mime type
            $mime_types = $this->get_mime_types();
            $mime_type = (isset($mime_types[$res['mime_type']]['header'])) ? $mime_types[$res['mime_type']]['header'] : '';

            $cache = ['data' => $cache, 'mime_type' => $mime_type];

            // Save cache
            if (!empty($res['cache']) && ENVIRONMENT == 'production') {

                $this->cache->save($cache_key, $cache, $this->config->item('cache_life_time'));
            }
        }

        // set content type
        if (!empty($cache['mime_type'])) {
            $this->output->set_content_type($cache['mime_type']);
        }

        return $cache['data'];
    }

    /**
     *  Get mime types
     *
     * @return array
     */
    public function get_mime_types()
    {
        // load config
        $this->config->load('resmng/mime_type');

        return $this->config->item('mime_type');
    }

    /**
     * Get current page id.
     * Field id_resource_type_page from resource_type_page table
     * @return int
     */
    public function get_current_page_id()
    {
        return $this->current_page_id;
    }

    /**
     * All pages
     * @var array
     */
    private $_pages_list = array();

    protected function _create_pages_list()
    {
        if (empty($this->_pages_list)) {
            $this->_pages_list = $this->db->
                            select('r.id_resource, rp.alias, r.parent_id')->
                            where('rp.active', '1')->
                            where('r.id_item = rp.id_resource_type_page')->
                            where('r.id_resource_type', 3)->
                            get('resource r, resource_type_page rp')->result_array();
        }

        return TRUE;
    }

    /**
     * Prepare page
     * @param integer $id
     * @param string $page
     * @return string
     */
    private function _prepare_current_page($id = 0, $page = '')
    {
        // Get list of page
        if (empty($this->_pages_list)) {
            $this->_create_pages_list();
        }

        // Get id of current page
        if ($id == 0) {
            foreach ($this->_pages_list as $v) {
                if ($v['alias'] == $page) {
                    // Check for parent exist
                    foreach ($this->_pages_list as $parent) {
                        if ($parent['id_resource'] == $v['parent_id']) {

                            return FALSE;
                        }
                    }
                    $id = $v['id_resource'];
                    break;
                }
            }
        }

        // Get subpage
        $sub_page = $this->uri->get_next_segment($page);
        foreach ($this->_pages_list as $v) {
            if ($v['alias'] == $sub_page && $v['parent_id'] == $id) {
                return $this->_prepare_current_page($v['id_resource'], $sub_page);
            }
        }

        return $page;
    }

    /**
     * Create url for page
     * @param string $page
     * @param integer $parent_id
     * @return string
     */
    public function create_page_url($page = '', $parent_id = 0)
    {
        if (!$this->config->item('breadcrumbs_in_url')) {
            return base_url() . $page;
        }

        // Get list of page
        if (empty($this->_pages_list)) {
            $this->_create_pages_list();
        }

        // Get id of current page
        if ($parent_id == 0) {
            foreach ($this->_pages_list as $v) {
                if ($v['alias'] == $page) {
                    $parent_id = $v['parent_id'];
                    break;
                }
            }
        }

        foreach ($this->_pages_list as $v) {
            if ($v['id_resource'] == $parent_id) {

                return $this->create_page_url($v['alias'], $v['parent_id']) . (($v['alias'] == $this->config->item('home_page')) ? $this->config->item('home_page') : '') . '/' . $page;
            }
        }

        $lang = ($this->uri->current_language != $this->config->item('language_short')) ? $this->uri->current_language . '/' : '';

        $page = $lang . $page;

        return base_url() . (($page != $this->config->item('home_page')) ? $page : '');
    }

    /**
     * Get all resources
     * @param string $page_data
     * @param integer $parent_id
     * @return string 
     */
    private function _get_resources($page_data = '', $parent_id = 0)
    {
        // Get all resources
        $resources = array();
        $matches = array();
        preg_match_all('/\{id_([\w_\d]+)}|\<resource\s([\s\w\d\-\=\"\/]+)\/\>/i', $page_data, $matches);
        if (!empty($matches[1]) && !empty($matches[2])) {
            foreach ($matches[1] as $k => $item) {
                if (empty($matches[2][$k])) {
                    $resources[] = array(
                        'id' => $item
                    );
                } else {
                    $res_data = array();
                    $attr = array();
                    preg_match_all('/([\w]+)\=\"([\w\d\/]+)\"/', $matches[2][$k], $attr);
                    if (!empty($attr[1])) {
                        $res_data = array_combine($attr[1], $attr[2]);
                    }
                    if (!empty($res_data['id'])) {
                        $resources[] = $res_data;
                    }
                }
            }
        }

        // Check for empty
        if (empty($resources)) {

            return $page_data;
        }

        $ids = array();
        foreach ($resources as $res) {
            $ids[] = $res['id'];
        }
        $list = '\'' . implode('\', \'', array_unique($ids)) . '\'';

        $id_lan = $this->get_lang_id();
        $sql = $this->db->
                        select('r.id_resource, rtr.alias, rtrt.content, r.parent_id, rtr.id_resource_type_resource')->
                        where('(r.id_resource IN (' . $list . ') OR (rtr.alias IN (' . $list . ') AND r.parent_id = ' . (int) $parent_id . '))')->
                        where('r.deleted', FALSE)->
                        where('r.id_item = rtr.id_resource_type_resource')->
                        where('rtrt.id_resource_type_resource = rtr.id_resource_type_resource')->
                        where('rtrt.id_lan', $id_lan)->
                        where('r.id_resource_type', 4)->
                        get('resource r, resource_type_resource rtr, resource_type_resource_translate rtrt')->result_array();

        // Prepare data from DB
        $resource_data = array();
        foreach ($sql as $v) {
            $resource_data[$v['id_resource']] = $v;
            if (!empty($v['alias'])) {
                $resource_data[$v['parent_id'] . '_' . $v['alias']] = $v;
            }
        }

        // Prepare resources
        foreach ($resources as $resource) {
            $id = $resource['id'];
            // Find by id
            if (!empty($resource_data[$id])) {
                $v = $resource_data[$id];
                $run = $this->_get_resources($this->get_modules($v['content']));
                $page_data = $this->parse_resource($v['id_resource'], $run, $page_data, $v['id_resource_type_resource'], $resource);
            }
            // Find by alias
            if (!empty($resource_data[$parent_id . '_' . $id])) {
                $v = $resource_data[$parent_id . '_' . $id];
                $run = $this->_get_resources($this->get_modules($v['content']));
                $page_data = $this->parse_resource($v['alias'], $run, $page_data, $v['id_resource_type_resource'], $resource);
            }
        }

        return $page_data;
    }

    protected function parse_resource($key, $run, $page_data, $id_resource_type_resource, $attributes = array())
    {
        // Callback
        if (!empty($this->callback[Core_model::CALLBACK_PARSE_RESOURCE])) {
            foreach ($this->callback[Core_model::CALLBACK_PARSE_RESOURCE] as $callback) {
                $run = call_user_func($callback, $run, $id_resource_type_resource, $attributes);
            }
        }

        $pattern = '/(\{id\_' . $key . '\}|<resource[\s\w\d\-\=\"]+id\=\"' . $key . '\"\s*\/\>)/';
        if (count($attributes) > 1) {
            $pattern = '/(<resource[\s\w\d\-\=\"]+id\=\"' . $key . '\"[\s\w\d\-\=\"\/]+\/\>)/';
        }

        $data = preg_replace($pattern, str_replace('$', '\$', $run), $page_data);

        return $data;
    }

    /**
     * Prepare page
     * @param string $page
     * @return string 
     */
    public function get_modules($page = '')
    {
        // Prepare page
        while (preg_match('/\{module_([\w_]+\/[\w_]+\/[\w_]+)\/?([\w\/]+)?\}/i', $page, $modules)) {
            $modules[2] = (isset($modules[2])) ? $modules[2] : NULL;

            if ($this->config->item('enable_profiler')) {
                $this->benchmark->mark('MODULE: ' . $modules[1] . '_start');
            }

            $run = $this->_get_resources(Modules::run($modules[1], $modules[2]));

            if ($this->config->item('enable_profiler')) {
                $this->benchmark->mark('MODULE: ' . $modules[1] . '_end');
            }

            $page = str_replace($modules[0], $run, $page);
        }
        return $page;
    }

    /**
     * Add callback
     * @param string $key
     * @param callback $callback
     */
    public function add_callback($key, $callback)
    {
        $this->callback[$key][] = $callback;
    }

}
