/*
* Copyright 2014 - LAit SpA
*				 Realizzato da Icona Management srl su commessa LAit SpA
* Copyright 2015- LAit SpA
*                                                  Aggiornato da LAit SpA
*
* Concesso in licenza a norma dell'EUPL, versione 1.1 o
* successive dell'EUPL (la "Licenza")– non appena saranno
* approvate dalla Commissione europea;
* Non è possibile utilizzare l'opera salvo nel rispetto della Licenza.
* È possibile ottenere una copia della Licenza al seguente indirizzo:
*
* http://ec.europa.eu/idabc/eupl
*
* Salvo diversamente indicato dalla legge applicabile o
* concordato per iscritto, il software distribuito secondo
* i termini della Licenza è distribuito "TAL QUALE",
* SENZA GARANZIE O CONDIZIONI DI ALCUN TIPO,
* esplicite o implicite.
* Si veda la Licenza per la lingua specifica che disciplina
* le autorizzazioni e le limitazioni secondo i termini della
* Licenza.
*/
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package it.laitspa.cpf.faces.utils;

import it.laitspa.cpf.util.log.LogUtils;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.activation.DataHandler;
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.FacesEvent;
import javax.faces.model.SelectItem;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.quartz.JobExecutionContext;
import org.quartz.Scheduler;
import org.quartz.SchedulerContext;
import org.richfaces.model.UploadItem;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * 
 * @author Andrea
 */
public class FacesUtils
{

  static transient Logger logger;

  static
  {
    logger = Logger.getLogger(FacesUtils.class);
  }

  /**
   * 
   * @param name
   *          name of a Bean to retrieve
   * @return the Bean
   */
  public static Object findBean(String name)
  {

    // Ambiente in cui vive faces
    //
    FacesContext context = FacesContext.getCurrentInstance();

    // Il context in cui vive Spring
    //
    ExternalContext extContext = context.getExternalContext();

    // Application Context contiene tutti i bean Faces
    //
    ServletContext servletContext = (ServletContext)extContext.getContext();
    ApplicationContext appContext =
        WebApplicationContextUtils.getWebApplicationContext(servletContext);

    Object res = appContext.getBean(name);
    if(res == null)
    {
      String msg = "Unable to retrieve spring bean: " + name;
      logger.error(msg);
    }
    return res;
  }

  public static <K> K findBean(Class<K> clazz)
  {
    String beanName = clazz.getSimpleName();
    beanName = beanName.substring(1).concat("Facade");
    @SuppressWarnings("unchecked")
    K res = (K)findBean(beanName);
    return res;
  }

  public static Object findBean(String name, JobExecutionContext context)
  {
    Object res = null;
    try
    {
      Scheduler scheduler = context.getScheduler();
      SchedulerContext sContext = scheduler.getContext();
      ApplicationContext appContext = (ApplicationContext)sContext.get("applicationContext");
      res = appContext.getBean(name);
      if(res == null)
      {
        String msg = "Unable to retrieve spring bean: " + name;
        logger.error(msg);
      }
    }
    catch(Exception e)
    {
      logger.error(e);
    }
    return res;
  }

  public static Object getFacesBean(String beanName)
  {
    FacesContext ctx = FacesContext.getCurrentInstance();

    ELContext elCtx = ctx.getELContext();
    Application app = ctx.getApplication();
    Object res = app.getELResolver().getValue(elCtx, null, beanName);
    // ExternalContext ext = ctx.getExternalContext();
    // Map sessionMap = ext.getSessionMap();
    // Object res = sessionMap.get(beanName);
    if(res == null)
    {
      String msg = "Unable to retrieve class bean: " + beanName;
      logger.error(msg);
    }
    return res;
  }

  public static <K> K getFacesBean(Class<K> clazz)
  {
    String beanName = clazz.getSimpleName();
    @SuppressWarnings("unchecked")
    K res = (K)getFacesBean(beanName);
    return res;
  }

  public static HttpSession getCurrentSession()
  {
    FacesContext ctx = FacesContext.getCurrentInstance();
    HttpServletRequest req = (HttpServletRequest)ctx.getExternalContext().getRequest();
    return req.getSession();
  }

