<?php
/**
* @package   Warp Theme Framework
* @author    YOOtheme http://www.yootheme.com
* @copyright Copyright (C) YOOtheme GmbH
* @license   http://www.gnu.org/licenses/gpl.html GNU/GPL
*/

namespace Warp\Asset\Filter;

/**
 * Convert CSS to work right to left
 */
class CssRtlFilter implements FilterInterface
{
    public static $propertyMap = array(
        'margin-left' => 'margin-right',
        'margin-right' => 'margin-left',

        'padding-left' => 'padding-right',
        'padding-right' => 'padding-left',

        'border-left' => 'border-right',
        'border-right' => 'border-left',

        'border-left-color' => 'border-right-color',
        'border-right-color' => 'border-left-color',
        'border-left-width' => 'border-right-width',
        'border-right-width' => 'border-left-width',
        'border-left-style' => 'border-right-style',
        'border-right-style' => 'border-left-style',

        'border-bottom-right-radius' => 'border-bottom-left-radius',
        'border-bottom-left-radius' => 'border-bottom-right-radius',

        'border-top-right-radius' => 'border-top-left-radius',
        'border-top-left-radius' => 'border-top-right-radius',

        'left' => 'right',
        'right' => 'left'
    );

    public static $valueMap = array(
        'padding' => 'quad',
        'margin' => 'quad',
        'text-align' => 'rtltr',
        'float' => 'rtltr',
        'clear' => 'rtltr',
        'direction' => 'direction',
        'border-radius' => 'quad_radius',
        'border-color' => 'quad',
        'border-width' => 'quad',
        'border-style' => 'quad',
        'background-position' => 'bgPosition',
        'box-shadow' => 'boxShadow',
        'background' => 'background',
        'background-image' => 'backgroundImage',
        'transform' => 'transform',
        '-ms-transform' => 'transform',
        '-webkit-transform' => 'transform',
        'content' => 'icons'
    );

    public static $iconMap = array(
        "\\f04e" => "\\f04a",
        "\\f04a" => "\\f04e",

        // .uk-icon-fast-forward
        // .uk-icon-fast-backward
        "\\f050" => "\\f049",
        "\\f049" => "\\f050",

        // .uk-icon-step-forward
        // .uk-icon-step-backward
        "\\f051" => "\\f048",
        "\\f048" => "\\f051",

        // .uk-icon-mail-forward
        // .uk-icon-share
        // .uk-icon-mail-reply
        // .uk-icon-reply
        "\\f064" => "\\f112",
        "\\f112" => "\\f064",

        // .uk-icon-rotate-left
        // .uk-icon-undo
        // .uk-icon-rotate-right
        // .uk-icon-repeat
        "\\f0e2" => "\\f01e",
        "\\f01e" => "\\f0e2",

        // .uk-icon-align-left
        // .uk-icon-align-right
        "\\f036" => "\\f038",
        "\\f038" => "\\f036",

        // .uk-icon-chevron-left
        // .uk-icon-chevron-right
        "\\f053" => "\\f054",
        "\\f054" => "\\f053",

        // .uk-icon-arrow-left
        // .uk-icon-arrow-right
        "\\f060" => "\\f061",
        "\\f061" => "\\f060",

        // .uk-icon-hand-o-left
        // .uk-icon-hand-o-right
        "\\f0a5" => "\\f0a4",
        "\\f0a4" => "\\f0a5",

        // .uk-icon-arrow-circle-left
        // .uk-icon-arrow-circle-right
        "\\f0a8" => "\\f0a9",
        "\\f0a9" => "\\f0a8",

        // .uk-icon-caret-left
        // .uk-icon-caret-right
        "\\f0d9" => "\\f0da",
        "\\f0da" => "\\f0d9",

        // .uk-icon-angle-double-left
        // .uk-icon-angle-double-right
        "\\f100" => "\\f101",
        "\\f101" => "\\f100",

        // .uk-icon-angle-left
        // .uk-icon-angle-right
        "\\f104" => "\\f105",
        "\\f105" => "\\f104",

        // .uk-icon-quote-left
        // .uk-icon-quote-right
        "\\f10d" => "\\f10e",
        "\\f10e" => "\\f10d",

        // .uk-icon-chevron-circle-left
        // .uk-icon-chevron-circle-right
        "\\f137" => "\\f138",
        "\\f138" => "\\f137",

        // .uk-icon-long-arrow-left
        // .uk-icon-long-arrow-right
        "\\f177" => "\\f178",
        "\\f178" => "\\f177",

        // .uk-icon-arrow-circle-o-left
        // .uk-icon-arrow-circle-o-right
        "\\f190" => "\\f18e",
        "\\f18e" => "\\f190",

        // .uk-icon-toggle-left
        // .uk-icon-caret-square-o-left
        // .uk-icon-toggle-right
        // .uk-icon-caret-square-o-right
        "\\f191" => "\\f152",
        "\\f152" => "\\f191"
    );


