<?php

    /**
     * @extensione  YRMinifier
     * @author      Lev Milicenco
     * @copyright   (c) Marlev.it - Itroom SRLS - 2017. All rights reserved.
     * @license     GNU General Public License version 2 or later; see LICENSE.txt
     */
    defined('_JEXEC') or die;

    class plgSystemYRminifier extends JPlugin {

        protected $cache;
        protected $cache_id;
        protected $fullstring = array();

        public function OnAfterRender() {
            $app = JFactory::getApplication();
            if ($app->isSite()) {
                $this->params = $this->get_params();
                $enabled = (int)$this->params->optimizecss . $this->params->optimizejs;

                if ($this->params->mode == false || $enabled == 0)
                    return false;

                $html = JFactory::getApplication()->getBody();
                $rewrited = preg_replace_callback("#<\s*head[^>]*>.*<\s*/\s*head[^>]*>#si", "self::run_yrminifier", $html);
                JFactory::getApplication()->setBody($rewrited);
            }
        }

        protected function run_yrminifier($match) {
            $html = $match[0];
            //mode 1 - test
            //mode 2 - production
            $mode = (int) $this->params->mode;
            $getcache = $this->prepare_cache();
            if ($mode == 1) {
                $rewrited = $this->rewrite($html);
            }
            else {
                if ($getcache !== false) {
                    $cached = json_decode($getcache);
                    $rewrited = $this->build_from_cache($cached, $html);
                }
                else {
                    $rewrited = $this->rewrite($html);
                }
            }

            return $rewrited;
        }

        protected function prepare_cache() {

                $cache = JCache::getInstance('output', array('caching' => true));
                $this->cache_id = $cache->makeId();
                $getcache = $cache->get($this->cache_id, $this->params->cachegroup);
                return $getcache;

        }

        protected function build_from_cache($getcache, $html) {
            if ($this->params->optimizecss > 0) {
                $html = $this->build_from_cache_css($getcache, $html);
            }
            if ($this->params->optimizejs > 0) {
                $html = $this->build_from_cache_js($getcache, $html);
            }
            return $html;
        }

        protected function build_from_cache_js($cached, $html) {
            $grep_js = preg_grep("#src=[\"']#", $cached);
            $html = $this->remove_from_html($grep_js, $html);
            $html = $this->remove_script($html);
            $html = $this->add_jstohtml($html, $grep_js);
            return $html;
        }

        protected function build_from_cache_css($cached, $html) {
            $grep_css = preg_grep("#href=[\"']#", $cached);
            $html = $this->remove_from_html($grep_css, $html);
            $html = $this->remove_style($html);
            $html = $this->add_csstohtml($html, $grep_css);
            return $html;
        }

        protected function set_cache() {
            $options = array('lifetime' => 10080, 'caching' => true);
            $this->cache = JCache::getInstance('callback', $options);
            $this->cache->setCaching(true);
            $links = json_encode($this->fullstring);
            $this->cache->store($links, $this->cache_id, $this->params->cachegroup);
        }

        protected function rewrite($html) {
            $set = false;
            if ($this->params->optimizecss > 0) {
                $set = true;
                $html = $this->run_rewrite_css($html);
            }
            if ($this->params->optimizejs > 0) {
                $set = true;
                $html = $this->run_rewrite_js($html);
            }

            if ($set == true)
                $this->set_cache();
            return $html;
        }

        protected function run_rewrite_css($html) {
            $css = '#(?:<link[^>]*href=["|\']([^>]*\.css)(\?[^>]*)?["|\'][^>]*/?>)#isU';
            preg_match_all($css, $html, $match_css);
            $css_convalida = $this->convalida_delete($match_css, $html);
            $html = $this->rewrite_css($css_convalida->fnames, $css_convalida->html);
            return $html;
        }

        protected function run_rewrite_js($html) {
            $js = '#(?:<script[^>]*src=["|\']([^>]*\.js)(\?[^>]*)?["|\'][^>]*/?>.*</script>)#isU';
            preg_match_all($js, $html, $match_js);
            $js_convalida = $this->convalida_delete($match_js, $html);
            $html = $this->rewrite_js($js_convalida->fnames, $js_convalida->html);
            return $html;
        }

        protected function rewrite_css($fnames, $html) {

            if ($this->params->mode == 1) {
                file_put_contents(JPATH_BASE . "/cache/" . $this->params->cachegroup . "/" . $this->cache_id . ".css", '');
            }
            if (!file_exists(JPATH_BASE . "/cache/" . $this->params->cachegroup . "/" . $this->cache_id . ".css") || $this->params->mode == 1) {

                foreach ($fnames as $f => $name) {
                    if (file_exists(JPATH_BASE . "/" . $name) && !in_array($name, $this->params->escludecss)) {
                        $get_file = file_get_contents(JPATH_BASE . "/" . $name);
                        $img_patch = JUri::base() . $name;

                        $unpunto = $this->prepare_url_image_for_css($img_patch, 1);
                        $duepunti = $this->prepare_url_image_for_css($unpunto);

                        $minify = $this->replace_unused_incss($get_file, array('#\.\./#'), array($duepunti), array("./"), array($unpunto));
                        file_put_contents(JPATH_BASE . "/cache/" . $this->params->cachegroup . "/" . $this->cache_id . ".css", $minify, FILE_APPEND);
                    }
                }
                $html = $this->replace_style($html);
            }

            $newhtml = $this->add_csstohtml($html, $this->fullstring);
            return $newhtml;
        }

        protected function rewrite_js($fnames, $html) {

            if ($this->params->mode == 1) {
                file_put_contents(JPATH_BASE . "/cache/" . $this->params->cachegroup . "/" . $this->cache_id . ".js", '');
            }
            if (!file_exists(JPATH_BASE . "/cache/" . $this->params->cachegroup . "/" . $this->cache_id . ".js") || $this->params->mode == 1) {

                foreach ($fnames as $f => $name) {

                    if (file_exists(JPATH_BASE . "/" . $name) && !in_array($name, $this->params->escludejs)) {
                        $get_file = file_get_contents(JPATH_BASE . "/" . $name);
                        $minify = $get_file; //$this->replace_unused_injs($get_file, $name);
                        file_put_contents(JPATH_BASE . "/cache/" . $this->params->cachegroup . "/" . $this->cache_id . ".js", $minify, FILE_APPEND);
                    }
                }
                $html = $this->replace_script($html);
            }
            $newhtml = $this->add_jstohtml($html, $this->fullstring);
            return $newhtml;
        }

        protected function convalida_delete($matches, $html) {
            //arrays di local patch
            $base = array(JURI::base());
            if (JURI::base(true) != "/") {
                $base[] = JURI::base(true) . "/";
            }
            $locals = array("/templates", "/components", "/media", "/modules", "/plugins");
            $fnames = array();
            $hash = "";
            $i = 0;
            $return = new stdClass();
            $remove = array();
            foreach ($matches[1] as $value) {
                foreach (array_merge($locals, $base) as $key => $local) {
                    $countlocal = strlen($local);
                    $checklocal = substr($value, 0, $countlocal);
                    if ($checklocal == $local) {
                        $filename = (in_array($checklocal, $base)) ? substr($value, $countlocal) : $value;
                        $fnames[] = $filename;
                        $remove[] = $matches[0][$i];
                        $this->fullstring[] = $matches[0][$i];
                    }
                }
                $i++;
            }
            $html = $this->remove_from_html($remove, $html);

            $return->html = $html;
            $return->fnames = $fnames;


            return $return;
        }

        //rimuove tutti richiami in linkarray css e js da html
        public function remove_from_html($linkarray, $html) {
            $prepare = array('#^\s*[\r\n]#m');
            foreach ($linkarray as $key => $value) {
                $prepare[] = '#' . preg_quote($value) . '\s*(\r\n)?#s';
            }
            $html = preg_replace($prepare, '', $html);
            return $html;
        }

        protected function prepare_url_image_for_css($array, $cycle = 0) {
            //example ../ or ./
            $prepare_url_image = explode("/", $array);
            $delete = array_pop($prepare_url_image);
            if ($cycle == 1)
                $delete = array_pop($prepare_url_image);

            return implode("/", $prepare_url_image) . "/";
        }

        protected function replace_unused_incss($get_file, $preg_search = array(), $preg_replace = array(), $str_search = array(), $str_replace = array()) {
            if ($this->params->optimizecss == 2) {
                $preg_search = array_merge(array('/\s\s+/', '/\n/', '#/\*.*?\*/#s'), $preg_search);
                $preg_replace = array_merge(array('', '', ''), $preg_replace);
                $str_search = array_merge(array(": ", "; ", " {", " > ", "> ", " >", ", ", ", .", "\n"), $str_search);
                $str_replace = array_merge(array(":", ";", "{", ">", ">", ">", ",", ",.", ""), $str_replace);
            }
            $compress = preg_replace($preg_search, $preg_replace, $get_file);
            $minify = str_replace($str_search, $str_replace, $compress);
            return $minify;
        }

        protected function replace_style($html) {

            $style_html = '#<style.*>(.*)<\/style>#isU';
            return preg_replace_callback($style_html, "self::replacecss_inhtml", $html);
        }

        protected function replace_script($html) {
            $script_html = '@<script.*>(.*)<\/script>@isU';
            return preg_replace_callback($script_html, "self::replacejs_inhtml", $html);
        }

        protected function remove_style($html) {
            $style_html = '#(?:<style.*>(.*)<\/style>\s*(\n|\r)?)#isU';
            return preg_replace($style_html, "", $html);
        }

        protected function remove_script($html) {
            $script_html = '#(?:<script.*>(.*)<\/script>\s*(\n|\r)?)#isU';
            return preg_replace($script_html, "", $html);
        }

        protected function add_csstohtml($html, $cachedlinks) {
            $css = PHP_EOL . '<link href="' . JURI::base() . "cache/" . $this->params->cachegroup . "/" . $this->cache_id . '.css" rel="stylesheet" type="text/css" />' . PHP_EOL;
            return $this->add_tohtml($html, $css, $this->params->escludecss, $cachedlinks);
        }

        protected function add_jstohtml($html, $cachedlinks) {
            $js = PHP_EOL . '<script src="' . JURI::base() . "cache/" . $this->params->cachegroup . "/" . $this->cache_id . '.js" type="text/javascript"></script>' . PHP_EOL;
            return $this->add_tohtml($html, $js, $this->params->escludejs, $cachedlinks);
        }

        protected function add_tohtml($html, $cssorjs, $exluded, $cachedlinks) {
            foreach ($exluded as $key => $value) {
                if (!empty($value)) {
                    $find_escluded = preg_grep("#" . $value . "#", $cachedlinks);
                    foreach ($find_escluded as $f => $esc) {
                        $cssorjs .= $esc . PHP_EOL;
                    }
                }
            }

            $cssorjs .= '</head>';
            $html = preg_replace("#(?:\s*<\s*/\s*head\s*>)#", $cssorjs, $html);
            return $html;
        }

        protected function replacecss_inhtml($code) {

            if (!isset($code[1]))
                return;
            $minify = $this->replace_unused_incss($code[1], array("@<br*>(.*)<br*>@isU"), array(""));
            file_put_contents(JPATH_BASE . "/cache/" . $this->params->cachegroup . "/" . $this->cache_id . ".css", $minify, FILE_APPEND);
        }

        protected function replacejs_inhtml($code) {
            if (!isset($code[1]))
                return;
            $minify = $code[1]; //$this->replace_unused_injs($code[1]);
            file_put_contents(JPATH_BASE . "/cache/" . $this->params->cachegroup . "/" . $this->cache_id . ".js", $minify, FILE_APPEND);
        }

        protected function get_params() {
            $app = JFactory::getApplication('site');
            $template = $app->getTemplate(true);
            $return = new stdClass();
            $return->optimizecss = $template->params->get('optimize_css', false);
            $return->mode = $template->params->get('yrminifier_mode', false);
            $return->escludecss = $template->params->get('yrminifiercss_esclude', array(null));
            $return->optimizejs = $template->params->get('optimize_js', false);
            $return->escludejs = $template->params->get('yrminifierjs_esclude', array(null));
            $tname = $template->params->get('name', false);

            $return->cachegroup = $tname . "_template";
            return $return;
        }

    }
