      1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:
                 25:  26:
                 27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:
                 45:  46:  47:  48:  49:  50:  51:  52:  53:  54:
                 55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:
                 75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129:

    <?php

    /*
     * This file is part of the affilinet Product Data PHP SDK.
     *
     * For the full copyright and license information, please view the LICENSE
     * file that was distributed with this source code.
     */

    namespace Affilinet\ProductData\Requests\Helper;

    class Expression implements ExpressionInterface
    {
        public function __construct()
        {
            return $this;
        }

        /**
         * @var string
         */
        private $expression;

        /**
         * @param $keyword string
         * @return Expression
         */
        public function exactly($keyword)
        {
            return $this->combineLiterals(['"'. $keyword . '"'], 'AND');
        }

        /**
         * @param array ...$keywords
         * @return Expression
         */
        public function containsOneOf(...$keywords)
        {
            return $this->combineLiterals($keywords, 'OR');
        }

        /**
         *
         * @param  string     $keyword
         * @return Expression
         */
        public function contains($keyword)
        {
            return $this->combineLiterals([$keyword], 'AND');
        }

        /**
         * @param  array      $keywords
         * @return Expression
         */
        public function containsAllOf(...$keywords)
        {
            return $this->combineLiterals($keywords, 'AND');

        }

        /**
         * @param  array      $keywords
         * @return Expression
         */
        public function containsNot(...$keywords)
        {
            return  $this->combineLiterals($keywords, 'NOT');

        }

        /**
         * @param $keywords
         * @param $operator
         * @return $this
         */
        private function combineLiterals($keywords, $operator)
        {

            if (count($keywords) === 1) {
                switch ($operator) {
                    case 'NOT':
                        $this->expression .= ' NOT "' . $keywords[0] . '" ';

                        return $this;
                }
            }

            $string = '';

            // do not add a leading $operator
            if ($this->expression != '') {

                $string = ' ' . $operator . ' ';
            }

            if (count($keywords) < 1) {
                $string .= ' (';
            }

            $i = 0;
            foreach ($keywords as $keyword) {
                $i++;
                if ($i > 1 || $operator === 'NOT') {
                    $string .= ' '. $operator . ' ';
                }

                $string .= ' '. $keyword . ' ';
            }

            if (count($keywords) < 1) {
                $string .= ')';
            }

            $this->expression  .= $string;

            return $this;
        }

        /**
         * @return string
         */
        public function getExpression()
        {
            return $this->expression;
        }

    }