package com.acme.util.utilities;


import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.MethodExpression;
import javax.el.ValueExpression;

import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import oracle.adf.share.ADFContext;
import oracle.adf.share.security.SecurityContext;
import oracle.adf.share.security.identitymanagement.UserProfile;
import oracle.adf.view.rich.component.rich.RichPopup;
import oracle.adf.view.rich.component.rich.data.RichTable;
import oracle.adf.view.rich.component.rich.input.RichInputDate;
import oracle.adf.view.rich.component.rich.input.RichInputFile;
import oracle.adf.view.rich.component.rich.input.RichInputText;
import oracle.adf.view.rich.component.rich.input.RichSelectManyChoice;
import oracle.adf.view.rich.component.rich.input.RichSelectOneChoice;
import oracle.adf.view.rich.component.rich.input.RichTextEditor;
import oracle.adf.view.rich.component.rich.layout.RichPanelTabbed;
import oracle.adf.view.rich.component.rich.layout.RichShowDetailItem;
import oracle.adf.view.rich.component.rich.nav.RichGoLink;
import oracle.adf.view.rich.component.rich.output.RichOutputText;
import oracle.adf.view.rich.context.AdfFacesContext;

import oracle.security.idm.IMException;
import oracle.security.idm.IdentityStore;
import oracle.security.idm.User;
import oracle.security.jps.JpsContext;
import oracle.security.jps.JpsContextFactory;
import oracle.security.jps.service.idstore.IdentityStoreService;

import org.apache.myfaces.trinidad.component.UIXEditableValue;
import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
import org.apache.myfaces.trinidad.util.Service;


/**
 * General useful static utilies for working with JSF.
 * NOTE: Updated to use JSF 1.2 ExpressionFactory.
 *
 * @author Duncan Mills
 * @author Steve Muench
 *
 * $Id: JSFUtils.java 1885 2007-06-26 00:47:41Z ralsmith $
 */
public class JSFUtils
{

    private static final String NO_RESOURCE_FOUND = "Missing resource: ";

    /**
     * Method for taking a reference to a JSF binding expression and returning
     * the matching object (or creating it).
     * @param expression EL expression
     * @return Managed object
     */


    public static Object resolveExpression(String expression)
    {

        FacesContext facesContext = getFacesContext();
        Application app = facesContext.getApplication();
        ExpressionFactory elFactory = app.getExpressionFactory();
        ELContext elContext = facesContext.getELContext();
        ValueExpression valueExp = elFactory.createValueExpression(elContext, expression, Object.class);
        return valueExp.getValue(elContext);
    }

    /**
     * @return
     */
    public static String resolveRemoteUser()
    {
        FacesContext facesContext = getFacesContext();
        ExternalContext ectx = facesContext.getExternalContext();
        return ectx.getRemoteUser();
    }

    /**
     * @return
     */
    public static String resolveUserPrincipal()
    {
        FacesContext facesContext = getFacesContext();
        ExternalContext ectx = facesContext.getExternalContext();
        HttpServletRequest request = (HttpServletRequest)ectx.getRequest();
        return request.getUserPrincipal().getName();
    }

    /**
     * @param expression
     * @param returnType
     * @param argTypes
     * @param argValues
     * @return
     */
    public static Object resolveMethodExpression(String expression, Class returnType, Class[] argTypes, Object[] argValues)
    {
        FacesContext facesContext = getFacesContext();
        Application app = facesContext.getApplication();
        ExpressionFactory elFactory = app.getExpressionFactory();
        ELContext elContext = facesContext.getELContext();
        MethodExpression methodExpression = elFactory.createMethodExpression(elContext, expression, returnType, argTypes);
        return methodExpression.invoke(elContext, argValues);
    }

    /**
     * Method for taking a reference to a JSF binding expression and returning
     * the matching Boolean.
     * @param expression EL expression
     * @return Managed object
     */
    public static Boolean resolveExpressionAsBoolean(String expression)
    {
        return (Boolean)resolveExpression(expression);
    }


    /**
     * Refresh yapılacak componentId alınarak refresh islemi yapılır.
     * @param componentId
     *
     * */
    public static void refreshComponent(String componentId)
    {
        AdfFacesContext.getCurrentInstance().addPartialTarget(findComponentInRoot(componentId));
    }