  public static HttpServletRequest getRequest()
  {
    FacesContext ctx = FacesContext.getCurrentInstance();
    HttpServletRequest req = (HttpServletRequest)ctx.getExternalContext().getRequest();
    return req;
  }

  public static Object getComponentAttribute(FacesEvent event, String name)
  {
    return event.getComponent().getAttributes().get(name);
  }

  public static Object getRequestParameter(String name)
  {
    return FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()
        .get(name);
  }
  
  public static Object getSessionMapValue(String key)
  {
    return FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(key);
  }

  public static Object removeSessionMapValue(String key)
  {
    return FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(key);
  }

  public static void setSessionMapValue(String key, Object value)
  {
    FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(key, value);
  }

  public static Object getApplicationMapValue(String key)
  {
    return FacesContext.getCurrentInstance().getExternalContext().getApplicationMap().get(key);
  }

  public static void setApplicationMapValue(String key, Object value)
  {
    FacesContext.getCurrentInstance().getExternalContext().getApplicationMap().put(key, value);
  }

  /**
   * 
   * @param bundleName
   * @param key
   *          identify the message
   * @param params
   * @return the message
   */
  public static String getLocalMessageFromBundle(String bundleName, String key, Object[] params)
  {

    // Classe Java che individua la lingua
    //
    Locale loc = FacesContext.getCurrentInstance().getViewRoot().getLocale();

    // Recupera il messaggio dal file di properties
    //
    return getMessageFromBundle(bundleName, key, params, loc);
  }

  @SuppressWarnings("rawtypes")
  public static String getLocalMessageFromBundle(Class clazz, String key, Object[] params)
  {

    String className = clazz.getName();
    int index = className.lastIndexOf(".");
    if(index != -1)
    {
      String packageName = className.substring(0, index + 1);
      String bundleName = packageName + "Message";
      return getLocalMessageFromBundle(bundleName, key, params);
    }
    else
      return null;
  }

  /**
   * 
   * @param bundleName
   * @param key
   * @param params
   * @param locale
   * @return
   */
  public static String getMessageFromBundle(String bundleName, String key, Object[] params,
      Locale locale)
  {
    String message = null;
    try
    {
      ResourceBundle bundle = ResourceBundle.getBundle(bundleName, locale);
      message = bundle.getString(key);
    }
    catch(MissingResourceException error)
    {
      StringBuilder build = new StringBuilder();
      build.append("key:'");
      build.append(key);
      build.append("':");
      build.append("with error:");
      build.append(error);
      message = build.toString();
    }
    if(params != null)
      message = MessageFormat.format(message, params);
    return message;
  }

  public static void addContextMessage(String summary)
  {
    FacesMessage msg = new FacesMessage(summary);
    addContextMessage(msg);
  }

  @SuppressWarnings("rawtypes")
  public static void addContextMessage(Class clazz, String sumkey, String detkey)
  {
    String summary = getLocalMessageFromBundle(clazz, sumkey, null);
    String detail = getLocalMessageFromBundle(clazz, detkey, null);
    addContextMessage(summary, detail);
  }

  public static void addContextMessage(String summary, String detail)
  {
    FacesMessage msg = new FacesMessage(summary + " - " + detail, null);
    addContextMessage(msg);
  }

  public static void addContextMessage(Severity severity, String summary, String detail)
  {
    FacesMessage msg = new FacesMessage(severity, null, summary + " - " + detail);
    addContextMessage(msg);
  }

  public static void addContextMessage(FacesMessage msg)
  {
    addContextMessage(null, msg);
  }

  public static void addContextMessage(String clientId, FacesMessage msg)
  {
    FacesContext.getCurrentInstance().addMessage(clientId, msg);
  }

  @SuppressWarnings("rawtypes")
  public static void addContextErrorMessage(Class clazz, String key)
  {
    String detail = getLocalMessageFromBundle(clazz, key, null);
    FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Errore - " + detail, null);
    FacesContext.getCurrentInstance().addMessage(null, msg);
  }

  @SuppressWarnings("rawtypes")
  public static void addContextErrorMessage(Class clazz, String key, Object[] params)
  {
    String detail = getLocalMessageFromBundle(clazz, key, params);
    FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Errore - " + detail, null);
    FacesContext.getCurrentInstance().addMessage(null, msg);
  }

