/**
 * FXML Controller class
 *
 * @author Mostafa Ali <*****@*****.**>
 */
public class InteractionsListController extends VBox {

  // Logger
  private static final Logger logger = LogManager.getLogger();

  @FXML private TableView<InteractionState> InteractionTableView;
  @FXML private TableColumn<InteractionState, String> InteractionTableColumn;
  @FXML private TableColumn CheckTableColumn;

  CheckBox cb = new CheckBox();
  ObservableList<InteractionState> interactions = FXCollections.observableArrayList();

  public InteractionsListController() {
    logger.entry();
    FXMLLoader fxmlLoader =
        new FXMLLoader(getClass().getResource("/fxml/customcontrol/InteractionsList.fxml"));
    fxmlLoader.setController(this);
    fxmlLoader.setRoot(this);
    try {
      fxmlLoader.load();

    } catch (IOException ex) {
      logger.log(Level.FATAL, ex.getMessage(), ex);
    }
    logger.exit();
  }

  public void setFddObjectModel(FddObjectModel fddObjectModel) {
    logger.entry();
    if (fddObjectModel != null) {
      fddObjectModel
          .getInteractionClasses()
          .values()
          .stream()
          .forEach(
              (value) -> {
                interactions.add(new InteractionState(value));
              });
      InteractionTableView.setItems(interactions);
      interactions.forEach(
          (interaction) -> {
            interaction
                .onProperty()
                .addListener(
                    (observable, oldValue, newValue) -> {
                      if (!newValue) {
                        cb.setSelected(false);
                      } else if (interactions.stream().allMatch(a -> a.isOn())) {
                        cb.setSelected(true);
                      }
                    });
          });
      InteractionTableColumn.setCellValueFactory(new PropertyValueFactory<>("interactionName"));
      CheckTableColumn.setCellValueFactory(
          new Callback<
              TableColumn.CellDataFeatures<InteractionState, Boolean>, ObservableValue<Boolean>>() {
            @Override
            public ObservableValue<Boolean> call(
                TableColumn.CellDataFeatures<InteractionState, Boolean> param) {
              return param.getValue().onProperty();
            }
          });

      CheckTableColumn.setCellFactory(CheckBoxTableCell.forTableColumn(CheckTableColumn));
      cb.setUserData(CheckTableColumn);
      cb.setOnAction(
          (ActionEvent event) -> {
            CheckBox cb1 = (CheckBox) event.getSource();
            TableColumn tc = (TableColumn) cb1.getUserData();
            InteractionTableView.getItems()
                .stream()
                .forEach(
                    (item) -> {
                      item.setOn(cb1.isSelected());
                    });
          });
      CheckTableColumn.setGraphic(cb);
    }
    logger.exit();
  }

  public List<InteractionClassFDD> getInteractions() {
    return interactions
        .stream()
        .filter(a -> a.isOn())
        .map(a -> a.interaction)
        .collect(Collectors.toList());
  }

  public static class InteractionState {

    private final ReadOnlyStringWrapper interactionName = new ReadOnlyStringWrapper();
    private final BooleanProperty on = new SimpleBooleanProperty();
    private final InteractionClassFDD interaction;

    public InteractionState(InteractionClassFDD interaction) {
      this.interaction = interaction;
      interactionName.set(interaction.getFullName());
    }

    public String getInteractionName() {
      return interactionName.get();
    }

    public ReadOnlyStringProperty interactionNameProperty() {
      return interactionName.getReadOnlyProperty();
    }

    public boolean isOn() {
      return on.get();
    }

    public void setOn(boolean value) {
      on.set(value);
    }

    public BooleanProperty onProperty() {
      return on;
    }

    @Override
    public String toString() {
      return interaction.getFullName();
    }
  }
}
/**
 * FXML Controller class
 *
 * @author Mostafa
 */
public class LocalDeleteObjectInstanceServiceController implements Initializable {

  // Logger
  private static final Logger logger = LogManager.getLogger();

  @FXML private TextField ObjectInstanceDesignator;
  @FXML private Button OkButton;

  /** Initializes the controller class. */
  @Override
  public void initialize(URL url, ResourceBundle rb) {
    logger.entry();
    OkButton.disableProperty().bind(ObjectInstanceDesignator.textProperty().isEmpty());
    logger.exit();
  }

  @FXML
  private void Cancel_click(ActionEvent event) {
    logger.entry();
    ((Stage) OkButton.getScene().getWindow()).close();
    logger.exit();
  }

  @FXML
  private void Ok_click(ActionEvent event) {
    logger.entry();
    LogEntry log = new LogEntry("6.16", "Local Delete Object Instance service");
    try {
      ObjectInstanceHandle instanceHandle =
          rtiAmb
              .getObjectInstanceHandleFactory()
              .decode(
                  ByteBuffer.allocate(4)
                      .putInt(Integer.parseInt(ObjectInstanceDesignator.getText()))
                      .array(),
                  0);
      log.getSuppliedArguments()
          .add(
              new ClassValuePair(
                  "Object instance designator",
                  ObjectInstanceHandle.class,
                  instanceHandle.toString()));
      rtiAmb.localDeleteObjectInstance(instanceHandle);
      log.setDescription("Local Object instance deleted successfully");
      log.setLogType(LogEntryType.REQUEST);
    } catch (FederateNotExecutionMember
        | NotConnected
        | NumberFormatException
        | CouldNotDecode
        | RTIinternalError
        | OwnershipAcquisitionPending
        | FederateOwnsAttributes
        | ObjectInstanceNotKnown
        | SaveInProgress
        | RestoreInProgress ex) {
      log.setException(ex);
      log.setLogType(LogEntryType.ERROR);
      logger.log(Level.ERROR, ex.getMessage(), ex);
    } catch (Exception ex) {
      log.setException(ex);
      log.setLogType(LogEntryType.FATAL);
      logger.log(Level.FATAL, ex.getMessage(), ex);
    }
    logEntries.add(log);
    ((Stage) OkButton.getScene().getWindow()).close();
    logger.exit();
  }
}
/**
 * FXML Controller class
 *
 * @author Mostafa Ali <*****@*****.**>
 */
