<?php

/*
 * This file is part of the Behat Testwork.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Behat\Testwork\Argument;

use ReflectionFunctionAbstract;

/**
 * Organises arguments coming from preg_match results.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
final class PregMatchArgumentOrganiser implements ArgumentOrganiser
{
    /**
     * @var ArgumentOrganiser
     */
    private $baseOrganiser;

    /**
     * Initialises organiser.
     *
     * @param ArgumentOrganiser $organiser
     */
    public function __construct(ArgumentOrganiser $organiser)
    {
        $this->baseOrganiser = $organiser;
    }

    /**
     * {@inheritdoc}
     */
    public function organiseArguments(ReflectionFunctionAbstract $function, array $match)
    {
        $arguments = $this->cleanupMatchDuplicates($match);

        return $this->baseOrganiser->organiseArguments($function, $arguments);
    }

    /**
     * Cleans up provided preg_match match into a list of arguments.
     *
     * `preg_match` matches named arguments with named indexes and also
     * represents all arguments with numbered indexes. This method removes
     * duplication and also drops the first full match element from the
     * array.
     *
     * @param array $match
     *
     * @return mixed[]
     */
    private function cleanupMatchDuplicates(array $match)
    {
        $cleanMatch = array_slice($match, 1);
        $arguments = array();

        $keys = array_keys($cleanMatch);
        for ($keyIndex = 0; $keyIndex < count($keys); $keyIndex++) {
            $key = $keys[$keyIndex];

            $arguments[$key] = $cleanMatch[$key];

            if ($this->isKeyAStringAndNexOneIsAnInteger($keyIndex, $keys)) {
                $keyIndex += 1;
            }
        }

        return $arguments;
    }

    /**
     * Checks if key at provided index is a string and next key in the array is an integer.
     *
     * @param integer $keyIndex
     * @param mixed[] $keys
     *
     * @return bool
     */
    private function isKeyAStringAndNexOneIsAnInteger($keyIndex, array $keys)
    {
        $keyIsAString = is_string($keys[$keyIndex]);
        $nextKeyIsAnInteger = isset($keys[$keyIndex + 1]) && is_integer($keys[$keyIndex + 1]);

        return $keyIsAString && $nextKeyIsAnInteger;
    }
}
