<?php

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

class Js_css
{

    public $js_main_dir = '';    // location of main js dirrectory
    public $css_main_dir = '';  // location of main css dirrectory
    protected $js_list = array();      // java script files list
    protected $css_list = array();     // css files list
    protected $ignore_js_list = array();   // js ignore list
    protected $ignore_css_list = array();  // css ignore list
    protected $js_string_list = array();
    protected $css_string_list = array();
    protected $js_parse_list = array();
    protected $css_parse_list = array();
    protected $async_loading = false;

    public function __construct()
    {
        // Init configuration
        $ci = get_instance();
        $ci->config->load('js_css', true, true);
        $this->async_loading = $ci->config->item('async_loading', 'js_css');
    }

    /**
     * Add item to array
     * @param mixed $item - string or array
     * @param array $array
     */
    protected function _add_to_array($item, &$array)
    {
        if (!empty($item)) {
            if (is_array($item)) {
                foreach ($item as $v) {
                    $this->_add_to_array($v, $array);
                }
            } else {
                $item = trim($item);
                if (array_search($item, $array) === FALSE) {
                    $array[] = $item;
                }
            }
        }
    }

    public function get_js_list()
    {

        return $this->js_list;
    }

    public function set_js_list($js_list = array())
    {
        $this->js_list = array();
        $this->_add_to_array($js_list, $this->js_list);

        return $this;
    }

    public function get_css_list()
    {

        return $this->css_list;
    }

    public function set_css_list($css_list = array())
    {
        $this->css_list = array();
        $this->_add_to_array($css_list, $this->css_list);

        return $this;
    }

    /**
     * Add js file to js list
     * @param mixed     - path to js file. Like '/myfolder/myfile.js'
     * @return \Js_css
     */
    public function add_js()
    {
        $numargs = func_num_args();
        // Check for no args
        if ($numargs > 0) {
            // Add args
            $arg_list = func_get_args();
            foreach ($arg_list as $item) {
                $this->_add_to_array($item, $this->js_list);
            }
        }

        return $this;
    }

    /**
     * Add css file to css list
     * @param mixed     - path to css file. Like '/myfolder/myfile.css'
     * @return \Js_css
     */
    public function add_css()
    {
        $numargs = func_num_args();
        // Check for no args
        if ($numargs > 0) {
            // Add args
            $arg_list = func_get_args();
            foreach ($arg_list as $item) {
                $this->_add_to_array($item, $this->css_list);
            }
        }

        return $this;
    }

    /**
     * Add js string to js_string_list. This text will be add to page head.
     * @param mixed - like 'alert('hello world');'
     * @return \Js_css
     */
    public function add_js_string()
    {
        $numargs = func_num_args();
        // Check for no args
        if ($numargs > 0) {
            // Add args
            $arg_list = func_get_args();
            foreach ($arg_list as $item) {
                $this->_add_to_array($item, $this->js_string_list);
            }
        }

        return $this;
    }

    /**
     * Add css text to css_string_list. This text will be add to page head.
     * @param mixed - like '#table{color:red;}'
     * @return \Js_css
     */
    public function add_css_string()
    {
        $numargs = func_num_args();
        // Check for no args
        if ($numargs > 0) {
            // Add args
            $arg_list = func_get_args();
            foreach ($arg_list as $item) {
                $this->_add_to_array($item, $this->css_string_list);
            }
        }

        return $this;
    }

    /**
     * Add js file from module to project head
     * @param string $file - path to js file.
     * @param array $content - text variables to parse
     * Ex. if js file name is test.js and it located in myModule/views/js
     *      then line be like this - $this->js_css->parse('myModule/js/test.js');
     */
    public function parse_js($file, $content = array())
    {

        $this->js_parse_list[$file] = $content;
    }

    /**
     * Add css file from module to project head
     * @param string $file - path to css file.
     * @param array $content - text variables to parse
     * Ex. if css file name is test.css and it located in myModule/views/css
     *      then line be like this - $this->js_css->parse('myModule/css/test.css');
     */
    public function parse_css($file, $content = array())
    {

        $this->css_parse_list[$file] = $content;
    }

    /**
     * Remove js
     * @param mixed
     * @return \Js_css
     */
    public function remove_js()
    {
        $numargs = func_num_args();
        // Check for no args
        if ($numargs > 0) {
            // Add args
            $arg_list = func_get_args();
            foreach ($arg_list as $item) {
                $this->_add_to_array($item, $this->ignore_js_list);
            }
        }

        return $this;
    }

    /**
     * Remove css
     * @param mixed
     * @return \Js_css
     */
    public function remove_css()
    {
        $numargs = func_num_args();
        // Check for no args
        if ($numargs > 0) {
            // Add args
            $arg_list = func_get_args();
            foreach ($arg_list as $item) {
                $this->_add_to_array($item, $this->ignore_css_list);
            }
        }

        return $this;
    }

    protected function _reverse_array_items($array, $first_item, $second_item)
    {
        $id_1 = array_search($first_item, $array);
        $id_2 = array_search($second_item, $array);
        if ($id_1 && $id_2) {
            $tmp = $array[$id_1];
            $array[$id_1] = $array[$id_2];
            $array[$id_2] = $tmp;
        }

        return $array;
    }

