module glued.application.di.providers;

import std.traits;
import std.functional;

import glued.annotations;
import glued.logging;

import glued.application.di.resolveCall;

import glued.application.di.annotations;
import glued.application.di.initializers;

import dejector;

extern (C) Object _d_newclass(const TypeInfo_Class ci);

//todo is(class)
class ComponentClassProvider(T): Provider {
    mixin CreateLogger;
    private Logger log;

    this(LogSink logSink){
        log = Logger(logSink);
    }

    override Initialization get(Dejector injector){
        log.debug_.emit("Building seed of type ", fullyQualifiedName!T);
        auto seed = cast(T) _d_newclass(T.classinfo);
        log.debug_.emit("Built seed ", &seed, " of type ", fullyQualifiedName!T);
        return new Initialization(seed, false, new ComponentSeedInitializer!T(injector));
    }
}

class ConfigurationMethodProvider(C, string name, size_t i): Provider {
    mixin CreateLogger;
    private Logger log;

    this(LogSink logSink){
        log = Logger(logSink);
    }

    override Initialization get(Dejector injector){
        log.debug_.emit("Resolving configuration class instance ", fullyQualifiedName!C);
        C config = injector.get!C;
        log.debug_.emit("Resolved configuration class ", fullyQualifiedName!C, " instance ", &config);
        log.debug_.emit("Building configuration method ", fullyQualifiedName!C, ".", name);
        auto instance = resolveCall!(__traits(getOverloads, C, name)[i])(injector, &__traits(getOverloads, config, name)[i]);
        enum isSeed = hasOneAnnotation!(__traits(getOverloads, C, name)[i], Seed); //todo assert not has many
        log.debug_.emit("Built initialized instance ", &instance, " method ", fullyQualifiedName!C, ".", name);
        auto initializer = isSeed ? new InstanceInitializer!(C, true)(injector) : new NullInitializer;
        return new Initialization(cast(Object) instance, !isSeed, cast(Initializer) initializer);
    }
}