    /**
     * Filter callbacks
     */
    public function filterLoad($asset) {}

    public function filterContent($asset)
    {
        $asset->setContent($this->process($asset->getContent()));
    }

    /**
     * Convert CSS to be right to left
     *
     * @param string $css
     *
     * @return string
     */
    public function process($css)
    {

        $css = trim($css); // give it a solid trimming to start

        $css = preg_replace('/\/\*[\s\S]+?\*\//', '', $css);      // comments
        $css = preg_replace('/[\n\r]/', '', $css);                // line breaks and carriage returns
        $css = preg_replace('/\s*([:;,{}])\s*/', '$1', $css);     // space between selectors, declarations, properties and values
        $css = preg_replace('/\s+/', ' ', $css);                 // replace multiple spaces with single spaces

        $that = $this;

        $css = preg_replace_callback('/([^;:\{\}]+?)\:([^;:\{\}]+?)([;}])/i', function($arr) use ($that) {

            list($all, $prop, $val, $end) = $arr;

            $isImportant = strpos($val, '!important');

            if (isset($that::$propertyMap[$prop])) {
                $prop = $that::$propertyMap[$prop];
            }

            if (array_key_exists($prop, $that::$valueMap)) {
                $method = $that::$valueMap[$prop];
                $val = $that->{$method}($val);
            }

            if (strpos($val, '!important') === false && $isImportant) $val .= '!important';

            return $prop . ':' . $val . $end;
        }, $css);

        return $css;
    }

    public function icons($v) {
        // skip if there is definitely no icon
        if (strpos($v, "\\f") === false) {
            return $v;
        }

        // check all possible icons
        foreach ($this::$iconMap as $key => $replace) {
            if(strpos($v, $key) !== false) {
                return str_replace($key, $replace, $v);
            }
        }

        return $v;
    }

    public function quad($v) {
        // 1px 2px 3px 4px => 1px 4px 3px 2px
        $m = explode(' ', trim($v));
        if ($m && count($m) == 4) {
            return implode(' ', array($m[0], $m[3], $m[2], $m[1]));
        }
        return $v;
    }


    public function quad_radius($v) {
        $m = explode('/\s+/', trim($v));
        // 1px 2px 3px 4px => 1px 2px 4px 3px
        // since border-radius: top-left top-right bottom-right bottom-left
        // will be border-radius: top-right top-left bottom-left bottom-right
        if ($m && count($m) == 4) {
            return implode(' ', array($m[1], $m[0], $m[3], $m[2]));
        } else if ($m && count($m) == 3) {
            // 5px 10px 20px => 10px 5px 10px 20px
            return implode(' ', array($m[1], $m[0], $m[1], $m[2]));
        }
        return $v;
    }

    public function direction($v) {
        return (preg_match('/ltr/', $v) ? 'rtl' : (preg_match('/rtl/', $v) ? 'ltr' : $v));
    }

    public function bracketCommaSplit($str) {
        /* <prop1>(<args1>), <prop2>(<args2>) -> ["<prop1>(<args1>)", "<prop2>(<args2>)"]*/
        $parenthesisCount = 0;
        $lastSplit = 0;
        $arr = array();
        for($i = 0; $i<strlen($str); ++$i) {
            $c = $str[$i];
            $parenthesisCount += ($c == '(' ? 1 : ( $c == ')' ? -1 : 0));
            if (($c==',' && $parenthesisCount==0) || $i==strlen($str)-1) {
                $arr[]     = trim(trim(trim(substr($str ,$lastSplit, $i-$lastSplit+1)), ','));
                $lastSplit = $i+1; // +1 to get rid of the comma
            }
        }
        return $arr;
    }

    public function rtltr($v) {
        if (preg_match('/left/', $v)) return 'right';
        if (preg_match('/right/', $v)) return 'left';
        return $v;
    }

