<?php 
 
/* 
 * This file is part of Sulu. 
 * 
 * (c) Sulu GmbH 
 * 
 * This source file is subject to the MIT license that is bundled 
 * with this source code in the file LICENSE. 
 */ 
 
namespace Sulu\Component\Content\Compat; 
 
use Sulu\Component\Content\Document\Structure\PropertyValue; 
use Sulu\Component\Util\ArrayableInterface; 
 
/** 
 * Property of Structure generated from Structure Manager to map a template. 
 */ 
class Property implements PropertyInterface, \JsonSerializable 
{ 
    /** 
     * name of property. 
     * 
     * @var string 
     */ 
    private $name; 
 
    /** 
     * @var Metadata 
     */ 
    private $metadata; 
 
    /** 
     * is property mandatory. 
     * 
     * @var bool 
     */ 
    private $mandatory; 
 
    /** 
     * is property multilingual. 
     * 
     * @var bool 
     */ 
    private $multilingual; 
 
    /** 
     * min occurs of property value. 
     * 
     * @var int 
     */ 
    private $minOccurs; 
 
    /** 
     * max occurs of property value. 
     * 
     * @var int 
     */ 
    private $maxOccurs; 
 
    /** 
     * name of content type. 
     * 
     * @var string 
     */ 
    private $contentTypeName; 
 
    /** 
     * parameter of property to merge with parameter of content type. 
     * 
     * @var array 
     */ 
    private $params; 
 
    /** 
     * tags defined in xml. 
     * 
     * @var PropertyTag[] 
     */ 
    private $tags; 
 
    /** 
     * column span. 
     * 
     * @var string 
     */ 
    private $colSpan; 
 
    /** 
     * value of property. 
     * 
     * @var mixed 
     */ 
    private $value; 
 
    /** 
     * @var StructureInterface 
     */ 
    private $structure; 
 
    /** 
     * Constructor. 
     * 
     * @var PropertyValue 
     */ 
    protected $propertyValue; 
 
    /** 
     * properties managed by this block. 
     * 
     * @var PropertyType[] 
     */ 
    protected $types = []; 
 
    /** 
     * @var PropertyType[] 
     */ 
    protected $properties = []; 
 
    /** 
     * @var string|null 
     */ 
    protected $defaultTypeName; 
 
    public function __construct( 
        $name, 
        $metaData, 
        $contentTypeName, 
        $mandatory = false, 
        $multilingual = true, 
        $maxOccurs = 1, 
        $minOccurs = 1, 
        $params = [], 
        $tags = [], 
        $colSpan = null, 
        $defaultTypeName = null 
    ) { 
        $this->contentTypeName = $contentTypeName; 
        $this->mandatory = $mandatory; 
        $this->maxOccurs = $maxOccurs; 
        $this->minOccurs = $minOccurs; 
        $this->multilingual = $multilingual; 
        $this->name = $name; 
        $this->metadata = new Metadata($metaData); 
        $this->params = $params; 
        $this->tags = $tags; 
        $this->colSpan = $colSpan; 
        $this->defaultTypeName = $defaultTypeName; 
    } 
 
    public function setPropertyValue(PropertyValue $propertyValue) 
    { 
        $this->propertyValue = $propertyValue; 
    } 
 
    /** 
     * returns name of template. 
     * 
     * @return string 
     */ 
    public function getName() 
    { 
        return $this->name; 
    } 
 
    /** 
     * returns mandatory. 
     * 
     * @return bool 
     */ 
    public function isMandatory() 
    { 
        return $this->mandatory; 
    } 
 
    /** 
     * returns multilingual. 
     * 
     * @return bool 
     */ 
    public function isMultilingual() 
    { 
        return $this->multilingual; 
    } 
 
    /** 
     * return min occurs. 
     * 
     * @return int 
     */ 
    public function getMinOccurs() 
    { 
        return $this->minOccurs; 
    } 
 
    /** 
     * return max occurs. 
     * 
     * @return int 
     */ 
    public function getMaxOccurs() 
    { 
        return $this->maxOccurs; 
    } 
 