    /**
     * Reverses the order of loading files
     * @param string $first_file
     * @param string $second_file
     * @return \Js_css
     */
    public function js_reverse($first_file, $second_file)
    {
        $this->js_list = $this->_reverse_array_items($this->js_list, $first_file, $second_file);

        return $this;
    }

    /**
     * Reverses the order of loading files
     * @param string $first_file
     * @param string $second_file
     * @return \Js_css
     */
    public function css_reverse($first_file, $second_file)
    {
        $this->css_list = $this->_reverse_array_items($this->css_list, $first_file, $second_file);

        return $this;
    }

    /**
     * Create js string from js_list
     * @return string
     */
    public function get_js_string()
    {
        $result = '';
        foreach ($this->js_list as $record) {
            if (array_search($record, $this->ignore_js_list) === FALSE) {
                $result .= '<script type="text/javascript" src="' . $this->js_main_dir . $record . '"></script>' . PHP_EOL;
            }
        }

        $CI = & get_instance();

        // Parse js
        if (!empty($this->js_parse_list)) {
            $CI->load->library('parser');
            foreach ($this->js_parse_list as $file => $content) {
                $this->add_js_string($CI->parser->parse($file, $content, TRUE));
            }
        }

        // Add js text
        if (!empty($this->js_string_list)) {
            $data = implode("\n", $this->js_string_list);

            if (ENVIRONMENT == 'production') {
                $CI->load->driver('cache', $CI->config->item('cache_init_config'));
                // Prepare cache_key
                $cache_key = $CI->uri->current_page . '_' . md5($data) . '.js';
                $cache_life_time = $CI->config->item('cache_life_time') * 2;

                // Minimize js
                $CI->load->library('jsmin', '');
                $CI->cache->save($cache_key, JSMin::minify($data), $cache_life_time);
                $result .= '<script type="text/javascript" src="/getassets/' . $cache_key . '"></script>' . PHP_EOL;
                $this->js_list[] = '/getassets/' . $cache_key;
            } else {
                $result .= '<script type="text/javascript">';
                $result .= $data;
                $result .= '</script>';
            }
        }

        // Async loading
        if (ENVIRONMENT == 'production' && $this->async_loading && !empty($this->get_js_list())) {
            $result = '<script type="text/javascript">'
                    . 'function load_scripts_async(key = 0) {'
                    . 'var scripts = ' . json_encode($this->get_js_list()) . ';'
                    . 'if (!scripts[key]) {return false;}'
                    . 'var item = document.createElement("script");'
                    . 'item.setAttribute("src", scripts[key]);'
                    . 'item.onload = function () {key++;load_scripts_async(key);};'
                    . 'document.head.appendChild(item);'
                    . '}'
                    . 'load_scripts_async();'
                    . '</script>';
        }

        return $result;
    }

    /**
     * Create css string from css_list
     * @return string
     */
    public function get_css_string()
    {
        $result = '';
        foreach ($this->css_list as $record) {
            if (array_search($record, $this->ignore_css_list) === FALSE) {
                $result .= '<link type="text/css" rel="stylesheet" href="' . $this->css_main_dir . $record . '" />' . PHP_EOL;
            }
        }

        $CI = & get_instance();

        // Parse css
        if (!empty($this->css_parse_list)) {
            $CI->load->library('parser');
            foreach ($this->css_parse_list as $file => $content) {
                $this->add_css_string($CI->parser->parse($file, $content, TRUE));
            }
        }

        // Add css text
        if (!empty($this->css_string_list)) {
            $data = implode("\n", $this->css_string_list);

            if (ENVIRONMENT == 'production') {
                $CI->load->driver('cache', $CI->config->item('cache_init_config'));
                // Prepare cache_key
                $cache_key = $CI->uri->current_page . '_' . md5($data) . '.css';
                $cache_life_time = $CI->config->item('cache_life_time') * 2;
                $CI->load->library('cssmin', $data);
                $CI->cache->save($cache_key, $CI->cssmin->min(), $cache_life_time);
                $result .= '<link type="text/css" rel="stylesheet" href="/getassets/' . $cache_key . '" />' . PHP_EOL;
                $this->css_list[] = '/getassets/' . $cache_key;
            } else {
                $result .= '<style type="text/css">';
                $result .= $data;
                $result .= '</style>';
            }
        }

        // Async loading
        if (ENVIRONMENT == 'production' && $this->async_loading && !empty($this->get_js_list())) {
            $result = '<script type="text/javascript">'
                    . 'function load_styles_async(key = 0) {'
                    . 'var styles = ' . json_encode($this->get_css_list()) . ';'
                    . 'if (!styles[key]) {return false;}'
                    . 'var item = document.createElement("link");'
                    . 'var rel = document.createAttribute("rel");'
                    . 'var href = document.createAttribute("href");'
                    . 'rel.value = "stylesheet";'
                    . 'href.value = styles[key];'
                    . 'item.setAttributeNode(rel);'
                    . 'item.setAttributeNode(href);'
                    . 'item.onload = function () {key++;load_styles_async(key);};'
                    . 'document.head.appendChild(item);'
                    . '}'
                    . 'load_styles_async();'
                    . '</script>';
        }

        return $result;
    }

}
