
package de.ivu.elect.jee.presentation.action;

import static java.util.stream.Collectors.toList;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;

import org.primefaces.event.SelectEvent;
import org.primefaces.model.menu.DefaultMenuModel;
import org.primefaces.model.menu.MenuElement;
import org.primefaces.model.menu.MenuItem;
import org.primefaces.model.menu.Submenu;

import de.ivu.elect.jee.entity.Entity;
import de.ivu.elect.jee.exception.ElectCommonExceptionHandler;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.jee.protokollierung.ProtokollAktiv;

@RequestScoped
public abstract class AbstractActionMenuModelBuilder<E extends Entity< ? , ? >> extends AbstractHackedPartialStateSavingActionMenuModelBuilder<E> implements ActionEntry<E> {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient ProtokollActionFactory protokollActionFactory;

  @Inject
  private transient Messages messages;

  public AbstractActionMenuModelBuilder() {
    super();
  }

  public List<ActionEntry<E>> getActionEntriesInklusiveProtokollActionSortedEnabled(E entity) {
    return collectActionEntriesEnabled(getActionEntriesInklusiveProtokollActionSorted(), entity);
  }

  /**
   * @return Alle fachlichen Aktionen INKLUSIVE ggf. automatisch hinzugefügter Protokoll-Aktion...
   */
  @Override
  @SuppressWarnings({"rawtypes", "unchecked"})
  public List<ActionEntry<E>> getActionEntriesInklusiveProtokollActionSorted() {
    List<ActionEntry<E>> result = getActionEntriesExklusiveProtokollActionSorted();

    if (ProtokollAktiv.isProtokollierungAktiv() && isProtokollAnzeigen()) {
      AbstractBaseActionEntry<Entity< ? , ? >> protokollAction = protokollActionFactory.getProtokollAction();

      if (protokollAction != null) {
        List temp = result; // Ohne Generics, um die Protokoll Action reinmogeln zu können
        temp.add(protokollAction);
      }
    }

    return result;
  }

  public List<ActionEntry<E>> getActionEntriesExklusiveProtokollActionSortedEnabled(E entity) {
    return collectActionEntriesEnabled(getActionEntriesExklusiveProtokollActionSorted(), entity);
  }

  /**
   * @return Alle fachlichen Aktionen EXKLUSIVE ggf. automatisch hinzugefügter Protokoll-Aktion...
   *
   * @see AbstractActionMenuModelBuilder#getActionEntriesSorted
   */
  public List<ActionEntry<E>> getActionEntriesExklusiveProtokollActionSorted() {
    return getActionEntriesSorted();
  }

  /**
   * @return Alle fachlichen Aktionen...
   */
  protected abstract List<ActionEntry<E>> getActionEntriesSorted();

  private List<ActionEntry<E>> collectActionEntriesEnabled(List<ActionEntry<E>> actionEntries, E entity) {
    return actionEntries.stream() //
        .filter(actionEntryEnabled(entity)) //
        .collect(toList());
  }

  @Override
  public DefaultMenuModel getMenuModel(E entity) {
    DefaultMenuModel menuModel = new DefaultMenuModel();

    ArrayList<ActionEntry<E>> actionEntriesSorted = new ArrayList<>(getActionEntriesInklusiveProtokollActionSorted());

    for (ActionEntry<E> actionEntry : actionEntriesSorted) {
      MenuElement menuComponent = actionEntry.buildComponent(entity);

      if (menuComponent instanceof Submenu) {
        menuModel.getElements().add(menuComponent);
      } else if (menuComponent instanceof MenuItem) {
        menuModel.getElements().add(menuComponent);
      } else {
        throw new IllegalStateException("Unbekannte Klasse" + menuComponent + ", " + menuComponent.getClass());
      }
    }

    return menuModel;
  }

  @Override
  public boolean hasPrincipalRequiredPermission() {
    return getActionEntriesExklusiveProtokollActionSorted().stream().allMatch(ActionEntry::hasPrincipalRequiredPermission);
  }

  @Override
  public String getLabelKey() {
    throw new UnsupportedOperationException();
  }

  @Override
  public MenuElement buildComponent(E entity) {
    throw new UnsupportedOperationException();
  }

  @Override
  public String getIcon() {
    throw new UnsupportedOperationException();
  }

  @Override
  public String action(E entity) {
    throw new UnsupportedOperationException();
  }

  public boolean isRendered() {
    return true;
  }

  public boolean isDisabled() {
    return false;
  }

  @Override
  public boolean isDisabled(E entity) {
    return getActionEntriesInklusiveProtokollActionSorted().stream().noneMatch(actionEntryEnabled(entity));
  }

  /**
   * Menüeintrag "Protokoll anzeigen" immer anhängen?
   */
  public boolean isProtokollAnzeigen() {
    return true;
  }

  // ELECT-1211
  public void onRowDblselect(SelectEvent event) {
    try {
      E entity = (E) event.getObject();

      for (ActionEntry<E> actionEntry : getActionEntriesInklusiveProtokollActionSorted()) {
        if (actionEntry.isActionPossibleOnRowDblselect(entity)) {
          actionEntry.executeAction(entity);

          return;
        }
      }
    } catch (Exception var3) {
      ElectCommonExceptionHandler.handleException(var3, messages);
    }
  }

  @Override
  public boolean isActionPossible(E entity) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean isActionPossibleOnRowDblselect(E entity) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void executeAction(Object o) {
    throw new UnsupportedOperationException();
  }

  private Predicate< ? super ActionEntry<E>> actionEntryEnabled(E entity) {
    return actionEntry -> !actionEntry.isDisabled(entity) && actionEntry.hasPrincipalRequiredPermission();
  }
}
