<?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\Structure; 
 
use Sulu\Bundle\DocumentManagerBundle\Bridge\DocumentInspector; 
use Sulu\Component\Content\Compat\StructureInterface; 
use Sulu\Component\Content\Compat\StructureType; 
use Sulu\Component\Content\Document\Behavior\ExtensionBehavior; 
use Sulu\Component\Content\Document\Behavior\NavigationContextBehavior; 
use Sulu\Component\Content\Document\Behavior\OrderBehavior; 
use Sulu\Component\Content\Document\Behavior\RedirectTypeBehavior; 
use Sulu\Component\Content\Document\Behavior\ResourceSegmentBehavior; 
use Sulu\Component\Content\Document\Behavior\ShadowLocaleBehavior; 
use Sulu\Component\Content\Document\Behavior\StructureBehavior; 
use Sulu\Component\Content\Document\Behavior\WorkflowStageBehavior; 
use Sulu\Component\Content\Document\LocalizationState; 
use Sulu\Component\Content\Document\RedirectType; 
use Sulu\Component\Content\Document\WorkflowStage; 
use Sulu\Component\Content\Metadata\StructureMetadata; 
 
/** 
 * @deprecated Should be replaced by a proper StructureInterface implementation 
 */ 
class StructureBridge implements StructureInterface 
{ 
    /** 
     * @var StructureMetadata 
     */ 
    protected $structure; 
 
    /** 
     * @var object 
     */ 
    protected $document; 
 
    /** 
     * @var DocumentInspector 
     */ 
    protected $inspector; 
 
    /** 
     * @var LegacyPropertyFactory 
     */ 
    private $propertyFactory; 
 
    /** 
     * @var array 
     */ 
    private $loadedProperties = []; 
 
    /** 
     * Needed by structure extensions when the document has not been set.. 
     * 
     * @var string 
     */ 
    protected $locale; 
 
    /** 
     * @param object $document 
     */ 
    public function __construct( 
        StructureMetadata $structure, 
        DocumentInspector $inspector, 
        LegacyPropertyFactory $propertyFactory, 
        $document = null 
    ) { 
        $this->structure = $structure; 
        $this->inspector = $inspector; 
        $this->propertyFactory = $propertyFactory; 
        $this->document = $document; 
    } 
 
    public function setDocument(StructureBehavior $document) 
    { 
        $this->document = $document; 
    } 
 
    public function setLanguageCode($locale) 
    { 
        $this->locale = $locale; 
    } 
 
    public function getLanguageCode() 
    { 
        if (!$this->document) { 
            return $this->locale; 
        } 
 
        return $this->inspector->getLocale($this->getDocument()); 
    } 
 
    public function setWebspaceKey($webspace) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getWebspaceKey() 
    { 
        if (!$this->document) { 
            return null; 
        } 
 
        return $this->inspector->getWebspace($this->getDocument()); 
    } 
 
    public function getUuid() 
    { 
        return $this->getDocument()->getUuid(); 
    } 
 
    public function setUuid($uuid) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getCreator() 
    { 
        return $this->getDocument()->getCreator(); 
    } 
 
    public function setCreator($userId) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getChanger() 
    { 
        return $this->getDocument()->getChanger(); 
    } 
 
    public function setChanger($userId) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getCreated() 
    { 
        return $this->getDocument()->getCreated(); 
    } 
 
    public function setCreated(\DateTime $created) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getChanged() 
    { 
        return $this->getDocument()->getChanged(); 
    } 
 
    public function setChanged(\DateTime $changed) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getKey() 
    { 
        return $this->structure->getName(); 
    } 
 
    public function getInternal() 
    { 
        return $this->structure->isInternal(); 
    } 
 
    public function getProperty($name) 
    { 
        if ($this->hasProperty($name)) { 
            $property = $this->structure->getProperty($name); 
        } else { 
            $property = $this->structure->getChild($name); 
        } 
 
        return $this->createLegacyPropertyFromItem($property); 
    } 
 
    public function hasProperty($name) 
    { 
        return $this->structure->hasProperty($name); 
    } 
 
