/**
   * Discriminate special NF interfaces
   *
   * <ul>
   *   <li>COLLECTION: ignored, they are dynamically generated
   *   <li>CONTENT: if primitive, ignore it
   *   <li>BINDING: if primitive and DOESN'T HAVE F client interfaces, ignore it
   * </ul>
   *
   * @return true if 'special case', faslse otherwise
   */
  private boolean specialCasesForNfType(
      String itfName, PAGCMInterfaceType itfType, boolean isPrimitive) {

    // COLLECTION interfaces are ignored, because they are generated dynamically
    if (itfType.isFcCollectionItf()) {
      return true;
    }

    // CONTENT controller is not created for primitives
    if (Constants.CONTENT_CONTROLLER.equals(itfName)
        && !itfType.isFcClientItf()
        && !itfType.isInternal()
        && isPrimitive) {
      // logger.warn("Ignored NF Interface '"+ Constants.CONTENT_CONTROLLER +"' declared for
      // component '"+ this.componentParameters.getName() + "'");
      return true;
    }

    // BINDING controller is not created for primitives without client interfaces except if it has
    // an internal server interface
    if (Constants.BINDING_CONTROLLER.equals(itfName)
        && !itfType.isFcClientItf()
        && !itfType.isInternal()
        && isPrimitive) {
      if ((Utils.getClientItfTypes(this.componentParameters.getComponentType()).length == 0)
          && !hasNfInternalServerInterfaces()) {
        // logger.warn("Ignored NF Interface '"+ Constants.BINDING_CONTROLLER +"' declared for
        // component '"+ this.componentParameters.getName() + "'");
        return true;
      }
    }

    return false;
  }
  /**
   * Discriminate special controller interfaces
   *
   * <ul>
   *   <li>COLLECTION: ignored??? (TODO:check if this is needed)
   *   <li>CONTENT: if primitive, ignore it
   *   <li>BINDING: if primitive and DOESN'T HAVE F client interfaces, ignore it
   *   <li>Avoid duplicates
   * </ul>
   *
   * @param controllerName
   * @param itfType
   * @param isPrimitive
   * @return
   */
  private boolean specialCasesForController(
      String controllerName,
      PAGCMInterfaceType itfType,
      boolean isPrimitive,
      String controllersConfigFileLocation) {

    // COLLECTION interfaces are ignored, because they are generated dynamically (and an object
    // controller shouldn't be a collection, right?)
    // if(itfType.isFcCollectionItf()) {
    //	return true;
    // }

    // CONTENT controller is not created for primitives
    if (Constants.CONTENT_CONTROLLER.equals(controllerName)
        && !itfType.isFcClientItf()
        && !itfType.isInternal()
        && isPrimitive) {
      // logger.warn("Ignored controller '"+ Constants.CONTENT_CONTROLLER +"' declared for component
      // '"+ this.componentParameters.getName() + "' in file: "+ controllersConfigFileLocation);
      return true;
    }

    // BINDING controller is not created for primitives without client interfaces except if it has
    // an internal server interface
    if (Constants.BINDING_CONTROLLER.equals(controllerName)
        && !itfType.isFcClientItf()
        && !itfType.isInternal()
        && isPrimitive) {
      if ((Utils.getClientItfTypes(this.componentParameters.getComponentType()).length == 0)
          && !hasNfInternalServerInterfaces()) {
        // logger.warn("Ignored controller '"+ Constants.BINDING_CONTROLLER +"' declared for
        // component '"+ this.componentParameters.getName() + "' in file: "+
        // controllersConfigFileLocation);
        return true;
      }
    }

    // Controller interface had already been declared (f.e., in the NF Type). Do not create this
    // controller.
    if (existsNfInterface(controllerName)) {
      // logger.warn("Controller interface '"+ controllerName +"' already created. Ignoring this
      // controller.");
      return true;
    }

    return false;
  }
  /**
   * Instantiates an object controller and generates its representative interface using only the
   * Controller class (which must implement {@link AbstractPAController}.
   *
   * <p>The {@link PAGCMInterfaceType} is obtained from the object after instantiating it.
   *
   * @param controllerClass
   * @return interface generated for the controller
   */
  private PAInterface createControllerRepresentative(Class<?> controllerClass) throws Exception {

    // Instantiate the controller object, setting THIS component as its owner.
    Constructor<?> controllerClassConstructor =
        controllerClass.getConstructor(new Class[] {Component.class});
    AbstractPAController controller =
        (AbstractPAController) controllerClassConstructor.newInstance(new Object[] {this});
    // Obtains the interface type after having instantiated the object
    PAGCMInterfaceType controllerItfType = (PAGCMInterfaceType) controller.getFcItfType();
    String controllerName = controller.getFcItfName();
    // Generates the representative PAInterface
    PAInterface itfRef =
        RepresentativeInterfaceClassGenerator.instance()
            .generateInterface(
                controllerName, this, controllerItfType, controllerItfType.isInternal(), false);
    // itfRef.setFcItfImpl(controller);

    // TODO: This was not done before. Is it needed now? Following the scheme of "addControllers" I
    // would say yes
    ((StubObject) itfRef).setProxy(this.proxy);
    return itfRef;
  }
  /**
   * Create and add the NF Interfaces using both a component configuration file, and an NF Type.
   *
   * <ol>
   *   <li>Creates NF Interfaces from the declared NF Type. If there is non NF Type, this step is
   *       ignored.
   *   <li>Creates Object Controllers from the Controllers Configuration File. If some of them
   *       duplicates interfaces declared in the NF Type, they are ignored. In other words, the
   *       declared NF Type has priority over the Controllers Configuration File.
   *   <li>Checks that the mandatory interfaces are declared. In particular, if the membrane
   *       controller has been previously declared, it is created here.
   *   <li>Updates the NF Type of the Component.
   * </ol>
   *
   * NOTE: When an NF interface is described in the ADL file, the Factory adds the
   * 'membrane-controller' default implementation automatically, so that it is not necessary to
   * specify the 'membrane-controller' explicitly.
   */
  private void addControllerInterfaces() {

    // Vector to collect the real NF type
    Vector<InterfaceType> nfType = new Vector<InterfaceType>();

    boolean isPrimitive =
        Constants.PRIMITIVE.equals(this.componentParameters.getHierarchicalType());

    // ------------------------------------------------------------
    // 1. Create interfaces from the declared NF type
    // Read the NF Type
    PAComponentType componentType = (PAComponentType) this.componentParameters.getComponentType();
    InterfaceType[] nfItfTypes = componentType.getNfFcInterfaceTypes();
    PAGCMInterfaceType[] pagcmNfItfTypes = new PAGCMInterfaceType[nfItfTypes.length];
    System.arraycopy(nfItfTypes, 0, pagcmNfItfTypes, 0, nfItfTypes.length);
    // Class<?> controllerItf = null;

    for (PAGCMInterfaceType pagcmNfItfType : pagcmNfItfTypes) {

      String itfName = pagcmNfItfType.getFcItfName();
      PAInterface itfRef = null;

      try {
        // some controllers interfaces are ignored
        if (specialCasesForNfType(itfName, pagcmNfItfType, isPrimitive)) continue;

        // TODO: check the case MULTICAST && CLIENT, treated in
        // PAComponentImpl.addControllerInterfaces, but not here

        // Generate the representative interface
        itfRef =
            RepresentativeInterfaceClassGenerator.instance()
                .generateInterface(
                    pagcmNfItfType.getFcItfName(),
                    this,
                    pagcmNfItfType,
                    pagcmNfItfType.isInternal(),
                    false);

        // update the hashmap and the vector of NF types
        this.nfItfs.put(itfName, itfRef);
        nfType.add((InterfaceType) itfRef.getFcItfType());

      } catch (Exception e) {
        throw new ProActiveRuntimeException(
            "Could not create NF interface reference'"
                + itfName
                + "' while instantiating component'"
                + this.componentParameters.getName()
                + "'. "
                + e.getMessage(),
            e);
      }
    }

    // ------------------------------------------------------------
    // 2. Create interfaces from the Controller Configuration file

    // read the Controller Configuration File
    Map<String, String> controllerEntries = null;
    if (this.componentParameters.getControllerDescription().configFileIsSpecified()) {
      // Parse controller config file
      String controllersConfigFileLocation =
          this.componentParameters.getControllerDescription().getControllersConfigFileLocation();
      loggerADL.debug("Parsing Controller Configuration File: " + controllersConfigFileLocation);
      ComponentConfigurationHandler componentConfiguration =
          PAComponentImpl.loadControllerConfiguration(controllersConfigFileLocation);
      controllerEntries = componentConfiguration.getControllers();

      // Create controller objects from the Controller Configuration File
      for (Map.Entry<String, String> controllerEntry : controllerEntries.entrySet()) {

        String controllerName = null;
        String controllerItfName = controllerEntry.getKey();
        String controllerClassName = controllerEntry.getValue();
        Class<?> controllerItf = null;
        Class<?> controllerClass = null;
        AbstractPAController controller = null;
        PAInterface itfRef = null;
        PAGCMInterfaceType controllerItfType = null;

        try {
          // fetch the classes
          controllerItf = Class.forName(controllerItfName);
          controllerClass = Class.forName(controllerClassName);
          // Instantiates the controller object, using 'this' component as owner.
          Constructor<?> controllerClassConstructor =
              controllerClass.getConstructor(new Class[] {Component.class});
          controller =
              (AbstractPAController) controllerClassConstructor.newInstance(new Object[] {this});

          // Obtains the controller interfaceType as declared by the object (in the method
          // setControllerItfType)
          controllerItfType = (PAGCMInterfaceType) controller.getFcItfType();
          // now we can know the name of the controller, and discriminate special cases
          controllerName = controllerItfType.getFcItfName();

          // Some controllers are not created
          if (specialCasesForController(
              controllerName, controllerItfType, isPrimitive, controllersConfigFileLocation)) {
            continue;
          }

          if (!controllerItf.isAssignableFrom(controllerClass)) {
            logger.error(
                "Could not create controller. Class '"
                    + controllerClassName
                    + " does not implement interface '"
                    + controllerItfName
                    + ". Check controller configuration file.");
            continue;
          }

          // Generate the representative interface
          itfRef =
              RepresentativeInterfaceClassGenerator.instance()
                  .generateControllerInterface(controllerName, this, controllerItfType);
          ((StubObject) itfRef).setProxy(this.proxy);

        } catch (Exception e) {
          throw new ProActiveRuntimeException(
              "Could not create representative interface for controller '"
                  + controllerClassName
                  + "' while instantiating component'"
                  + this.componentParameters.getName()
                  + "'. Check your configuration file "
                  + this.componentParameters
                      .getControllerDescription()
                      .getControllersConfigFileLocation()
                  + " : "
                  + e.getMessage(),
              e);
        }

        // add the controller to the controllers interfaces map, and add the controller type to the
        // NF type
        nfItfs.put(controllerName, itfRef);
        nfType.add((InterfaceType) itfRef.getFcItfType());
      }
    }

    // ------------------------------------------------------------
    // 3. Check that the mandatory controllers have been created
    checkMandatoryControllers(nfType);

    // ------------------------------------------------------------
    // 4. Set the real NF type, after having created all the NF interfaces
    try {
      Component boot = Utils.getBootstrapComponent();
      PAGCMTypeFactory tf = Utils.getPAGCMTypeFactory(boot);
      InterfaceType[] f = this.componentParameters.getComponentType().getFcInterfaceTypes();
      InterfaceType[] nf = nfType.toArray(new InterfaceType[] {});
      // Re-Set the real ComponentType
      this.componentParameters.setComponentType(tf.createFcType(f, nf));
    } catch (Exception e) {
      logger.error("NF type could not be set");
      e.printStackTrace();
    }
  }