public class JoinFederationExecutionServiceController implements Initializable {

  // Logger
  private static final Logger logger = LogManager.getLogger();

  @FXML private TextField FederateName;
  @FXML private TextField FederateType;
  @FXML private TextField FederationExecutionName;
  @FXML private FilesList FomModuleDesignators;
  @FXML private Button OkButton;

  /** Initializes the controller class. */
  @Override
  public void initialize(URL url, ResourceBundle rb) {
    logger.entry();
    OkButton.disableProperty()
        .bind(
            Bindings.isEmpty(FederationExecutionName.textProperty())
                .or(Bindings.isEmpty(FederateType.textProperty())));
    logger.exit();
  }

  @FXML
  private void Cancel_click(ActionEvent event) {
    logger.entry();
    ((Stage) FederationExecutionName.getScene().getWindow()).close();
    logger.exit();
  }

  @FXML
  private void OK_click(ActionEvent event) {
    logger.entry();
    FederateHandle federateHandle;
    LogEntry log = new LogEntry("4.9", "Join Federation Execution service");
    try {
      if (!FederateName.getText().isEmpty()) {
        log.getSuppliedArguments()
            .add(new ClassValuePair("Federate Name", String.class, FederateName.getText()));
      }
      log.getSuppliedArguments()
          .add(new ClassValuePair("Federate Type", String.class, FederateType.getText()));
      log.getSuppliedArguments()
          .add(
              new ClassValuePair(
                  "Federation Execution Name", String.class, FederationExecutionName.getText()));
      List<URL> foms = new ArrayList<>();
      int i = 1;
      for (File file : FomModuleDesignators.getFiles()) {
        foms.add(file.toURI().toURL());
        log.getSuppliedArguments()
            .add(
                new ClassValuePair(
                    "FOM Module Deisgnator " + i++, URL.class, file.toURI().toURL().toString()));
      }
      if (FederateName.getText().isEmpty() && FomModuleDesignators.getFileNames().isEmpty()) {
        federateHandle =
            rtiAmb.joinFederationExecution(
                FederateType.getText(), FederationExecutionName.getText());
      } else if (FomModuleDesignators.getFileNames().isEmpty()) {
        federateHandle =
            rtiAmb.joinFederationExecution(
                FederateName.getText(), FederateType.getText(), FederationExecutionName.getText());
      } else if (FederateName.getText().isEmpty()) {
        federateHandle =
            rtiAmb.joinFederationExecution(
                FederateType.getText(),
                FederationExecutionName.getText(),
                foms.toArray(new URL[foms.size()]));
      } else {
        federateHandle =
            rtiAmb.joinFederationExecution(
                FederateName.getText(),
                FederateType.getText(),
                FederationExecutionName.getText(),
                foms.toArray(new URL[foms.size()]));
      }
      log.getReturnedArguments()
          .add(
              new ClassValuePair(
                  "Federate Handle", FederateHandle.class, federateHandle.toString()));
      log.setDescription("Federate joined federation execution successfully");
      log.setLogType(LogEntryType.REQUEST);
      logicalTimeFactory = rtiAmb.getTimeFactory();
      currentLogicalTime = logicalTimeFactory.makeInitial();

      // subscribe to HLAcurrentFDD to retrieve FDD
      ObjectClassHandle FederationHandle =
          rtiAmb.getObjectClassHandle("HLAobjectRoot.HLAmanager.HLAfederation");
      currentFDDHandle = rtiAmb.getAttributeHandle(FederationHandle, "HLAcurrentFDD");
      AttributeHandleSet set = rtiAmb.getAttributeHandleSetFactory().create();
      set.add(currentFDDHandle);
      rtiAmb.subscribeObjectClassAttributes(FederationHandle, set);
      rtiAmb.requestAttributeValueUpdate(FederationHandle, set, null);
      // In case of HLA_EVOKED we require this line to receive the FDD
      rtiAmb.evokeMultipleCallbacks(
          .05,
          1); // evoke one callback will not be enough because the reflect attribute is the second
              // one
    } catch (CouldNotCreateLogicalTimeFactory
        | CallNotAllowedFromWithinCallback
        | CouldNotOpenFDD
        | ErrorReadingFDD
        | InconsistentFDD
        | FederateNameAlreadyInUse
        | FederateAlreadyExecutionMember
        | FederationExecutionDoesNotExist
        | SaveInProgress
        | RestoreInProgress
        | NotConnected
        | RTIinternalError ex) {
      log.setException(ex);
      log.setLogType(LogEntryType.ERROR);
      logger.log(Level.ERROR, ex.getMessage(), ex);
    } catch (Exception ex) {
      log.setException(ex);
      log.setLogType(LogEntryType.FATAL);
      logger.log(Level.FATAL, ex.getMessage(), ex);
    }
    logEntries.add(log);
    ((Stage) FederationExecutionName.getScene().getWindow()).close();
    logger.exit();
  }
}