    public static void refreshComponent(UIComponent component)
    {
        AdfFacesContext.getCurrentInstance().addPartialTarget(component);
    }


    /**
     * Method deletionPopup kapatma.
     *
     */
    public static void hidePopup(RichPopup rp)
    {
        FacesContext ctx = FacesContext.getCurrentInstance();
        String popupId = rp.getClientId(ctx);
        Service.getRenderKitService(ctx, ExtendedRenderKitService.class).addScript(ctx, "var popup = AdfPage.PAGE.findComponent('" + popupId + "'); popup.hide();");
    }

    public static void hidePopup(String popupId)
    {
        RichPopup popup = (RichPopup)findComponentInRoot(popupId);
        popup.hide();
    }


    /**
     * Method for taking a reference to a JSF binding expression and returning
     * the matching String (or creating it).
     * @param expression EL expression
     * @return Managed object
     */
    public static String resolveExpressionAsString(String expression)
    {
        return (String)resolveExpression(expression);
    }

    /**
     * Convenience method for resolving a reference to a managed bean by name
     * rather than by expression.
     * @param beanName name of managed bean
     * @return Managed object
     */
    public static Object getManagedBeanValue(String beanName)
    {
        StringBuffer buff = new StringBuffer("#{");
        buff.append(beanName);
        buff.append("}");
        return resolveExpression(buff.toString());
    }

    /**
     * Method for setting a new object into a JSF managed bean
     * Note: will fail silently if the supplied object does
     * not match the type of the managed bean.
     * @param expression EL expression
     * @param newValue new value to set
     */
    public static void setExpressionValue(String expression, Object newValue)
    {
        FacesContext facesContext = getFacesContext();
        Application app = facesContext.getApplication();
        ExpressionFactory elFactory = app.getExpressionFactory();
        ELContext elContext = facesContext.getELContext();
        ValueExpression valueExp = elFactory.createValueExpression(elContext, expression, Object.class);

        //Check that the input newValue can be cast to the property type
        //expected by the managed bean.
        //If the managed Bean expects a primitive we rely on Auto-Unboxing
        Class bindClass = valueExp.getType(elContext);
        if (bindClass.isPrimitive() || bindClass.isInstance(newValue))
        {
            valueExp.setValue(elContext, newValue);
        }
    }

    /**
     * Convenience method for setting the value of a managed bean by name
     * rather than by expression.
     * @param beanName name of managed bean
     * @param newValue new value to set
     */
    public static void setManagedBeanValue(String beanName, Object newValue)
    {
        StringBuffer buff = new StringBuffer("#{");
        buff.append(beanName);
        buff.append("}");
        setExpressionValue(buff.toString(), newValue);
    }


    /**
     * Convenience method for setting Session variables.
     * @param key object key
     * @param object value to store
     */

    public static void storeOnSession(String key, Object object)
    {
        FacesContext ctx = getFacesContext();
        Map sessionState = ctx.getExternalContext().getSessionMap();
        sessionState.put(key, object);
    }

    /**
     * Convenience method for getting Session variables.
     * @param key object key
     * @return session object for key
     */
    public static Object getFromSession(String key)
    {
        FacesContext ctx = getFacesContext();
        Map sessionState = ctx.getExternalContext().getSessionMap();
        return sessionState.get(key);
    }

    /**
     * @param key
     * @return
     */
    public static String getFromHeader(String key)
    {
        FacesContext ctx = getFacesContext();
        ExternalContext ectx = ctx.getExternalContext();
        return ectx.getRequestHeaderMap().get(key);
    }

    /**
     * Convenience method for getting Request variables.
     * @param key object key
     * @return session object for key
     */
    public static Object getFromRequest(String key)
    {
        FacesContext ctx = getFacesContext();
        Map sessionState = ctx.getExternalContext().getRequestMap();
        return sessionState.get(key);
    }


    /**
     * Pulls a String resource from the property bundle that
     * is defined under the application &lt;message-bundle&gt; element in
     * the faces config. Respects Locale
     * @param key string message key
     * @return Resource value or placeholder error String
     */
    public static String getStringFromBundle(String key)
    {
        ResourceBundle bundle = getBundle();
        return getStringSafely(bundle, key, null);
    }


