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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Nullable;

import org.primefaces.model.menu.DefaultMenuItem;
import org.primefaces.model.menu.DefaultMenuModel;
import org.primefaces.model.menu.MenuElement;

import de.ivu.elect.jee.entity.Entity;
import de.ivu.elect.jee.faces.FacesContextUtil;

/**
 * NOTE ELECT-2181, dass dieser (zugegeben unschöne) HACK notwendig ist, um 'Partial State Saving' trotz Tabellen mit Action-Menüs per Zeile verwenden zu können.
 * <p/>
 * Entsprechend dem Action-Menü mit der maximalen Anzahl an Actions (welches auch immer das ist) müssen im Template
 * <code>/elect-common-war/src/main/webapp/WEB-INF/tags/modelBuilderActionMenu.xhtml</code> dementsprechend viele MenuItem definiert sein. Dieser Workaround ersetzt das vorher verwendete MenuModel!
 */
public abstract class AbstractHackedPartialStateSavingActionMenuModelBuilder<E extends Entity< ? , ? >> {

  private static final int MAX_ACTION_ENTRIES_COUNT = 35;

  private final Map<E, List<DefaultMenuItem>> menuItemsPerEntity;

  private List<ActionEntry<E>> actionEntries;

  protected AbstractHackedPartialStateSavingActionMenuModelBuilder() {
    this.menuItemsPerEntity = new HashMap<>();
  }

  public boolean isRendered(E entity, int index) {
    DefaultMenuItem menuItem = getMenuModelMenuItem(entity, index);

    return menuItem != null && menuItem.isRendered();
  }

  public boolean isDisabled(E entity, int index) {
    if (isRendered(entity, index)) {
      DefaultMenuItem menuModelMenuItem = getMenuModelMenuItem(entity, index);
      if (menuModelMenuItem!=null) {
        return menuModelMenuItem.isDisabled();
      }
    }

    return true;
  }

  @Nullable
  public Object getValue(E entity, int index) {
    if (isRendered(entity, index)) {
      DefaultMenuItem menuModelMenuItem = getMenuModelMenuItem(entity, index);
      if (menuModelMenuItem!=null) {
        return menuModelMenuItem.getValue();
      }
    }

    return null;
  }

  @Nullable
  public String getIcon(E entity, int index) {
    if (isRendered(entity, index)) {
      DefaultMenuItem menuModelMenuItem = getMenuModelMenuItem(entity, index);
      if (menuModelMenuItem!=null) {
        return menuModelMenuItem.getIcon();
      }
    }

    return null;
  }

  public boolean isAjax(E entity, int index) {
    if (isRendered(entity, index)) {
      DefaultMenuItem menuModelMenuItem = getMenuModelMenuItem(entity, index);
      if (menuModelMenuItem!=null) {
        return menuModelMenuItem.isAjax();
      }
    }

    return false;
  }

  @Nullable
  public String getOnstart(E entity, int index) {
    if (isRendered(entity, index)) {
      DefaultMenuItem menuModelMenuItem = getMenuModelMenuItem(entity, index);
      if (menuModelMenuItem!=null) {
        return menuModelMenuItem.getOnstart();
      }
    }

    return null;
  }

  @Nullable
  public String getOncomplete(E entity, int index) {
    if (isRendered(entity, index)) {
      DefaultMenuItem menuModelMenuItem = getMenuModelMenuItem(entity, index);
      if (menuModelMenuItem!=null) {
        return menuModelMenuItem.getOncomplete();
      }
    }

    return null;
  }

  @Nullable
  public String getUpdate(E entity, int index) {
    if (isRendered(entity, index)) {
      DefaultMenuItem menuModelMenuItem = getMenuModelMenuItem(entity, index);
      if (menuModelMenuItem!=null) {
        return menuModelMenuItem.getUpdate();
      }
    }

    return null;
  }

  @Nullable
  public String getStyleClass(E entity, int index) {
    if (isRendered(entity, index)) {
      DefaultMenuItem menuModelMenuItem = getMenuModelMenuItem(entity, index);
      if (menuModelMenuItem!=null) {
        return menuModelMenuItem.getStyleClass();
      }
    }

    return null;
  }

  @Nullable
  public String getStyle(E entity, int index) {
    if (isRendered(entity, index)) {
      DefaultMenuItem menuModelMenuItem = getMenuModelMenuItem(entity, index);
      if (menuModelMenuItem!=null) {
        return menuModelMenuItem.getStyle();
      }
    }

    return null;
  }

