<?php
/**
 * File containing the Categorie SIT class
 *
 * @copyright Copyright (C) 2014 aYaline. All rights reserved.
 * @version
 */

namespace Ayaline\Bundle\EzSitBundle\eZ\Publish\FieldType\AyalineCategoriesSit;

use eZ\Publish\Core\FieldType\FieldType;
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition;
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType;
use eZ\Publish\Core\FieldType\ValidationError;
use eZ\Publish\SPI\FieldType\Value as SPIValue;
use eZ\Publish\Core\FieldType\Value as BaseValue;

/**
 * The Selection field type.
 *
 * This field type represents a simple string.
 */
class Type extends FieldType
{
    /**
     * The setting keys which are available on this field type.
     *
     * The key is the setting name, and the value is the default value for given
     * setting, set to null if no particular default should be set.
     *
     * @var mixed
     */
    protected $settingsSchema = array(
        'isMultiple' => array(
            'type' => 'bool',
            'default' => false,
        ),
        'categories' => array(
            'type' => 'array',
            'default' => array(),
        ),
    );

    /**
     * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct
     *
     * @param mixed $fieldSettings
     *
     * @return \eZ\Publish\SPI\FieldType\ValidationError[]
     */
    public function validateFieldSettings( $fieldSettings )
    {
        $validationErrors = array();

        foreach ( $fieldSettings as $settingKey => $settingValue )
        {
            switch ( $settingKey )
            {
                case 'isMultiple':
                    if ( !is_bool( $settingValue ) )
                    {
                        $validationErrors[] = new ValidationError(
                            "FieldType '%fieldType%' expects setting %setting% to be a of type %type%",
                            null,
                            array(
                                "fieldType" => $this->getFieldTypeIdentifier(),
                                "setting"   => $settingKey,
                                "type"      => "bool",
                            )
                        );
                    }
                    break;
                case 'options':
                    if ( !is_array( $settingValue ) )
                    {
                        $validationErrors[] = new ValidationError(
                            "FieldType '%fieldType%' expects setting %setting% to be a of type %type%",
                            null,
                            array(
                                "fieldType" => $this->getFieldTypeIdentifier(),
                                "setting"   => $settingKey,
                                "type"      => "hash",
                            )
                        );
                    }
                    break;
                default:
                    $validationErrors[] = new ValidationError(
                        "Setting '%setting%' is unknown",
                        null,
                        array(
                            "setting" => $settingKey
                        )
                    );
            }
        }

        return $validationErrors;
    }

    /**
     * Returns the field type identifier for this field type
     *
     * @return string
     */
    public function getFieldTypeIdentifier()
    {
        return "ezcategoryselection";
    }

    /**
     * Returns the name of the given field value.
     *
     * It will be used to generate content name and url alias if current field is designated
     * to be used in the content name/urlAlias pattern.
     *
     * @param mixed $value
     *
     * @return string
     */
    public function getName( SPIValue $value )
    {
        throw new \RuntimeException( 'Implement this method' );
    }

    /**
     * Returns the fallback default value of field type when no such default
     * value is provided in the field definition in content types.
     *
     * @return \eZ\Publish\Core\FieldType\Selection\Value
     */
    public function getEmptyValue()
    {
        return new Value(array());
    }

    /**
     * Inspects given $inputValue and potentially converts it into a dedicated value object.
     *
     * @param array|\eZ\Publish\Core\FieldType\Selection\Value $inputValue
     *
     * @return \eZ\Publish\Core\FieldType\Selection\Value The potentially converted and structurally plausible value.
     */
    protected function createValueFromInput( $inputValue )
    {
        if ( is_array( $inputValue ) )
        {
            $inputValue = new Value( $inputValue );
        }

        return $inputValue;
    }

    /**
     * Throws an exception if value structure is not of expected format.
     *
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure.
     *
     * @param \eZ\Publish\Core\FieldType\Selection\Value $value
     *
     * @return void
     */
    protected function checkValueStructure( BaseValue $value )
    {
        if ( !is_array( $value->selection ) )
        {
            throw new InvalidArgumentType(
                '$value->selection',
                'array',
                $value->selection
            );
        }
    }

    /**
     * Validates field value against 'isMultiple' and 'options' settings.
     *
     * Does not use validators.
     *
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
     *
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field
     * @param \eZ\Publish\Core\FieldType\Selection\Value $fieldValue The field value for which an action is performed
     *
     * @return \eZ\Publish\SPI\FieldType\ValidationError[]
     */
    public function validate( FieldDefinition $fieldDefinition, SPIValue $fieldValue )
    {
        $validationErrors = array();

        if ( $this->isEmptyValue( $fieldValue ) )
        {
            return $validationErrors;
        }

        $fieldSettings = $fieldDefinition->getFieldSettings();

        if ( ( !isset( $fieldSettings["isMultiple"] ) || $fieldSettings["isMultiple"] === false )
            && count( $fieldValue->selection ) > 1 )
        {
            $validationErrors[] = new ValidationError(
                "Field definition does not allow multiple options to be selected.",
                null,
                array()
            );
        }

        foreach ( $fieldValue->selection as $optionIndex )
        {
            if ( !isset( $fieldSettings["options"][$optionIndex] ) )
            {
                $validationErrors[] = new ValidationError(
                    "Option with index %index% does not exist in the field definition.",
                    null,
                    array(
                        "index" => $optionIndex
                    )
                );
            }
        }

        return $validationErrors;
    }

    /**
     * Returns information for FieldValue->$sortKey relevant to the field type.
     *
     * @param \eZ\Publish\Core\FieldType\Selection\Value $value
     *
     * @return array
     */
    protected function getSortInfo( BaseValue $value )
    {
        return implode( '-', $value->selection );
    }

    /**
     * Converts an $hash to the Value defined by the field type
     *
     * @param mixed $hash
     *
     * @return \eZ\Publish\Core\FieldType\Selection\Value $value
     */
    public function fromHash( $hash )
    {
        if ($hash != null && $hash !== "") {
            $hash = explode('-', $hash);
        } else {
            $hash = array();
        }
        return new Value( $hash );
    }

    /**
     * Converts a $Value to a hash
     *
     * @param \eZ\Publish\Core\FieldType\Selection\Value $value
     *
     * @return mixed
     */
    public function toHash( SPIValue $value )
    {
        return $value->selection;
    }

    /**
     * Returns whether the field type is searchable
     *
     * @return boolean
     */
    public function isSearchable()
    {
        return true;
    }
}