    public function getProperties($flatten = false) 
    { 
        if ($flatten) { 
            $items = $this->structure->getProperties(); 
        } else { 
            $items = $this->structure->getChildren(); 
        } 
 
        $propertyBridges = []; 
        foreach ($items as $property) { 
            $propertyBridges[$property->getName()] = $this->createLegacyPropertyFromItem($property); 
        } 
 
        return $propertyBridges; 
    } 
 
    public function getExt() 
    { 
        return $this->document->getExtensionsData(); 
    } 
 
    public function setExt($data) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function setHasChildren($hasChildren) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getHasChildren() 
    { 
        return $this->inspector->hasChildren($this->getDocument()); 
    } 
 
    public function setChildren($children) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getChildren() 
    { 
        $children = []; 
 
        foreach ($this->getDocument()->getChildren() as $child) { 
            $children[] = $this->documentToStructure($child); 
        } 
 
        return $children; 
    } 
 
    /** 
     * @return $this 
     */ 
    public function getParent() 
    { 
        return $this->documentToStructure($this->inspector->getParent($this->getDocument())); 
    } 
 
    public function getPublishedState() 
    { 
        return WorkflowStage::PUBLISHED === $this->getWorkflowDocument(__METHOD__)->getWorkflowStage(); 
    } 
 
    public function setPublished($published) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getPublished() 
    { 
        return $this->getWorkflowDocument(__METHOD__)->getPublished(); 
    } 
 
    public function getPropertyValue($name) 
    { 
        return $this->getProperty($name)->getValue(); 
    } 
 
    public function getPropertyNames() 
    { 
        return \array_keys($this->structure->getChildren()); 
    } 
 
    public function setType($type) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getType() 
    { 
        $document = $this->getDocument(); 
        $localizationState = $this->inspector->getLocalizationState($document); 
 
        if (LocalizationState::GHOST === $localizationState) { 
            return StructureType::getGhost($this->getDocument()->getLocale()); 
        } 
 
        if (LocalizationState::SHADOW === $this->inspector->getLocalizationState($document)) { 
            return StructureType::getShadow($this->getDocument()->getLocale()); 
        } 
    } 
 
    public function getPath() 
    { 
        return $this->inspector->getContentPath($this->getDocument()); 
    } 
 
    public function setPath($path) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function setHasTranslation($hasTranslation) 
    { 
        $this->readOnlyException(__METHOD__); 
    } 
 
    public function getHasTranslation() 
    { 
        return $this->getTitle() ? true : false; 
    } 
 
    public function toArray($complete = true) 
    { 
        $document = $this->getDocument(); 
 
        $result = [ 
            'id' => $this->inspector->getUuid($document), 
            'path' => $this->inspector->getContentPath($document), 
            'nodeType' => $this->getNodeType(), 
            'nodeState' => $this->getNodeState(), 
            'internal' => false, 
            'availableLocales' => $this->inspector->getLocales($document), 
            'contentLocales' => $this->inspector->getConcreteLocales($document), 
            'hasSub' => $this->getHasChildren(), 
            'title' => $document->getTitle(), // legacy system returns diffent fields for title depending on $complete 
        ]; 
 
        if ($document instanceof OrderBehavior) { 
            $result['order'] = $document->getSuluOrder(); 
        } 
 
        if ($document instanceof RedirectTypeBehavior) { 
            $redirectType = $document->getRedirectType(); 
            $result['linked'] = null; 
            if (RedirectType::INTERNAL == $redirectType && null !== $document->getRedirectTarget()) { 
                $result['linked'] = 'internal'; 
                $result['internal_link'] = $document->getRedirectTarget()->getUuid(); 
            } elseif (RedirectType::EXTERNAL == $redirectType) { 
                $result['linked'] = 'external'; 
                $result['external'] = $document->getRedirectExternal(); 
            } 
        } 
 
        if ($document instanceof WorkflowStageBehavior) { 
            $result['publishedState'] = WorkflowStage::PUBLISHED === $document->getWorkflowStage(); 
            $result['published'] = $document->getPublished(); 
        } 
 
        $result['navContexts'] = []; 
        if ($document instanceof NavigationContextBehavior) { 
            $result['navContexts'] = $document->getNavigationContexts(); 
        } 
 
        if (null !== $this->getType()) { 
            $result['type'] = $this->getType()->toArray(); 
        } 
 
        if ($complete) { 
            if ($document instanceof ShadowLocaleBehavior) { 
                $result = \array_merge( 
                    $result, 
                    [ 
                        'shadowLocales' => $this->inspector->getShadowLocales($document), 
                        'shadowOn' => $document->isShadowLocaleEnabled(), 
                        'shadowBaseLanguage' => $document->getShadowLocale(), 
                    ] 
                ); 
            } 
 
            $result = \array_merge( 
                $result, 
                [ 
                    'template' => $this->structure->getName(), 
                    'originTemplate' => $this->structure->getName(), 
                    'creator' => $document->getCreator(), 
                    'changer' => $document->getChanger(), 
                    'created' => $document->getCreated(), 
                    'changed' => $document->getChanged(), 
                    'title' => $document->getTitle(), 
                    'url' => null, 
                ] 
            ); 
 
            if ($document instanceof ResourceSegmentBehavior) { 
                $result['url'] = $document->getResourceSegment(); 
            } 
 
            if ($document instanceof ExtensionBehavior) { 
                $result['ext'] = $document->getExtensionsData(); 
            } 
 
            $result = \array_merge($this->getDocument()->getStructure()->toArray(), $result); 
 
            return $result; 
        } 
 
        return $result; 
    } 
 
