
package de.ivu.elect.presentation.gebietsbaum.tree;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nullable;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.inject.Inject;

import org.primefaces.event.NodeExpandEvent;
import org.primefaces.event.NodeSelectEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;

import de.ivu.elect.business.gebietsbaum.boundary.GebietChangeEvent;
import de.ivu.elect.business.gebietsbaum.boundary.GebietSessionSelect;
import de.ivu.elect.business.wahl.boundary.WahlSelectChangeEvent;
import de.ivu.elect.business.wahl.boundary.WahlterminSelectChangeEvent;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.faces.FacesMessageUtil;
import de.ivu.elect.jee.presentation.AjaxHelper;
import de.ivu.elect.presentation.LayoutComponents;
import de.ivu.elect.presentation.LayoutComponentsContext;
import de.ivu.elect.presentation.security.ElectRedirectParameterAttachement;


import static java.util.Arrays.asList;

public abstract class AbstractNoStoreGebietsbaumView implements Serializable {

  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "gebietsbaumView";

  public static final String GEBIETSBAUM_FORM_ID = "frmTree";

  @Inject
  protected transient AjaxHelper ajaxHelper;

  @Inject
  protected transient GebietSessionSelect gebietSelect;

  @Inject
  protected transient LayoutComponentsContext layoutComponentsContext;

  private final DefaultTreeNode dummyRoot = new DefaultTreeNode("Root", null);

  public AbstractNoStoreGebietsbaumView() {
    super();
  }

  protected abstract AbstractGebietsbaumTreeBuilder getGebietsbaumTreeBuilder();

  protected abstract DefaultTreeNode getRootInternal();

  protected abstract void setRootInternal(@Nullable DefaultTreeNode root);

  protected abstract void setSelectedNodeInternal(@Nullable TreeNode selectedNode);

  protected abstract TreeNode getSelectedNodeInternal();

  public boolean isTreeDynamic() {
    return getGebietsbaumTreeBuilder().isDynamic();
  }

  protected void initRootAndSelectedNode() {
    if (getRootInternal() == null) {
      DefaultTreeNode newRootNode = new DefaultTreeNode("Root", null);
      setRootInternal(newRootNode);

      GebietsbaumTreeModel model = getGebietsbaumTreeBuilder().buildModel();

      TreeNode rootNode = model.getRootNode();
      if (rootNode != null) {
        newRootNode.getChildren().add(rootNode);
      }

      setSelectedNodeInternalInternal(model.getSelectedNode());
    }
  }

  private void setSelectedNodeInternalInternal(TreeNode selectedNode) {
    setSelectedNodeInternal(selectedNode);
  }

  public TreeNode getRoot() {
    if (isTreeReady()) {
      initRootAndSelectedNode();
      return getRootInternal();
    } else {
      return dummyRoot;
    }
  }

  @Nullable
  public TreeNode getSelectedNode() {
    if (isTreeReady()) {
      initRootAndSelectedNode();
      return getSelectedNodeInternal();
    } else {
      return null;
    }
  }

  public void setSelectedNode(@Nullable TreeNode selectedNode) {
    // nichts tun, auf event reagieren
  }

  public void onNodeSelect(NodeSelectEvent event) {
    if (event == null || event.getTreeNode() == null) {
      // sollte nicht passieren, passiert aber
      reset();
      FacesContextUtil.redirectToView(FacesContextUtil.getCurrentViewId());
    } else {
      if (!event.getTreeNode().equals(getSelectedNode()) && event.getTreeNode().getData() != null) {
        GebietsbaumTreeNodeData data = (GebietsbaumTreeNodeData) event.getTreeNode().getData();
        if (data.isSelectable()) {
          // War füher mal:
          // FacesContextUtil.redirectToView(FacesContextUtil.getCurrentViewId());
          setSelectedNodeInternal(event.getTreeNode());
          gebietSelect.setEntity(data.getPk());
          refreshMessages();
          refreshPageContent(data.getPk().getId());
        } else {
          FacesMessageUtil.addWarnMessage("gebiet.select.kein.recht");
          refreshMessages();
        }
      }
      ajaxHelper.executeScript("PF('ctnContentBlockUI').hide()");
    }
  }

  private void refreshPageContent(Long gebietId) {
    String pageMainMenu = LayoutComponents.MAIN_MENU;
    String pageContentHeader = LayoutComponents.CTN_PAGE_CONTENT_HEADER_ID;

    // NOTE ELECT-7641, dass nur mit dem ":"-Prefix die Aktualisierung des Inhaltes für ALLE Suchen zu funktionieren scheint...
    String pageContent = LayoutComponents.CTN_PAGE_CONTENT_CENTER_ID;
    //  wieder raus ELECT-7602 String pageContentDoppelpunkt = ":" + LayoutComponents.CTN_PAGE_CONTENT_CENTER_ID;

    List<String> refreshComponents = new ArrayList<>(asList(pageContentHeader, pageContent, pageMainMenu));

    // Nur sinnvoll, wenn überhaupt Content vorhanden
    if (layoutComponentsContext.isSubcontextPresent()) {
      refreshComponents.add(LayoutComponents.HEADER_SUBCONTEXT);
    }

    ajaxHelper.update(refreshComponents);

    ajaxHelper.executeScript("if (window.initSearchTable) {initSearchTable();}");
    // URL des Browsers auf aktuelles Gebiet setzen
    ajaxHelper.executeScript("try {window.history.pushState('object or string', 'Title', window.location.pathname + window.location.search.replace(/"
        + ElectRedirectParameterAttachement.ELECT_GEBIET_ID + "=\\d+/, '" + ElectRedirectParameterAttachement.ELECT_GEBIET_ID + "=" + gebietId + "'));} catch (e) {}");
  }

  private void refreshMessages() {
    ajaxHelper.updateMessages();
  }

  public void onNodeExpand(NodeExpandEvent event) {
    // Zu überschreiben, falls Baum bswp. lazy geladen wird.
  }

  public void onWahlSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) WahlSelectChangeEvent event) {
    reset();
  }

  public void onWahlterminSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) WahlterminSelectChangeEvent event) {
    reset();
  }

  public void gebietChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) GebietChangeEvent event) {
    reset();
  }

  public void reset() {
    setRootInternal(null);
    setSelectedNodeInternalInternal(null);
    setTreeReadyInternal(false);
    getGebietsbaumTreeBuilder().reset();
  }

  @Nullable
  public TreeNode[] getSelectedNodes() {
    if (getSelectedNode() == null) {
      return null;
    }

    return new TreeNode[] {getSelectedNode()};
  }

  public void setSelectedNodes(TreeNode[] selectedNodes) {
    // nichts zu tun this.selectedNodes = selectedNodes;
  }

  public boolean isTreeReady() {
    if (getRootInternal() == null) {
      return isTreeReadyInternal();
    }
    return true;
  }

  public void setTreeReady() {
    setTreeReadyInternal(true);
  }

  protected abstract void setTreeReadyInternal(boolean treeReady);

  protected abstract boolean isTreeReadyInternal();

}