  public static void addContextErrorMessage(String detail)
  {
    /*
     * Construct a new FacesMessage with the specified initial values
     */
    FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Errore - " + detail, null);

    // Append a FacesMessage to the set of messages associated with the
    // specified client identifier, if clientId is not null.
    //
    FacesContext.getCurrentInstance().addMessage(null, msg);
  }

  /**
   * 
   * @param summary
   * @param detail
   */
  public static void addContextErrorMessage(String summary, String detail)
  {
    FacesMessage msg =
        new FacesMessage(FacesMessage.SEVERITY_FATAL, summary + " - " + detail, null);
    FacesContext.getCurrentInstance().addMessage(null, msg);
  }

  public static boolean isBindingExpression(String expression)
  {
    String regex = "#{[^}]+}";
    Pattern p = Pattern.compile(regex);
    Matcher m = p.matcher(expression);
    return m.find();
  }

  public static void refreshCurrentPage()
  {
    FacesContext context = FacesContext.getCurrentInstance();
    String viewId = context.getViewRoot().getViewId();
    ViewHandler handler = context.getApplication().getViewHandler();
    UIViewRoot root = handler.createView(context, viewId);
    root.setViewId(viewId);
    context.setViewRoot(root);
  }

  /**
   * Finds component with the given id
   */
  public static UIComponent findComponent(UIComponent c, String id)
  {
    if(id.equals(c.getId()))
    {
      return c;
    }
    Iterator<UIComponent> kids = c.getFacetsAndChildren();
    while(kids.hasNext())
    {
      UIComponent found = findComponent(kids.next(), id);
      if(found != null)
      {
        return found;
      }
    }
    return null;
  }

  public static UIComponent findUIComponentById(String id)
  {
    FacesContext context = FacesContext.getCurrentInstance();
    UIViewRoot view = context.getViewRoot();
    UIComponent component = findComponent(view, id);
    if(component == null)
      logger.error("Component not found for id: " + id);
    return component;
  }

  public static Object getUIComponentValue(String id)
  {
    UIComponent component = findUIComponentById(id);
    if(component == null)
    {
      return null;
    }

    UIInput ic = (UIInput)component;
    // Evaluate value in 3 phase. 
    // 1. Request param in submitted value
    // 2. Converted value in LocalValue
    // 3. Validation
    // 4. Apply value in value after validation.
    Object value = ic.getLocalValue();
    LogUtils.debug(FacesUtils.class, "Component id " + id + " >>>> local value: " + value);
    if(value == null) 
    {
      value = ic.getSubmittedValue();
      LogUtils.debug(FacesUtils.class, "Component id " + id + " >>>> submitted value: " + value);
    }
    if(value == null)
    {
      value = ic.getValue();
      LogUtils.debug(FacesUtils.class, "Component id " + id + " >>>> value: " + value);
    }

    return value;
  }

  public static String getCurrentView()
  {
    FacesContext ctx = FacesContext.getCurrentInstance();
    ExternalContext ectx = ctx.getExternalContext();
    String facespath = ectx.getRequestServletPath();
    String ctxp = ectx.getRequestContextPath();
    UIViewRoot view = ctx.getViewRoot();
    return ctxp.concat(facespath.concat(view.getViewId()));
  }

  public static String getCurrentViewId()
  {
    FacesContext ctx = FacesContext.getCurrentInstance();
    UIViewRoot view = ctx.getViewRoot();
    return view.getViewId();
  }

  @SuppressWarnings("rawtypes")
  public static ValueExpression getExpression(String value, Class expectedType)
  {
    Application app = FacesContext.getCurrentInstance().getApplication();
    ELContext ctx = FacesContext.getCurrentInstance().getELContext();
    ExpressionFactory factory = app.getExpressionFactory();
    ValueExpression exp = factory.createValueExpression(ctx, value, expectedType);
    return exp;
  }

  public static Object getInitParameter(String name)
  {
    return FacesContext.getCurrentInstance().getExternalContext().getInitParameter(name);
  }

