import 'reflect-metadata';
import { Type } from './Util';
import { FrontworkComponent } from '../Types/FrontworkComponent';


/**
 * The Injector stores services and resolves requested instances.
 */
export const Injector = new class {

  injectionQueue :any[] = []

  rootInterface : Type<any>
  root : Type<any>
  rootModules : Type<any>[] = []

  moduleObjs : {[key in string] : FrontworkComponent} = {}

  /**
   * Resolves instances by injecting required services
   * @param {Type<any>} target
   * @returns {T}
   */
  resolve<T>(target: Type<any>): T {
    // tokens are required dependencies, while injections are resolved tokens from the Injector

    if(this.moduleObjs[target.name])
      return this.moduleObjs[target.name] as any

    if(target.name === this.rootInterface.name || target.name === this.root.name){
      let modules = this.rootModules.map(m => {
        const module = new m()
        this.moduleObjs[m.name] = module
        return module
      })
      const rootobj = new this.root(modules);

      this.moduleObjs[this.rootInterface.name] = rootobj
      this.moduleObjs[target.name] = rootobj
      while(this.injectionQueue.length > 0){
        const i = this.injectionQueue.shift()
        if(this.moduleObjs[i.what.name])
          i.target[i.where] = this.moduleObjs[i.what.name]
        else
          this.injectionQueue.push()
      }
      this.injectionQueue.forEach(i => {
        i.target[i.where] = this.moduleObjs[i.what.name]
      })
      return rootobj
    }
    this.moduleObjs[target.name] = new target()
    return this.moduleObjs[target.name] as any
    
    
  }
};