    /**
     * Convenience method to construct a <code>FacesMesssage</code>
     * from a defined error key and severity
     * This assumes that the error keys follow the convention of
     * using <b>_detail</b> for the detailed part of the
     * message, otherwise the main message is returned for the
     * detail as well.
     * @param key for the error message in the resource bundle
     * @param severity severity of message
     * @return Faces Message object
     */
    public static FacesMessage getMessageFromBundle(String key, FacesMessage.Severity severity)
    {
        ResourceBundle bundle = getBundle();
        String summary = getStringSafely(bundle, key, null);
        String detail = getStringSafely(bundle, key + "_detail", summary);
        FacesMessage message = new FacesMessage(summary, detail);
        message.setSeverity(severity);
        return message;
    }

    /**
     * Add JSF info message.
     * @param msg info message string
     */
    public static void addFacesInformationMessage(String msg)
    {
        FacesContext context = FacesContext.getCurrentInstance();
        context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, null));
    }

    public static void addFacesWarningMessage(String msg)
    {
        FacesContext context = FacesContext.getCurrentInstance();
        context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, msg, null));
    }

    /**
     * Add JSF error message.
     * @param msg error message string
     */
    public static void addFacesErrorMessage(String msg)
    {
        FacesContext context = FacesContext.getCurrentInstance();
        context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, null));
    }

    /**
     * Add JSF error message for a specific attribute.
     * @param attrName name of attribute
     * @param msg error message string
     */
    public static void addFacesErrorMessage(String attrName, String msg)
    {
        FacesContext ctx = getFacesContext();
        FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, attrName, msg);
        ctx.addMessage(getRootViewComponentId(), fm);
    }

    // Informational getters

    /**
     * Get view id of the view root.
     * @return view id of the view root
     */
    public static String getRootViewId()
    {
        return getFacesContext().getViewRoot().getViewId();
    }

    /**
     * Get component id of the view root.
     * @return component id of the view root
     */
    public static String getRootViewComponentId()
    {
        return getFacesContext().getViewRoot().getId();
    }

    /**
     * Get FacesContext.
     * @return FacesContext
     */
    public static FacesContext getFacesContext()
    {
        FacesContext context = FacesContext.getCurrentInstance();
        return context;
    }

    public static void showPopup(String id)
    {
        RichPopup component = (RichPopup)findComponentInRoot(id);
        component.show(new RichPopup.PopupHints());
    }

    /**
     *Burasi facesContext nul geldigi durumlarda �alistirilacaktir.
     */
    private abstract static class InnerFacesContext extends FacesContext
    {
        protected static void setFacesContextAsCurrentInstance(final FacesContext facesContext)
        {
            FacesContext.setCurrentInstance(facesContext);
        }
    }

    public static FacesContext getFacesContext(HttpServletRequest request, HttpServletResponse response)
    {
        FacesContext context = FacesContext.getCurrentInstance();

        if (context == null)
        {
            FacesContextFactory contextFactory = (FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
            LifecycleFactory lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
            Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

            ServletContext servletContext = ((HttpServletRequest)request).getSession().getServletContext();
            context = contextFactory.getFacesContext(servletContext, request, response, lifecycle);

            JSFUtils.InnerFacesContext.setFacesContextAsCurrentInstance(context);

            //UIViewRoot view = context.getApplication().getViewHandler().createView(context, "gwtViewID");
            //context.setViewRoot(view);
        }
        return context;
    }


    /*
   * Internal method to pull out the correct local
   * message bundle
   */

    private static ResourceBundle getBundle()
    {
        FacesContext ctx = getFacesContext();
        ClassLoader ldr = Thread.currentThread().getContextClassLoader();
        return ResourceBundle.getBundle(ctx.getApplication().getMessageBundle(), getCurrentLocale(), ldr);
    }

    public static Locale getCurrentLocale()
    {
        return FacesContext.getCurrentInstance().getViewRoot().getLocale();
    }

    /**
     * Get an HTTP Request attribute.
     * @param name attribute name
     * @return attribute value
     */
    public static Object getRequestAttribute(String name)
    {
        return getFacesContext().getExternalContext().getRequestMap().get(name);
    }

    /**
     * Set an HTTP Request attribute.
     * @param name attribute name
     * @param value attribute value
     */
    public static void setRequestAttribute(String name, Object value)
    {
        getFacesContext().getExternalContext().getRequestMap().put(name, value);
    }

    /*
   * Internal method to proxy for resource keys that don't exist
   */

    private static String getStringSafely(ResourceBundle bundle, String key, String defaultValue)
    {
        String resource = null;
        try
        {
            resource = bundle.getString(key);
        }
        catch (MissingResourceException mrex)
        {
            if (defaultValue != null)
            {
                resource = defaultValue;
            } else
            {
                resource = NO_RESOURCE_FOUND + key;
            }
        }
        return resource;
    }

    /**
     * Locate an UIComponent in view root with its component id. Use a recursive way to achieve this.
     * @param id UIComponent id
     * @return UIComponent object
     */
    public static UIComponent findComponentInRoot(String id)
    {
        UIComponent component = null;
        FacesContext facesContext = getFacesContext();
        if (facesContext != null)
        {
            UIComponent root = facesContext.getViewRoot();
            component = findComponent(root, id);
        }
        return component;
    }

    /**
     * Locate an UIComponent from its root component.
     * Taken from http://www.jroller.com/page/mert?entry=how_to_find_a_uicomponent
     * @param base root Component (parent)
     * @param id UIComponent id
     * @return UIComponent object
     */
    public static UIComponent findComponent(UIComponent base, String id)
    {
        if (id.equals(base.getId()))
            return base;

        UIComponent children = null;
        UIComponent result = null;
        Iterator childrens = base.getFacetsAndChildren();
        while (childrens.hasNext() && (result == null))
        {
            children = (UIComponent)childrens.next();
            if (id.equals(children.getId()))
            {
                result = children;
                break;
            }
            result = findComponent(children, id);
            if (result != null)
            {
                break;
            }
        }
        return result;
    }

    /**
     * Method to create a redirect URL. The assumption is that the JSF servlet mapping is
     * "faces", which is the default
     *
     * @param view the JSP or JSPX page to redirect to
     * @return a URL to redirect to
     */
    public static String getPageURL(String view)
    {
        FacesContext facesContext = getFacesContext();
        ExternalContext externalContext = facesContext.getExternalContext();
        String url = ((HttpServletRequest)externalContext.getRequest()).getRequestURL().toString();
        StringBuffer newUrlBuffer = new StringBuffer();
        newUrlBuffer.append(url.substring(0, url.lastIndexOf("faces/")));
        newUrlBuffer.append("faces");
        String targetPageUrl = view.startsWith("/") ? view : "/" + view;
        newUrlBuffer.append(targetPageUrl);
        return newUrlBuffer.toString();
    }

    public static void resetValueInputItems(AdfFacesContext adfFacesContext, UIComponent component)
    {
        List<UIComponent> items = component.getChildren();
        for (UIComponent item : items)
        {
            if (item instanceof RichInputText)
            {
                RichInputText input = (RichInputText)item;
                System.out.println(input.getId());
                input.setValue("");
                input.resetValue();
                adfFacesContext.addPartialTarget(input);
            } else if (item instanceof RichOutputText)
            {
                //RichOutputText input = (RichOutputText)item;
                //input.setValue("");
                //adfFacesContext.addPartialTarget(input);

            } else if (item instanceof RichInputDate)
            {
                RichInputDate input = (RichInputDate)item;
                input.setValue(null);
                input.resetValue();
                adfFacesContext.addPartialTarget(input);
            } else if (item instanceof RichSelectOneChoice)
            {
                RichSelectOneChoice input = (RichSelectOneChoice)item;
                input.setValue(-1);
                input.resetValue();
                adfFacesContext.addPartialTarget(input);
            } else if (item instanceof RichGoLink)
            {
                RichGoLink input = (RichGoLink)item;
                input.setText("");
                adfFacesContext.addPartialTarget(input);
            } else if (item instanceof RichInputFile)
            {
                RichInputFile input = (RichInputFile)item;
                input.resetValue();
                adfFacesContext.addPartialTarget(input);
            } else if (item instanceof RichTextEditor)
            {
                RichTextEditor input = (RichTextEditor)item;
                input.resetValue();
                adfFacesContext.addPartialTarget(input);
            } else if (item instanceof RichSelectManyChoice)
            {
                RichSelectManyChoice input = (RichSelectManyChoice)item;
                input.setValue(null);
                input.resetValue();
                adfFacesContext.addPartialTarget(input);
            } else if (item instanceof RichTable)
            {
                RichTable input = (RichTable)item;
                input.setValue(null);
                adfFacesContext.addPartialTarget(input);
            }

            resetValueInputItems(adfFacesContext, item);

        }
    }

    public static final String showMessage(String messageText, FacesMessage.Severity severity)
    {
        FacesMessage fm = new FacesMessage(messageText);
        fm.setSeverity(severity);
        FacesContext context = FacesContext.getCurrentInstance();
        context.addMessage(null, fm);
        return null;
    }

    public static String getUserProfileThroughActiveSession()
    {
        ADFContext adfCtx = ADFContext.getCurrent();
        SecurityContext sContext = adfCtx.getSecurityContext();

        return sContext.getUserName();
    }

    public static UserProfile getUserProfileByUsername(String userName)
    {
        UserProfile userProfile = null;
        try
        {
            JpsContext jpsCtx = JpsContextFactory.getContextFactory().getContext();
            IdentityStoreService service = jpsCtx.getServiceInstance(IdentityStoreService.class);

            IdentityStore idStore = service.getIdmStore();
            User user = idStore.searchUser(userName);
            if (user != null)
            {
                userProfile = castToConvenientObject(user.getUserProfile());
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return userProfile;
    }

    private static UserProfile castToConvenientObject(oracle.security.idm.UserProfile userProfile)
    {
        UserProfile convenientUserProfile = null;
        try
        {
            convenientUserProfile = new UserProfile();
            convenientUserProfile.setDisplayName(userProfile.getDisplayName());
            convenientUserProfile.setUserName(userProfile.getUserName());
            convenientUserProfile.setUserID(userProfile.getUserID());
            convenientUserProfile.setFirstName(userProfile.getFirstName());
            convenientUserProfile.setMiddleName(userProfile.getMiddleName());
            convenientUserProfile.setLastName(userProfile.getLastName());
            convenientUserProfile.setManager(userProfile.getManager());
            convenientUserProfile.setUniqueName(userProfile.getUniqueName());
            convenientUserProfile.setTitle(userProfile.getTitle());
            convenientUserProfile.setDateofBirth(userProfile.getDateofBirth());
            convenientUserProfile.setUIAccessMode(userProfile.getUIAccessMode());
        }
        catch (IMException e)
        {
            e.printStackTrace();
        }
        return convenientUserProfile;
    }

    public static void updateEditableComponent(UIXEditableValue smc_testDepartmentSubject)
    {
        smc_testDepartmentSubject.resetValue();
        smc_testDepartmentSubject.updateModel(getFacesContext());
        getFacesContext().renderResponse();
    }

    public static UIComponent findActiveTabInPanelTabbed(String panelTabbedId)
    {
        UIComponent component = findComponentInRoot(panelTabbedId);
        if (!(component instanceof RichPanelTabbed))
            return null;

        UIComponent foundedComponent = null;
        UIComponent childPanelTabbedComponent = null;
        List<UIComponent> lstChildren = component.getChildren();
        for (UIComponent child : lstChildren)
        {
            if (child instanceof RichShowDetailItem)
            {
                if (((RichShowDetailItem)child).isDisclosed())
                {
                    if ((childPanelTabbedComponent = findChildPanelTabbedComponent(child.getId())) != null)
                    {
                        foundedComponent = findActiveTabInPanelTabbed(childPanelTabbedComponent.getId());
                        if (foundedComponent != null)
                            return foundedComponent;
                    } else
                        return child;
                }
            } else if (child instanceof RichPanelTabbed)
            {
                foundedComponent = findActiveTabInPanelTabbed(child.getId());
                if (foundedComponent != null)
                    return foundedComponent;
            }
        }
        return foundedComponent;
    }

    public static UIComponent findChildPanelTabbedComponent(String componentId)
    {
        UIComponent component = findComponentInRoot(componentId);

        List<UIComponent> lstChildren = component.getChildren();
        for (UIComponent child : lstChildren)
        {
            if (child instanceof RichPanelTabbed)
                return child;
            else if (!child.getChildren().isEmpty() && findChildPanelTabbedComponent(child.getId()) != null)
                return child;
        }
        return null;
    }
    
    public static void refresh()
    {
        FacesContext fctx = FacesContext.getCurrentInstance();
        fctx.renderResponse();
    }
}