    #[\ReturnTypeWillChange] 
    public function jsonSerialize() 
    { 
    } 
 
    public function getPropertyByTagName($tagName, $highest = true) 
    { 
        return $this->createLegacyPropertyFromItem($this->structure->getPropertyByTagName($tagName, $highest)); 
    } 
 
    public function getPropertiesByTagName($tagName) 
    { 
        $properties = []; 
        foreach ($this->structure->getPropertiesByTagName($tagName) as $structureProperty) { 
            $properties[] = $this->createLegacyPropertyFromItem($structureProperty); 
        } 
 
        return $properties; 
    } 
 
    public function getPropertyValueByTagName($tagName) 
    { 
        return $this->getPropertyByTagName($tagName)->getValue(); 
    } 
 
    public function hasTag($tag) 
    { 
        return $this->structure->hasPropertyWithTagName($tag); 
    } 
 
    public function getNodeType() 
    { 
        if ($this->getDocument() instanceof RedirectTypeBehavior) { 
            return $this->getDocument()->getRedirectType(); 
        } 
 
        return RedirectType::NONE; 
    } 
 
    public function getNodeName() 
    { 
        if ($this->document instanceof RedirectTypeBehavior 
            && RedirectType::INTERNAL == $this->document->getRedirectType() 
            && null !== $this->document->getRedirectTarget() 
        ) { 
            return $this->getDocument()->getRedirectTarget()->getTitle(); 
        } 
 
        return $this->getDocument()->getTitle(); 
    } 
 
    public function getLocalizedTitle($languageCode) 
    { 
        return $this->structure->getTitle($languageCode); 
    } 
 
    public function getNodeState() 
    { 
        $document = $this->getDocument(); 
 
        if (!$document instanceof WorkflowStageBehavior) { 
            return WorkflowStage::PUBLISHED; 
        } 
 
        return $this->getDocument()->getWorkflowStage(); 
    } 
 
    public function getTitle() 
    { 
        return $this->getDocument()->getTitle(); 
    } 
 
    public function getUrl() 
    { 
        return $this->getDocument()->getResourceSegment(); 
    } 
 
    public function copyFrom(StructureInterface $structure) 
    { 
        foreach ($this->getProperties(true) as $property) { 
            if ($structure->hasProperty($property->getName())) { 
                $property->setValue($structure->getPropertyValue($property->getName())); 
            } 
        } 
 
        $this->setDocument($structure->getDocument()); 
    } 
 
    /** 
     * Magic getter. 
     * 
     * @deprecated Do not use magic getters. Use ArrayAccess instead 
     */ 
    public function __get($name) 
    { 
        return $this->getProperty($name)->getValue(); 
    } 
 
    public function getShadowLocales() 
    { 
        return $this->inspector->getShadowLocales($this->getDocument()); 
    } 
 