    /** 
     * returns field is mandatory. 
     * 
     * @return bool 
     */ 
    public function getMandatory() 
    { 
        return $this->mandatory; 
    } 
 
    /** 
     * returns field is multilingual. 
     * 
     * @return bool 
     */ 
    public function getMultilingual() 
    { 
        return $this->multilingual; 
    } 
 
    /** 
     * returns tags defined in xml. 
     * 
     * @return \Sulu\Component\Content\Compat\PropertyTag[] 
     */ 
    public function getTags() 
    { 
        return $this->tags; 
    } 
 
    /** 
     * returns tag with given name. 
     * 
     * @param string $tagName 
     * 
     * @return PropertyTag 
     */ 
    public function getTag($tagName) 
    { 
        return $this->tags[$tagName]; 
    } 
 
    /** 
     * add a property tag. 
     * 
     * @return PropertyTag 
     */ 
    public function addTag(PropertyTag $tag) 
    { 
        return $this->tags[$tag->getName()] = $tag; 
    } 
 
    /** 
     * return true if a tag with the given name exists. 
     * 
     * @return bool 
     */ 
    public function hasTag($tagName) 
    { 
        return isset($this->tags[$tagName]); 
    } 
 
    /** 
     * returns column span. 
     * 
     * @return string 
     */ 
    public function getColSpan() 
    { 
        return $this->colSpan; 
    } 
 
    /** 
     * returns title of property. 
     * 
     * @param string $languageCode 
     * 
     * @return string 
     */ 
    public function getTitle($languageCode) 
    { 
        return $this->metadata->get('title', $languageCode, \ucfirst($this->name)); 
    } 
 
    /** 
     * returns infoText of property. 
     * 
     * @param string $languageCode 
     * 
     * @return string 
     */ 
    public function getInfoText($languageCode) 
    { 
        return $this->metadata->get('info_text', $languageCode, ''); 
    } 
 
    /** 
     * returns placeholder of property. 
     * 
     * @param string $languageCode 
     * 
     * @return string 
     */ 
    public function getPlaceholder($languageCode) 
    { 
        return $this->metadata->get('placeholder', $languageCode, ''); 
    } 
 
    /** 
     * sets the value from property. 
     */ 
    public function setValue($value) 
    { 
        if ($this->propertyValue) { 
            $this->propertyValue->setValue($value); 
        } 
 
        $this->value = $value; 
    } 
 
    public function setValueByReference(&$value) 
    { 
        $this->value = $value; 
    } 
 
    /** 
     * gets the value from property. 
     */ 
    public function getValue() 
    { 
        if ($this->propertyValue) { 
            return $this->propertyValue->getValue(); 
        } 
 
        return $this->value; 
    } 
 
    /** 
     * returns name of content type. 
     * 
     * @return string 
     */ 
    public function getContentTypeName() 
    { 
        return $this->contentTypeName; 
    } 
 
    /** 
     * parameter of property. 
     * 
     * @return array 
     */ 
    public function getParams() 
    { 
        return $this->params; 
    } 
 
    /** 
     * returns TRUE if property is a block. 
     * 
     * @return bool 
     */ 
    public function getIsBlock() 
    { 
        return false; 
    } 
 
    /** 
     * returns TRUE if property is multiple. 
     * 
     * @return bool 
     */ 
    public function getIsMultiple() 
    { 
        $minOccurs = $this->getMinOccurs(); 
        $maxOccurs = $this->getMaxOccurs(); 
 
        if (\is_null($minOccurs) && \is_null($maxOccurs)) { 
            // if no occurs attributes are set it defaults to false 
            return false; 
        } 
 
        if (0 === $minOccurs && 1 === $maxOccurs) { 
            // this one allows to have an optional field 
            return true; 
        } 
 
        if (1 === $minOccurs && 1 === $maxOccurs) { 
            // if the occurences have a high and low limit of 1 it should be displayed as single 
            return false; 
        } 
 
        // if minOccurs is set a value of 1 is enough, because maxOccurs would be considered "unbound" when null 
        return $minOccurs >= 1 || $maxOccurs > 1; 
    } 
 
    /** 
     * @return Metadata 
     */ 
    public function getMetadata() 
    { 
        return $this->metadata; 
    } 
 