  @Nullable
  private DefaultMenuItem getMenuModelMenuItem(E entity, int index) {
    if (entity != null && isActionEntryAvailable(index)) {
      if (!menuItemsPerEntity.containsKey(entity)) {
        List<MenuElement> menuModelChildren = getMenuModel(entity).getElements();

        List<DefaultMenuItem> menuItems = menuModelChildren.stream()
            .filter(child -> child instanceof DefaultMenuItem).map(child -> (DefaultMenuItem) child)
            .collect(Collectors.toList());

        if (index >= menuItems.size()) {
          throw new IllegalStateException("Action-Menue unterstuetzt momentan maximal " + menuItems.size() + " MenuItem [index = " + index + "]");
        }

        menuItemsPerEntity.put(entity, menuItems);
      }

      return menuItemsPerEntity.get(entity).get(index);
    }

    return null;
  }

  private List<ActionEntry<E>> getActionEntries() {
    if (actionEntries == null) {
      actionEntries = new ArrayList<>(getActionEntriesInklusiveProtokollActionSorted());

      if (actionEntries.size() > getMaxActionEntriesCount()) {
        throw new IllegalStateException("Action-Menue besitzt mehr als die maximal erwarteten Actions [count = " + actionEntries.size() + ", max = " + getMaxActionEntriesCount()
            + "]. Die maximal erwartete Anzahl muss dementspreched in der Klasse " + AbstractHackedPartialStateSavingActionMenuModelBuilder.class.getName() + " angepasst werden!");
      }
    }

    return actionEntries;
  }

  public int getActionEntriesCount() {
    return getActionEntries().size();
  }

  public int getMaxActionEntriesCount() {
    return MAX_ACTION_ENTRIES_COUNT;
  }

  private boolean isActionEntryAvailable(int index) {
    return index >= 0 && index < getActionEntriesCount();
  }

  public void executeMenuItemAction(Number index) {
    ((AbstractBaseActionEntry<E>) getActionEntries().get(index.intValue())).executeAction(getEntityFromMenu());
  }

  @SuppressWarnings("unchecked")
  private E getEntityFromMenu() {
    // Cf. de.ivu.elect.jee.presentation.action.AbstractBaseActionEntry.processAction(ActionEvent)
    return (E) FacesContextUtil.getCurrentComponent().getParent().getAttributes().get("entity");
  }

  protected abstract List<ActionEntry<E>> getActionEntriesInklusiveProtokollActionSorted();

  public abstract DefaultMenuModel getMenuModel(E entity);

  public void resetMenuItemsPerEntity() {
    this.menuItemsPerEntity.clear();
  }


  // Zum  Generieren wieder einkommentieren und ausführen
//  public static void main(String... args) {
//    /*
//     * Letzter Überschlag zu ActionEntries per ActionMenuBuilder (inklusive Eintrag für's Protokoll)...
//     *
//     * 1 bis 3 -> 19, 4 bis 6 -> 15, 7 bis ... -> 7
//     */
//    int modul = 1;
//
//    // Cf. /elect-common-war/src/main/webapp/WEB-INF/tags/modelBuilderActionMenu.xhtml!
//    String menuItemFormat = "<p:menuitem rendered=\"#{modelBuilder.isRendered(modelEntity, 0)}\" disabled=\"#{modelBuilder.isDisabled(modelEntity, 0)}\" action=\"#{modelBuilder.executeMenuItemAction(0)}\" ajax=\"#{modelBuilder.isAjax(modelEntity, 0)}\" onstart=\"#{modelBuilder.getOnstart(modelEntity, 0)}\" oncomplete=\"#{modelBuilder.getOncomplete(modelEntity, 0)}\" update=\"#{modelBuilder.getUpdate(modelEntity, 0)}\" value=\"#{modelBuilder.getValue(modelEntity, 0)}\" icon=\"#{modelBuilder.getIcon(modelEntity, 0)}\" styleClass=\"#{modelBuilder.getStyleClass(modelEntity, 0)}\" style=\"#{modelBuilder.getStyle(modelEntity, 0)}\" />";
//
//    int ifCount = 0;
//
//    System.out.println("<!-- AUTO-GENERATED - START -->");
//
//    for (int i = 0; i < MAX_ACTION_ENTRIES_COUNT; i++) {
//      if (i > 0 && i % modul == 0) {
//        /*
//         * NOTE, dass <c:if /> verwendet werden muss, da dieses (bspw. im Gegensatz zu <ui:fragment rendered="..." />) kein Kind-Element in den XHTML-Baum hängt, denn <p:menu /> erlaubt nur
//         * <p:menuitem /> als Kind-Elemente!
//         */
//        System.out.println("<c:if test=\"#{modelBuilder.actionEntriesCount > " + i + "}\">");
//
//        ifCount++;
//      }
//
//      System.out.println(menuItemFormat.replaceAll("0", String.valueOf(i)));
//    }
//
//    for (int i = 0; i < ifCount; i++) {
//      System.out.println("</c:if>");
//    }
//
//    System.out.println("<!-- AUTO-GENERATED - END -->");
//  }

}