    public function bgPosition($v) {
        if (preg_match('/\bleft\b/', $v)) {
            $v = preg_replace('/\bleft\b/', 'right', $v);
        } else if (preg_match('/\bright\b/', $v)) {
            $v = preg_replace('/\bright\b/', 'left', $v);
        }
        $m = explode(' ', trim($v));
        if ($m && (count($m) == 1) && preg_match('/(\d+)([a-z]{2}|%)/', $v)) {
            $v = 'right ' . $v;
        }
        if ($m && count($m) == 2 && preg_match('/\d+%/', $m[0])) {
            // 30% => 70% (100 - x)
            $v = (100 - (int)$m[0]) . '% ' . $m[1];
        }
        preg_match('/(\-?\d+)px/', $m[0], $pxmatch);
        if($m && count($m) == 2 && $pxmatch) {
            $x = $pxmatch[1];
            $minusX = ($x=='0' ? '0' : ((int)$x < 0 ? substr($x, 1) . 'px' : '-' . $x . 'px'));
            $v = $minusX . ' ' . $m[1];
        }
        return $v;
    }

    public function boxShadow($v) {
        $shadowRtl = function($shadow) {
            // multiplies <left> offset with -1
            $found = false;
            $parts = explode(' ', $shadow);
            for ($i = 0; $i<count($parts); ++$i) {
                $el = $parts[$i];
                if (!$found && preg_match('/\d/', $el)) {
                    $found = true;
                    $parts[$i] = ($el[0] == "0" ? 0 : ($el[0] == "-" ? substr($el, 1) : "-".$el));
                }
            }
            return implode(' ', $parts);
        };
        $arr = $this->bracketCommaSplit($v);

        $v = implode(',', array_map($shadowRtl, $arr));

        return $v;
    }

    public function backgroundImage($val) {
      $parseSingle = function($v) {
        if(substr($v, 0, 4) == "url(") {
          // dont mess with background image paths for now
          return $v;
        }
        if (strpos($v, "gradient")) {
          $v = preg_replace_callback('/(left|right)/', function($dir) {
             return $dir === 'left' ? 'right' : 'left';
          }, $v);
          $v = preg_replace_callback('/(\d+deg)/', function($el) {

            $num = (int)preg_replace('/deg/', '', $el[0]);

            return (180-$num).'deg';
          }, $v);
         }
        return $v;
      };
      $arr = $this->bracketCommaSplit($val);
      return implode(',', array_map($parseSingle, $arr));
    }

    public function background($v) {
      // TODO: split several background layers (divided by comma)

      $that = $this;
      $parseSingle = function($v) use ($that) {
        // background-image
        $method = function($val) use ($that) {
            $str = $val[0];
            return $that->backgroundImage($str);
        };
        $v = preg_replace_callback('/url\((.*?)\)|none|([^\s]*?gradient.*?\(.+\))/i', $method, $v);

        // background-position
        $v = preg_replace_callback('/\s(left|right|center|top|bottom|-?\d+([a-zA-Z]{2}|%?))\s(left|right|center|top|bottom|-?\d+([a-zA-Z]{2}|%?))[;\s]?/i',
            function($arr) use ($that) {
                return ' '.$that->bgPosition(trim($arr[0], ';')).(strpos($arr[0], ';') !== false ? ';' : ' ');
            }, $v);
        return $v;
      };
      $arr = $this->bracketCommaSplit($v);
      return implode(',', array_map($parseSingle, $arr));
    }


    public function transform($val) {

        $negateValue = function($valString) {
            return ($valString[0] == "0" ? 0 : ($valString[0] == "-" ? substr($valString, 1) : "-".$valString));
        };

        // translate, translateX, translate3D
        $translateRegex = '/(translate(X|x|3D|3d)?)\(([^,\)]+)([^\)]*)\)/';
        $translateCallback = function($matches) use ($negateValue) {

            $value = trim($matches[3]);
            $remainder = $matches[4];
            $res  = $matches[1].'('.$negateValue($value).($remainder ? $remainder : '').')';
            return $res;
        };
        $val = preg_replace_callback($translateRegex, $translateCallback, $val);

        // rotate
        $rotateRegex = '/rotate\(\s*(\d+)\s*deg\s*\)/';
        $rotateCallback = function($matches) {
            $angle = (int)$matches[1];
            $mirrored = 180 - $angle;
            return 'rotate('.$mirrored.'deg)';
        };
        $val = preg_replace_callback($rotateRegex, $rotateCallback, $val);


        return $val;
    }

}