    /** 
     * returns structure. 
     * 
     * @return StructureInterface 
     */ 
    public function getStructure() 
    { 
        return $this->structure; 
    } 
 
    /** 
     * @param StructureInterface $structure 
     */ 
    public function setStructure($structure) 
    { 
        $this->structure = $structure; 
    } 
 
    /** 
     * magic getter for twig templates. 
     * 
     * @param string $property 
     */ 
    public function __get($property) 
    { 
        if (\method_exists($this, 'get' . \ucfirst($property))) { 
            return $this->{'get' . \ucfirst($property)}(); 
        } else { 
            return; 
        } 
    } 
 
    #[\ReturnTypeWillChange] 
    public function jsonSerialize() 
    { 
        $result = [ 
            'name' => $this->getName(), 
            'metadata' => $this->getMetadata()->getData(), 
            'mandatory' => $this->getMandatory(), 
            'multilingual' => $this->getMultilingual(), 
            'minOccurs' => $this->getMinOccurs(), 
            'maxOccurs' => $this->getMaxOccurs(), 
            'contentTypeName' => $this->getContentTypeName(), 
            'params' => $this->getParams(), 
            'tags' => [], 
        ]; 
        foreach ($this->getTags() as $tag) { 
            $result['tags'][] = [ 
                'name' => $tag->getName(), 
                'priority' => $tag->getPriority(), 
            ]; 
        } 
 
        return $result; 
    } 
 
    public function __clone() 
    { 
        $clone = new self( 
            $this->getName(), 
            $this->getMetadata(), 
            $this->getContentTypeName(), 
            $this->getMandatory(), 
            $this->getMultilingual(), 
            $this->getMaxOccurs(), 
            $this->getMinOccurs(), 
            $this->getParams(), 
            $this->getTags(), 
            $this->getColSpan(), 
            $this->getDefaultTypeName() 
        ); 
 
        $clone->types = []; 
        foreach ($this->types as $type) { 
            $clone->addType(clone $type); 
        } 
 
        $clone->setValue($this->getValue()); 
 
        return $clone; 
    } 
 
    public function toArray($depth = null) 
    { 
        if ($this->getValue() instanceof ArrayableInterface) { 
            return $this->getValue()->toArray($depth); 
        } else { 
            return $this->getValue(); 
        } 
    } 
 
    public function getTypes() 
    { 
        return $this->types; 
    } 
 
    public function addType($type) 
    { 
        $this->types[$type->getName()] = $type; 
    } 
 
    public function getType($name) 
    { 
        if (!$this->hasType($name)) { 
            throw new \InvalidArgumentException( 
                \sprintf( 
                    'The block type "%s" has not been registered. Known block types are: [%s]', 
                    $name, 
                    \implode(', ', \array_keys($this->types)) 
                ) 
            ); 
        } 
 
        return $this->types[$name]; 
    } 
 
    public function hasType($name) 
    { 
        return isset($this->types[$name]); 
    } 
 
    public function getDefaultTypeName() 
    { 
        return $this->defaultTypeName; 
    } 
 
    /** 
     * returns child properties of given Type. 
     * 
     * @param string $typeName 
     * 
     * @return PropertyInterface[] 
     */ 
    public function getTypeChildProperties($typeName) 
    { 
        return $this->getType($typeName)->getChildProperties(); 
    } 
 
    public function initProperties($index, $typeName) 
    { 
        $type = $this->getType($typeName); 
        $this->properties[$index] = clone $type; 
 
        return $this->properties[$index]; 
    } 
 
    public function clearProperties() 
    { 
        $this->properties = []; 
    } 
 
    public function getProperties($index) 
    { 
        if (!isset($this->properties[$index])) { 
            throw new \OutOfRangeException(\sprintf( 
                'No properties at index "%s" in block "%s". Valid indexes: [%s]', 
                $index, $this->getName(), \implode(', ', \array_keys($this->properties)) 
            )); 
        } 
 
        return $this->properties[$index]; 
    } 
 
    public function getLength() 
    { 
        return \count($this->properties); 
    } 
}