
package de.ivu.elect.jee.faces;

import java.io.IOException;

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.faces.application.Application;
import javax.faces.application.NavigationHandler;
import javax.faces.application.ProjectStage;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.Flash;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.jee.presentation.redirect.RedirectInterceptor;

public final class FacesContextUtil {

  private static final Logger LOG = LoggerFactory.getLogger(FacesContextUtil.class);

  private static final String OUTCOME_NAVIGATE_TO_ERROR_VIEW = "navigateToErrorView"; // siehe navigation-rule in faces-config.xml

  private static final String OUTCOME_NAVIGATE_TO_LOGIN_VIEW = "navigateToLoginView"; // siehe navigation-rule in faces-config.xml

  private FacesContextUtil() {
    super();
  }

  public static FacesContext getFacesContext() {
    return FacesContext.getCurrentInstance();
  }

  public static ExternalContext getExternalContext() {
    return getFacesContext().getExternalContext();
  }

  public static HttpServletRequest getRequest() {
    return getRequest(getFacesContext());
  }

  public static HttpServletRequest getRequest(FacesContext fc) {
    return (HttpServletRequest) fc.getExternalContext().getRequest();
  }

  public static String getContextPath() {
    return getRequest().getContextPath();
  }

  @SuppressWarnings("unchecked")
  public static <T> T getElValue(FacesContext fc, String el, Class<T> clazz) {
    ExpressionFactory expressionFactory = fc.getApplication().getExpressionFactory();
    ValueExpression valueExpression = expressionFactory.createValueExpression(fc.getELContext(), el, clazz);
    return (T) valueExpression.getValue(fc.getELContext());
  }

  public static <T> T getElValue(String el, Class<T> clazz) {
    return getElValue(FacesContext.getCurrentInstance(), el, clazz);
  }

  public static void redirectToView(String viewId) {
    redirectToView(FacesContext.getCurrentInstance(), viewId);
  }

  /**
   * Nicht wahlId, WahlterminId, GebietId in View-Parameter aufnehmen
   */
  public static void redirectToViewNoRedirectParameter(String viewId) {
    redirectToView(FacesContext.getCurrentInstance(), viewId, false);
  }

  public static void redirectToExternalUrl(String url) {
    try {
      FacesContext.getCurrentInstance().getExternalContext().redirect(url);
    } catch (IOException e) {
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  public static void redirectToView(FacesContext fc, String viewId) {
    redirectToView(fc, viewId, true);
  }

  public static void redirectToView(FacesContext fc, String viewId, boolean includeRediretParameter) {
    try {
      // ELECT-225
      // Die folgende Methode funktioniert im Fehlerfall nicht hinreichend: javax.enterprise.context.ContextNotActiveException: Conversation Context not active when method called on conversation Transient conversation
      Application application = fc.getApplication();
      NavigationHandler navigationHandler = application.getNavigationHandler();
      navigationHandler.handleNavigation(fc, null, RedirectInterceptor.concat(viewId, RedirectInterceptor.getRedirectSuffix(includeRediretParameter)));
    } catch (IllegalStateException e) {
      //ELECT-3794
      LOG.warn("Redirect nicht ausgeführt: " + viewId + ", " + e.getMessage());
    }
  }

  /**
   * Benötigt navigation-rule in faces-config.xml
   */
  public static void redirectToErrorView() {
    triggerNavigationRule(OUTCOME_NAVIGATE_TO_ERROR_VIEW);
  }

  /**
   * Benötigt navigation-rule in faces-config.xml
   */
  public static void redirectToLoginView() {
    triggerNavigationRule(OUTCOME_NAVIGATE_TO_LOGIN_VIEW);
  }

  /*
   * Siehe navigation-rule in faces-config.xml
   */
  private static void triggerNavigationRule(String outcome) {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    Application application = facesContext.getApplication();
    NavigationHandler navigationHandler = application.getNavigationHandler();
    navigationHandler.handleNavigation(facesContext, null, outcome);
    facesContext.renderResponse();
  }

  public static String getCurrentViewId() {
    return getCurrentViewId(getFacesContext());
  }

  public static String getCurrentTopic() {
    String currentViewId = getCurrentViewId();
    if (StringUtils.isBlank(currentViewId) || currentViewId.length() < 2 || !currentViewId.substring(1).contains("/")) {
      return null;
    }
    return currentViewId.substring(1, currentViewId.lastIndexOf("/"));
  }

  private static String getCurrentViewId(FacesContext fc) {
    return fc.getViewRoot().getViewId();
  }

  public static UIComponent getCurrentComponent() {
    return UIComponent.getCurrentComponent(FacesContext.getCurrentInstance());
  }

  public static UIComponent getCurrentCompositeComponent() {
    return UIComponent.getCurrentCompositeComponent(FacesContext.getCurrentInstance());
  }

  public static Flash flashScope() {
    return FacesContext.getCurrentInstance().getExternalContext().getFlash();
  }

  @SuppressWarnings("unchecked")
  public static <T> T getContextualInstance(BeanManager manager, Class<T> type) {
    T result = null;
    Class notTypeClass = type; // Notwendig, weil der Compiler in IntelliJ hier rumzickt...
    Bean< ? > bean = manager.resolve(manager.getBeans(notTypeClass));
    Bean beanNoType = bean; // Notwendig, weil der Compiler in IntelliJ hier rumzickt...
    if (beanNoType != null) {
      CreationalContext<T> context = manager.createCreationalContext(beanNoType);
      if (context != null) {
        result = (T) manager.getReference(beanNoType, notTypeClass, context);
      }
    }
    return result;
  }

  public static BeanManager getBeanManager() {
    return (BeanManager) ((ServletContext) getFacesContext().getExternalContext().getContext()).getAttribute("javax.enterprise.inject.spi.BeanManager");
  }

  public static <T> T getContextualInstance(final Class<T> type) {
    return getContextualInstance(getBeanManager(), type);
  }

  public static void renderResponse() {
    FacesContext.getCurrentInstance().renderResponse();
  }

  public static boolean isAjaxRequest() {
    return getFacesContext().getPartialViewContext() != null && getFacesContext().getPartialViewContext().isAjaxRequest();
  }

  public static boolean isPartialRequest() {
    return getFacesContext().getPartialViewContext() != null && getFacesContext().getPartialViewContext().isPartialRequest();
  }

  public static void requireDevelopmentStage() {
    if (!isDevelopmentStage()) {
      throw new IllegalStateException("Nur für Entwicklung zulässig!");
    }
  }

  public static boolean isDevelopmentStage() {
    if (DevelopmentStore.isDevelopmentStageSet()){
      return DevelopmentStore.isDevelopmentStage();
    }

    // getFacesContext == null z.B. in Integration Tests
    return getFacesContext() != null && getFacesContext().isProjectStage(ProjectStage.Development);
  }
}
