<?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\Scanner\Tuner;

use Magento\Mray\CodeStructuralElement\Php\NodeVisitor\DeclaresAttributeSetter;
use Magento\Mray\CodeStructuralElement\Php\Reflection\Context\StructuralElementDeclarations;
use Magento\Mray\Package\AbstractTree\Node\PhpCode;
use Magento\Mray\Index\Scanner\Tuner;
use Magento\Mray\Package\AbstractTree\ScannerSubject;
use PhpParser\NodeTraverser;
use function get_object_vars;

class PhpCodeDeclaresAttributeSetter implements Tuner
{
    /** @var StructuralElementDeclarations|null  */
    private $elementsRegistry;

    /**
     * @param StructuralElementDeclarations|null $elementsRegistry
     */
    public function __construct(?StructuralElementDeclarations $elementsRegistry = null)
    {
        $this->elementsRegistry = $elementsRegistry;
    }

    /**
     * @inheritDoc
     */
    public function tune(ScannerSubject $subject, array $packages): array
    {
        foreach ($packages as $package) {
            foreach (get_object_vars($package) as $node) {
                if ($node instanceof PhpCode) {
                    $this->findDeclarations($node);
                }
            }
        }
        return $packages;
    }

    /**
     * @param PhpCode $phpCode
     */
    private function findDeclarations(PhpCode $phpCode)
    {
        if ($phpCode->getDeclarations()) {
            return;
        }

        $visitor = new DeclaresAttributeSetter($this->elementsRegistry);
        $traverser = new NodeTraverser();
        $traverser->addVisitor($visitor);
        foreach ($phpCode->files as $file) {
            if ($file->syntaxErrs) {
                // todo: use partially valid files
                continue;
            }

            $traverser->traverse($file->ast);
            $file->setDeclarations($visitor->fileScopeDeclarations);
        }

        $phpCode->setDeclarations($visitor->allDeclarations);
    }
}
