<?php

namespace Snaju\Inception\Injection;

use Doctrine\Common\Annotations\AnnotationReader;
use MyProject\Proxies\__CG__\stdClass;
use Snaju\Inception\Annotation\InceptionRoute;
use Snaju\Inception\Annotation\Inject;
use Snaju\Inception\Annotation\Singleton;
use Snaju\Inception\Injection\Exception\InjectionMemberNotFoundException;
use Snaju\Inception\Util\AnnotationHelper;

class InjectionManager
{

    public static $objs = [];

    public static function &getInstance(string $className)
    {
        if (!isset(self::$objs[$className])) {
            return self::loadInstance($className);
        }

        return self::$objs[$className];
    }

    public static function sync(&$obj)
    {
        self::$objs[get_class($obj)] = $obj;
    }

    public static function &loadInstance(string $className)
    {
        if (class_exists($className)) {
            $obj = new $className();
            self::$objs[$className] = $obj;
            return $obj;
        }

        return new stdClass();
    }

    public static function init()
    {
        // Load any singletons into the memory
        $classes = AnnotationHelper::findClassesWithAnnotation(Singleton::class);
        foreach ($classes as $class) {
            if ($class instanceof \ReflectionClass) {
                $annon = AnnotationHelper::getClassAnnotation($class->getName(), Singleton::class);
                if ($annon instanceof Singleton) {
                    self::loadInstance($class->getName());
                }
            }
        }

        // Inject into the @Singleton members auto
        foreach (self::$objs as &$obj) {
            self::inject($obj);
        }

    }

    private function getTypeNameFromAnnotation(string $className, string $propertyName): ?string
    {
        $rp = new \ReflectionProperty($className, $propertyName);
        if (preg_match('/@var\s+([^\s]+)/', $rp->getDocComment(), $matches)) {
            return $matches[1];
        }

        return null;
    }

    public static function inject(&$object)
    {
        $annon = new AnnotationReader();
        $class = new \ReflectionClass(get_class($object));
        if ($class instanceof \ReflectionClass) {
            foreach ($class->getProperties() as $property) {
                if ($property instanceof \ReflectionProperty) {
                    $an = $annon->getPropertyAnnotation($property, Inject::class);
                    if ($an instanceof Inject) {
                        $typeName = null;
                        if ($property->hasType()) {
                            $t = $property->getType();
                            if ($t instanceof \ReflectionType) {
                                $typeName = $t->getName();
                            }
                        } else {
                            if (preg_match('/@var\s+([^\s]+)/', $property->getDocComment(), $matches)) {
                                $typeName = $matches[1];
                            }
                        }

                        if ($typeName != null) {
                            $property->setValue($object, self::getInstance($typeName));
                        } else {
                            throw new \Exception("Invalid Type defined in the Inject Member");
                        }
                    }
                }
            }
        }
    }

}

?>