  public static String getNormalizedFileName(String fileName)
  {
    if(fileName == null || fileName.trim().length() == 0)
      return null;

    // Convert to common unix system.
    //
    while(fileName.contains("\\"))
      fileName = fileName.replace("\\", "/");

    int idx = fileName.lastIndexOf("/");

    return idx >= 0 ? fileName.substring(idx + 1) : fileName;
  }

  /**
   * @param list
   *          input list to convert in SelectItem
   * @param value
   *          the get method to invoke to obtain the current values of
   *          SelectItem
   * @param label
   *          the get method to invoke to obtain the current label of SelectItem
   * @return
   */
  public static List<SelectItem> toSelectItems(List<?> list, String value, String label)
  {
    return toSelectItems(list, value, label, null);
  }

  /**
   * @param list
   *          input list to convert in SelectItem
   * @param value
   *          the get method to invoke to obtain the current values of
   *          SelectItem
   * @param label
   *          the get method to invoke to obtain the current label of SelectItem
   * @param object
   *          the get method to invoke to obtain the current hidden object of
   *          SelectObjectItem
   * @return
   */
  public static List<SelectItem> toSelectItems(List<?> list, String value, String label,
      String object)
  {

    if(list == null)
      return null;

    List<SelectItem> sl = new ArrayList<SelectItem>();

    for(Object o : list)
    {
      Class<?> c = o.getClass();
      try
      {
        Method mV = c.getMethod(value, (Class<?>[])null);
        Object rV = mV.invoke(o, (Object[])null);

        Method mL = c.getMethod(label, (Class<?>[])null);
        String lV = (String)mL.invoke(o, (Object[])null);

        Object oV = null;
        if(object != null)
        {
          
          Method mO = c.getMethod(object, (Class<?>[])null);
          oV = mO.invoke(o, (Object[])null);
        }
    

        ObjectSelectItem item = new ObjectSelectItem();
        item.setValue(rV);
        item.setLabel(lV);
        item.setItem(oV);
        sl.add(item);
      }
      catch(Exception e)
      {
        logger.error("Exception caught while iterating in list.", e);
        return null;
      }
    }

    return sl;
  }

  public static Object getItemFromValue(List<SelectItem> list, Object value)
  {

    if(list == null)
      return null;

    for(SelectItem o : list)
    {
      ObjectSelectItem os = (ObjectSelectItem)o;
      if(o.getValue() != null && o.getValue().equals(value))
        return os.getItem();
    }
    return null;
  }

  public static Object getItemFromValue(List<?> list, Object value, String name)
  {

    if(list == null)
      return null;

    for(Object o : list)
    {
      Class<?> c = o.getClass();
      try
      {
        Method mV = c.getMethod(name, (Class<?>[])null);
        Object rV = mV.invoke(o, (Object[])null);

        if(rV != null && rV.equals(value))
          return rV;
      }
      catch(Exception e)
      {
        logger.error("Exception caught while iterating in list.", e);
        return null;
      }
    }
    return null;
  }

  public static byte[] getDataFromFile(UploadItem file)
  {
    if(file == null)
      return null;

    byte[] data = file.getData();
    return data;
  }

  public static Object getObjectFromValue(List<?> list, Object value, String name)
  {

    if(list == null)
      return null;

    for(Object o : list)
    {
      Class<?> c = o.getClass();
      try
      {
        Method mV = c.getMethod(name, (Class<?>[])null);
        Object rV = mV.invoke(o, (Object[])null);

        if(rV != null && rV.equals(value))
          return o;
      }
      catch(Exception e)
      {
        logger.error("Exception caught while iterating in list.", e);
        return null;
      }
    }
    return null;
  }

  public static byte[] toBytes(DataHandler dh)
  {
    try
    {
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      InputStream in = dh.getInputStream();
      byte[] buffer = new byte[512];
      int bytesRead;
      while((bytesRead = in.read(buffer)) >= 0)
      {
        bos.write(buffer, 0, bytesRead);
      }
      return bos.toByteArray();
    }
    catch(Exception e)
    {
      logger.error("Error during datahandler conversion.", e);
      return null;
    }
  }



  public static String returnAndClean(String toReturn, String sessionPropertyToClean)
  {
    FacesUtils.removeSessionMapValue(sessionPropertyToClean);
    return toReturn;
  }

}
