<?php

/**
 * This file is part of the Peast package
 *
 * (c) Marco Marchiò <marco.mm89@gmail.com>
 *
 * For the full copyright and license information refer to the LICENSE file
 * distributed with this source code
 */
namespace HelpGent\Peast\Selector\Node\Part;

use HelpGent\Peast\Syntax\Node\Node;
use HelpGent\Peast\Syntax\Utils;
/**
 * Selector part attribute class
 * 
 * @author Marco Marchiò <marco.mm89@gmail.com>
 */
class Attribute extends Part
{
    /**
     * Priority
     *
     * @var int
     */
    protected $priority = 4;
    /**
     * Attribute names
     *
     * @var array
     */
    protected $names = array();
    /**
     * Attribute match operator
     *
     * @var array
     */
    protected $operator = null;
    /**
     * Attribute value
     *
     * @var mixed
     */
    protected $value = null;
    /**
     * Case insensitive flag
     *
     * @var bool
     */
    protected $caseInsensitive = \false;
    /**
     * Regex flag
     *
     * @var bool
     */
    protected $regex = \false;
    /**
     * Adds a name
     *
     * @param string $name Name
     *
     * @return $this
     */
    public function addName($name)
    {
        $this->names[] = $name;
        return $this;
    }
    /**
     * Sets the operator
     *
     * @param string $operator Operator
     *
     * @return $this
     */
    public function setOperator($operator)
    {
        $this->operator = $operator;
        return $this;
    }
    /**
     * Sets the value
     *
     * @param mixed $value Value
     *
     * @return $this
     */
    public function setValue($value)
    {
        $this->value = $value;
        return $this;
    }
    /**
     * Sets the case insensitive flag
     *
     * @param bool $caseInsensitive Case insensitive flag
     *
     * @return $this
     */
    public function setCaseInsensitive($caseInsensitive)
    {
        $this->caseInsensitive = $caseInsensitive;
        return $this;
    }
    /**
     * Sets the regex flag
     *
     * @param bool $regex Regex flag
     *
     * @return $this
     */
    public function setRegex($regex)
    {
        $this->regex = $regex;
        return $this;
    }
    /**
     * Returns true if the selector part matches the given node,
     * false otherwise
     *
     * @param Node $node    Node
     * @param Node $parent  Parent node
     *
     * @return bool
     */
    public function check(Node $node, $parent = null)
    {
        $attr = $node;
        foreach ($this->names as $name) {
            $attrFound = \false;
            if ($attr instanceof Node) {
                $props = Utils::getNodeProperties($attr);
                foreach ($props as $prop) {
                    if ($prop["name"] === $name) {
                        $attrFound = \true;
                        $attr = $attr->{$prop["getter"]}();
                        break;
                    }
                }
            }
            if (!$attrFound) {
                return \false;
            }
        }
        $bothStrings = \is_string($attr) && \is_string($this->value);
        switch ($this->operator) {
            case "=":
                if ($bothStrings) {
                    if ($this->regex) {
                        return \preg_match($this->value, $attr);
                    }
                    return $this->compareStr($this->value, $attr, $this->caseInsensitive, \true, \true);
                }
                if (\is_int($attr) && \is_float($this->value)) {
                    return (float) $attr === $this->value;
                }
                return $attr === $this->value;
            case "<":
                if (\is_float($this->value) && !\is_float($attr) && !\is_int($attr) && !\is_string($attr)) {
                    return \false;
                }
                return $attr < $this->value;
            case ">":
                if (\is_float($this->value) && !\is_float($attr) && !\is_int($attr) && !\is_string($attr)) {
                    return \false;
                }
                return $attr > $this->value;
            case "<=":
                if (\is_float($this->value) && !\is_float($attr) && !\is_int($attr) && !\is_string($attr)) {
                    return \false;
                }
                return $attr <= $this->value;
            case ">=":
                if (\is_float($this->value) && !\is_float($attr) && !\is_int($attr) && !\is_string($attr)) {
                    return \false;
                }
                return $attr >= $this->value;
            case "^=":
            case "\$=":
            case "*=":
                return $this->compareStr($this->value, $attr, $this->caseInsensitive, $this->operator === "^=", $this->operator === "\$=");
            default:
                return \true;
        }
    }
    /**
     * Compares two strings
     *
     * @param string $v1                Search value
     * @param string $v2                Compare value
     * @param bool   $caseInsensitive   True if the search must be case insensitive
     * @param bool   $matchStart        True if the search must be executed from the
     *                                  beginning of the string
     * @param bool   $matchEnd          True if the search must be executed from the
     *                                  end of the string
     *
     * @return bool
     */
    protected function compareStr($v1, $v2, $caseInsensitive, $matchStart, $matchEnd)
    {
        $regex = "#" . ($matchStart ? "^" : "") . \preg_quote($v1) . ($matchEnd ? "\$" : "") . "#u" . ($caseInsensitive ? "i" : "");
        return (bool) \preg_match($regex, $v2);
    }
}
