<?php

/**
 * Part of the Joomla Framework String Package
 *
 * @copyright  Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE
 */

namespace Joomla\String;

use Doctrine\Common\Inflector\Inflector as DoctrineInflector;

/**
 * Joomla Framework String Inflector Class
 *
 * The Inflector transforms words
 *
 * @since  1.0
 */
class Inflector extends DoctrineInflector
{
    /**
     * The singleton instance.
     *
     * @var    Inflector
     * @since  1.0
     * @deprecated  3.0
     */
    private static $instance;

    /**
     * The inflector rules for countability.
     *
     * @var    array
     * @since  2.0.0
     */
    private static $countable = [
        'rules' => [
            'id',
            'hits',
            'clicks',
        ],
    ];

    /**
     * Adds inflection regex rules to the inflector.
     *
     * @param   mixed   $data      A string or an array of strings or regex rules to add.
     * @param   string  $ruleType  The rule type: singular | plural | countable
     *
     * @return  void
     *
     * @since   1.0
     * @throws  \InvalidArgumentException
     */
    private function addRule($data, string $ruleType)
    {
        if (\is_string($data)) {
            $data = [$data];
        } elseif (!\is_array($data)) {
            throw new \InvalidArgumentException('Invalid inflector rule data.');
        } elseif (!\in_array($ruleType, ['singular', 'plural', 'countable'])) {
            throw new \InvalidArgumentException('Unsupported rule type.');
        }

        if ($ruleType === 'countable') {
            foreach ($data as $rule) {
                // Ensure a string is pushed.
                array_push(self::$countable['rules'], (string) $rule);
            }
        } else {
            static::rules($ruleType, $data);
        }
    }

    /**
     * Adds a countable word.
     *
     * @param   mixed  $data  A string or an array of strings to add.
     *
     * @return  $this
     *
     * @since   1.0
     */
    public function addCountableRule($data)
    {
        $this->addRule($data, 'countable');

        return $this;
    }

    /**
     * Adds a specific singular-plural pair for a word.
     *
     * @param   string  $singular  The singular form of the word.
     * @param   string  $plural    The plural form of the word. If omitted, it is assumed the singular and plural are identical.
     *
     * @return  $this
     *
     * @since   1.0
     * @deprecated  3.0  Use Doctrine\Common\Inflector\Inflector::rules() instead.
     */
    public function addWord($singular, $plural = '')
    {
        trigger_deprecation(
            'joomla/string',
            '2.0.0',
            '%s() is deprecated and will be removed in 3.0, use %s::rules() instead.',
            __METHOD__,
            DoctrineInflector::class
        );

        if ($plural !== '') {
            static::rules(
                'plural',
                [
                    'irregular' => [$plural => $singular],
                ]
            );

            static::rules(
                'singular',
                [
                    'irregular' => [$singular => $plural],
                ]
            );
        } else {
            static::rules(
                'plural',
                [
                    'uninflected' => [$singular],
                ]
            );

            static::rules(
                'singular',
                [
                    'uninflected' => [$singular],
                ]
            );
        }

        return $this;
    }

    /**
     * Adds a pluralisation rule.
     *
     * @param   mixed  $data  A string or an array of regex rules to add.
     *
     * @return  $this
     *
     * @since   1.0
     * @deprecated  3.0  Use Doctrine\Common\Inflector\Inflector::rules() instead.
     */
    public function addPluraliseRule($data)
    {
        trigger_deprecation(
            'joomla/string',
            '2.0.0',
            '%s() is deprecated and will be removed in 3.0, use %s::rules() instead.',
            __METHOD__,
            DoctrineInflector::class
        );

        $this->addRule($data, 'plural');

        return $this;
    }

    /**
     * Adds a singularisation rule.
     *
     * @param   mixed  $data  A string or an array of regex rules to add.
     *
     * @return  $this
     *
     * @since   1.0
     * @deprecated  3.0  Use Doctrine\Common\Inflector\Inflector::rules() instead.
     */
    public function addSingulariseRule($data)
    {
        trigger_deprecation(
            'joomla/string',
            '2.0.0',
            '%s() is deprecated and will be removed in 3.0, use %s::rules() instead.',
            __METHOD__,
            DoctrineInflector::class
        );

        $this->addRule($data, 'singular');

        return $this;
    }

    /**
     * Gets an instance of the Inflector singleton.
     *
     * @param   boolean  $new  If true (default is false), returns a new instance regardless if one exists. This argument is mainly used for testing.
     *
     * @return  static
     *
     * @since   1.0
     * @deprecated  3.0  Use static methods without a class instance instead.
     */
    public static function getInstance($new = false)
    {
        trigger_deprecation(
            'joomla/string',
            '2.0.0',
            '%s() is deprecated and will be removed in 3.0.',
            __METHOD__
        );

        if ($new) {
            return new static();
        }

        if (!\is_object(self::$instance)) {
            self::$instance = new static();
        }

        return self::$instance;
    }

    /**
     * Checks if a word is countable.
     *
     * @param   string  $word  The string input.
     *
     * @return  boolean  True if word is countable, false otherwise.
     *
     * @since   1.0
     */
    public function isCountable($word)
    {
        return \in_array($word, self::$countable['rules']);
    }

    /**
     * Checks if a word is in a plural form.
     *
     * @param   string  $word  The string input.
     *
     * @return  boolean  True if word is plural, false if not.
     *
     * @since   1.0
     */
    public function isPlural($word)
    {
        return $this->toPlural($this->toSingular($word)) === $word;
    }

    /**
     * Checks if a word is in a singular form.
     *
     * @param   string  $word  The string input.
     *
     * @return  boolean  True if word is singular, false if not.
     *
     * @since   1.0
     */
    public function isSingular($word)
    {
        return $this->toSingular($word) === $word;
    }

    /**
     * Converts a word into its plural form.
     *
     * @param   string  $word  The singular word to pluralise.
     *
     * @return  string  The word in plural form.
     *
     * @since   1.0
     * @deprecated  3.0  Use Doctrine\Common\Inflector\Inflector::pluralize() instead.
     */
    public function toPlural($word)
    {
        trigger_deprecation(
            'joomla/string',
            '2.0.0',
            '%s() is deprecated and will be removed in 3.0, use %s::pluralize() instead.',
            __METHOD__,
            DoctrineInflector::class
        );

        return static::pluralize($word);
    }

    /**
     * Converts a word into its singular form.
     *
     * @param   string  $word  The plural word to singularise.
     *
     * @return  string  The word in singular form.
     *
     * @since   1.0
     * @deprecated  3.0  Use Doctrine\Common\Inflector\Inflector::singularize() instead.
     */
    public function toSingular($word)
    {
        trigger_deprecation(
            'joomla/string',
            '2.0.0',
            '%s() is deprecated and will be removed in 3.0, use %s::singularize() instead.',
            __METHOD__,
            DoctrineInflector::class
        );

        return static::singularize($word);
    }
}