    public function getContentLocales() 
    { 
        return $this->inspector->getConcreteLocales($this->getDocument()); 
    } 
 
    public function getIsShadow() 
    { 
        if (!$this->document) { 
            return false; 
        } 
 
        $document = $this->getDocument(); 
        if (!$document instanceof ShadowLocaleBehavior) { 
            return false; 
        } 
 
        return $document->isShadowLocaleEnabled(); 
    } 
 
    public function getShadowBaseLanguage() 
    { 
        $document = $this->getDocument(); 
        if (!$document instanceof ShadowLocaleBehavior) { 
            return; 
        } 
 
        return $document->getShadowLocale(); 
    } 
 
    public function getResourceLocator() 
    { 
        $document = $this->getDocument(); 
        if (RedirectType::EXTERNAL == $document->getRedirectType()) { 
            return $document->getRedirectExternal(); 
        } 
 
        if (RedirectType::INTERNAL === $document->getRedirectType()) { 
            $target = $document->getRedirectTarget(); 
 
            if (!$target) { 
                throw new \RuntimeException('Document is an internal redirect, but no redirect target has been set.'); 
            } 
 
            return $target->getResourceSegment(); 
        } 
 
        return $document->getResourceSegment(); 
    } 
 
    /** 
     * Returns document. 
     * 
     * @return object 
     */ 
    public function getDocument() 
    { 
        if (!$this->document) { 
            throw new \RuntimeException( 
                'Document has not been applied to structure yet, cannot retrieve data from structure.' 
            ); 
        } 
 
        return $this->document; 
    } 
 
    /** 
     * Returns structure metadata. 
     * 
     * @return StructureMetadata 
     */ 
    public function getStructure() 
    { 
        return $this->structure; 
    } 
 
    protected function readOnlyException($method) 
    { 
        throw new \BadMethodCallException( 
            \sprintf( 
                'Compatibility layer StructureBridge instances are readonly. Tried to call "%s"', 
                $method 
            ) 
        ); 
    } 
 
    /** 
     * @param StructureBehavior $document The document to convert 
     * 
     * @return $this 
     */ 
    protected function documentToStructure(StructureBehavior $document) 
    { 
        return new $this( 
            $this->inspector->getStructureMetadata($document), 
            $this->inspector, 
            $this->propertyFactory, 
            $document 
        ); 
    } 
 
    private function getWorkflowDocument($method) 
    { 
        $document = $this->getDocument(); 
        if (!$document instanceof WorkflowStageBehavior) { 
            throw new \BadMethodCallException( 
                \sprintf( 
                    'Cannot call "%s" on Document which does not implement PageInterface. Is "%s"', 
                    $method, 
                    \get_class($document) 
                ) 
            ); 
        } 
 
        return $document; 
    } 
 
    private function notImplemented($method) 
    { 
        throw new \InvalidArgumentException( 
            \sprintf( 
                'Method "%s" is not yet implemented', 
                $method 
            ) 
        ); 
    } 
 
    private function normalizeData(?array $data = null) 
    { 
        if (null === $data) { 
            return; 
        } 
 
        if (false === \is_array($data)) { 
            return $this->normalizeValue($data); 
        } 
 
        foreach ($data as &$value) { 
            if (\is_array($value)) { 
                foreach ($value as $childKey => $childValue) { 
                    $data[$childKey] = $this->normalizeData($childValue); 
                } 
            } 
 
            $value = $this->normalizeValue($value); 
        } 
 
        return $data; 
    } 
 
    private function normalizeValue($value) 
    { 
        if ($value instanceof StructureBehavior) { 
            return $this->documentToStructure($value); 
        } 
 
        return $value; 
    } 
 
    private function createLegacyPropertyFromItem($item) 
    { 
        $name = $item->getName(); 
        if (isset($this->loadedProperties[$name])) { 
            return $this->loadedProperties[$name]; 
        } 
 
        $propertyBridge = $this->propertyFactory->createProperty($item, $this); 
 
        if ($this->document) { 
            $property = $this->getDocument()->getStructure()->getProperty($name); 
            $propertyBridge->setPropertyValue($property); 
        } 
 
        $this->loadedProperties[$name] = $propertyBridge; 
 
        return $propertyBridge; 
    } 
}