<?php
/**
 * Copyright 2020 Adobe
 * All Rights Reserved.
 *
 * NOTICE: Adobe permits you to use, modify, and distribute this file in
 * accordance with the terms of the Adobe license agreement accompanying
 * it.
 */
declare(strict_types=1);

namespace Magento\Mray\Index;

use Magento\Mray\CodeStructuralElement\Php\Reflection\ClassConstantDeclaration;
use Magento\Mray\Index\Collection\Dictionary;
use Magento\Mray\Index\Data\PhpClassLikeMemberMeta;
use Magento\Mray\Index\Data\PhpTypeMeta;
use phpDocumentor\Reflection\Type;
use function array_merge;

class PhpClassConstantMeta extends PhpClassLikeMemberMeta implements ClassConstantDeclaration
{
    /** @var PhpTypeMeta  */
    protected $type;

    /**
     * @param ClassConstantDeclaration $d
     * @return static
     */
    public static function createFromObject(ClassConstantDeclaration $d): self
    {
        return new static(array_merge(
            self::membershipDataFromObject($d),
            [
                'type' => PhpTypeMeta::createFromObject($d->getType()),
            ]
        ));
    }

    /**
     * @return int
     */
    public static function getKind(): int
    {
        return PhpElementKinds::DECLARED_ | PhpElementKinds::MEMBER_ | PhpElementKinds::CONST_;
    }

    /**
     * @param array $packed
     * @param Dictionary $dict
     * @return array
     */
    protected static function unpackData(array $packed, Dictionary $dict): array
    {
        $type = null;
        if (isset($packed[0])) {
            $packed[0] = self::unstringify($dict->read($packed[0]));
            $type = isset($packed[0][10]) ? $dict->read($packed[0][10]) : null;
            if ($type) {
                $type = PhpTypeMeta::unpack($type, $dict);
            }
        }
        $data = parent::unpackData($packed, $dict);
        $data['type'] = $type ?? null;
        return $data;
    }

    /**
     * @param Dictionary $dict
     * @return array
     */
    protected function packData(Dictionary $dict): array
    {
        $data = parent::packData($dict);
        $data[0][10] = $dict->write($this->type->pack($dict));
        $data[0] = $dict->write(self::stringify($data[0]));
        return $data;
    }

    /**
     * @param array $data
     */
    public function __construct($data)
    {
        parent::__construct($data);
        $this->type = $data['type'] ?? null;
    }

    /**
     * @return Type|null
     */
    public function getType(): ?Type
    {
        if ($this->type) {
            return $this->type->cast();
        }
        return null;
    }

    /**
     * @return Type|null
     */
    public function getValueType(): ?Type
    {
        return $this->getType();
    }
}
