/**
   * Adds a config class
   *
   * @param interfaze type (<code>interface</code>) of <code>config class</code>.
   * @param clazz the <code>object class</code> of <code>config class</code> need to be added.
   * @return returns the previous <code>object class</code> of <code>config class</code> appropriate
   *     to <b><i>interfaze</i></b> if it existed, if not, returns <b>null</b>.
   */
  @SuppressWarnings("unchecked")
  public static <T> T addConfigClass(Class<T> interfaze, Class<? extends T> clazz) {

    Assertor.notNull(interfaze);
    Assertor.notNull(clazz);
    if (interfaze.equals(clazz)) {
      throw new JGentleRuntimeException("invalid arguments !");
    }
    if (!interfaze.isInterface()) {
      throw new JGentleRuntimeException(interfaze.toString() + " must be a interface.");
    }
    return (T) JGentle.configObjClassList.put(interfaze, clazz);
  }
  /**
   * Adds {@link BeforeConfigure} instances
   *
   * @param classes the object classes of {@link BeforeConfigure} need to be added.
   */
  public static void addBeforeConfig(Class<? extends BeforeConfigure>... classes) {

    Assertor.notEmpty(classes, "Object class must not be null and must have at least one element.");
    for (Class<? extends BeforeConfigure> clazz : classes) {
      BeforeConfigure result = ReflectUtils.createInstance(clazz);
      addBeforeConfig(result);
    }
  }
  /**
   * Removes a given configurable class from registered list.
   *
   * @param interfaze the interface type of configurable class.
   * @return returns the removed configigurabke class if it existed, otherwise returns null.
   */
  @SuppressWarnings("unchecked")
  public static <T> T removeConfigClass(Class<T> interfaze) {

    Assertor.notNull(interfaze);
    if (!interfaze.isInterface()) {
      throw new JGentleRuntimeException(interfaze.toString() + " must be a interface.");
    }
    return (T) JGentle.configObjClassList.remove(interfaze);
  }
  /**
   * Removes a given {@link BeforeInitContext} instance.
   *
   * @param instance the {@link BeforeInitContext} instance need to be removed
   */
  public static void removeBeforeInitContext(BeforeInitContext instance) {

    Assertor.notNull(instance);
    if (JGentle.beforeInitContextList.contains(instance)) {
      JGentle.beforeInitContextList.remove(instance);
    } else {
      throw new JGentleRuntimeException("BeforeInitContext instance is not existed.");
    }
  }
  /**
   * Returns the configurable class bound to the given interface type.
   *
   * @param interfaze the given interface type
   * @return returns the object class if it existed, if not, one exception will be thrown.
   */
  @SuppressWarnings("unchecked")
  public static <T> Class<? extends T> getConfigClass(Class<T> interfaze) {

    Assertor.notNull(interfaze);
    if (!JGentle.containsConfigClass(interfaze)) {
      throw new JGentleRuntimeException("Does not found config class with type " + interfaze);
    }
    return (Class<? extends T>) JGentle.configObjClassList.get(interfaze);
  }
  /**
   * Adds a {@link BeforeConfigure} or a group of {@link BeforeConfigure} instances.
   *
   * @param beans the given instances
   */
  public static void addBeforeConfig(BeforeConfigure... beans) {

    Assertor.notEmpty(beans, "Object beans must not be null and must have at least one element.");
    for (BeforeConfigure result : beans) {
      if (!JGentle.getBeforeConfigBeanList().contains(result)) {
        JGentle.getBeforeConfigBeanList().add(result);
      } else {
        throw new JGentleRuntimeException("This BeforeConfigure Bean is existed.");
      }
    }
  }
  /**
   * Adds {@link BeforeInitContext} <code>instances</code>
   *
   * @param beforeInitContexts the before init contexts
   */
  public static void addBeforeInitContext(BeforeInitContext... beforeInitContexts) {

    Assertor.notEmpty(
        beforeInitContexts, "Object beans must not be null and must have at least one element.");
    for (BeforeInitContext obj : beforeInitContexts) {
      if (!JGentle.beforeInitContextList.contains(obj)) {
        JGentle.beforeInitContextList.add(obj);
      } else {
        throw new JGentleRuntimeException("This BeforeInitContext bean is existed.");
      }
    }
  }
  /*
   * (non-Javadoc)
   * @seeorg.jgentleframework.core.reflection.aohreflect.IAnnotationVisitor#
   * addAnnotationBeanProcessor
   * (org.jgentleframework.core.reflection.annohandler
   * .AnnotationBeanProcessor)
   */
  @Override
  @SuppressWarnings("unchecked")
  public <T extends Annotation> AnnotationBeanProcessor<?> addAnnotationBeanProcessor(
      AnnotationBeanProcessor<T> handler) throws ClassNotFoundException {

    Assertor.notNull(handler);
    Class<?> clazz = handler.getClass();
    List<Type> typeList = ReflectUtils.getAllGenericInterfaces(clazz, true);
    for (Type type : typeList) {
      if (ReflectUtils.isCast(ParameterizedType.class, type)) {
        ParameterizedType pType = (ParameterizedType) type;
        if (pType.getRawType().equals(AnnotationPostProcessor.class)
            || pType.getRawType().equals(AnnotationHandler.class)) {
          String annoStrClass = pType.getActualTypeArguments()[0].toString().split(" ")[1];
          Class<T> annoClass = (Class<T>) Class.forName(annoStrClass);
          if (annoClass == null)
            throw new InOutDependencyException("Does not found valid annotation class.");
          return this.visitorHandler.addAnnotationBeanProcessor(annoClass, handler);
        }
      }
    }
    return null;
  }
  /**
   * Builds the context.
   *
   * @param serviceProvider the service provider
   * @param argsType the args type
   * @param args the args
   * @param configClasses the config classes
   * @return the context
   */
  private static Context buildContext(
      boolean serviceProvider,
      Class<?>[] argsType,
      Object[] args,
      Class<? extends Configurable>... configClasses) {

    Assertor.notNull((Object[]) configClasses);
    if ((argsType == null && args != null) || (argsType != null && args == null)) {
      throw new JGentleRuntimeException("Property 'argsType' or 'args' is invalid !");
    }
    if (argsType != null && args != null) {
      if (argsType.length != args.length) {
        throw new JGentleRuntimeException("Property 'argsType' or 'args' is invalid !");
      }
      if (configClasses.length != 1) {
        throw new JGentleRuntimeException(
            "Lenght of array property 'configClasses' must be not greater than"
                + " one if property 'argsType' and 'args' are not null.");
      }
    }
    /*
     * Khởi tạo các config object
     */
    configObjClassList.put(BindingConfig.class, BindingConfigImpl.class);
    configObjClassList.put(SystemConfig.class, SystemConfigImpl.class);
    /*
     * Khởi tạo core services
     */
    AnnotationRegister annoRegister = new AnnotationRegisterImpl();
    DefinitionManager defManager = new DefinitionManagerImpl(annoRegister);
    ServiceHandler serviceHandler = new ServiceHandlerImpl(defManager);
    JGentle.registerAnnotations(serviceHandler);
    /*
     * Thực thi các tiền xử lý trước khi thực thi cấu hình (invoke configure
     * methods).
     */
    List<Class<? extends Configurable>> absCfgList = new ArrayList<Class<? extends Configurable>>();
    for (Class<? extends Configurable> clazz : configClasses) {
      absCfgList.add(clazz);
    }
    for (BeforeConfigure bcBean : JGentle.getBeforeConfigBeanList()) {
      bcBean.doProcessing(serviceHandler, defManager, annoRegister, absCfgList);
    }
    // Khởi tạo config instance
    List<Configurable> objectList = new ArrayList<Configurable>();
    for (Class<? extends Configurable> targetClass : absCfgList) {
      Configurable result = ConfigurationProxy.createProxy(targetClass, argsType, args);
      result.configure();
      // Tìm các imported configurable class nếu có
      List<ConfigurableImporter> importingList = result.getImportsCfgLst();
      List<Configurable> allResults = new ArrayList<Configurable>();
      if (importingList != null && importingList.size() != 0) {
        for (ConfigurableImporter ci : importingList) {
          allResults.add(
              ConfigurationProxy.createProxy(ci.getConfigurableClass(), ci.argsType(), ci.args()));
        }
      }
      allResults.add(result);
      for (Configurable objResult : allResults) {
        // Nếu context khởi tạo là một Services Context
        if (serviceProvider) {
          // Thực thi đăng kí annotation nếu trong config instance có
          // tồn tại một hoặc nhiều binding CSC
          List<Class<? extends ComponentServiceContextType<?>>> list;
          list = objResult.getCscClassList();
          for (Class<? extends ComponentServiceContextType<?>> cscClass : list) {
            Definition def = defManager.getDefinition(cscClass);
            ComponentServiceContext anno = null;
            if (def != null && def.isAnnotationPresent(ComponentServiceContext.class)) {
              anno = def.getAnnotation(ComponentServiceContext.class);
            }
            if (anno != null) {
              if (anno.beforeConfigure()) {
                // Thực thi đăng kí.
                JGentle.registerAnnotationInCSC(serviceHandler, anno, false, null);
              }
            }
          }
        }
        // invoke configure method
        if (objResult != result) objResult.configure();
        objectList.add(objResult);
      }
    }
    Context result = null;
    result =
        buildContext(
            serviceHandler,
            serviceProvider,
            objectList.toArray(new Configurable[objectList.size()]));
    /*
     * Clear toàn bộ config object
     */
    configObjClassList.clear();
    return result;
  }