/**
 * Copyright (C) 2003-2007 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 */
package org.exoplatform.social.webui;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.exoplatform.commons.utils.ExpressionUtil;
import org.exoplatform.commons.utils.PropertyManager;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.portal.application.PortalRequestContext;
import org.exoplatform.portal.application.RequestNavigationData;
import org.exoplatform.portal.config.UserPortalConfigService;
import org.exoplatform.portal.mop.State;
import org.exoplatform.portal.mop.SiteType;
import org.exoplatform.portal.mop.description.DescriptionService;
import org.exoplatform.portal.mop.user.UserNavigation;
import org.exoplatform.portal.mop.user.UserNode;
import org.exoplatform.portal.webui.util.Util;
import org.exoplatform.portal.webui.workspace.UIPortalApplication;
import org.exoplatform.portal.webui.workspace.UIWorkingWorkspace;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.social.common.router.ExoRouter;
import org.exoplatform.social.common.router.ExoRouter.Route;
import org.exoplatform.social.core.binding.spi.GroupSpaceBindingService;
import org.exoplatform.social.core.identity.model.Identity;
import org.exoplatform.social.core.identity.model.Profile;
import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
import org.exoplatform.social.core.identity.provider.SpaceIdentityProvider;
import org.exoplatform.social.core.manager.ActivityManager;
import org.exoplatform.social.core.manager.IdentityManager;
import org.exoplatform.social.core.manager.RelationshipManager;
import org.exoplatform.social.core.relationship.model.Relationship;
import org.exoplatform.social.core.space.SpaceUtils;
import org.exoplatform.social.core.space.model.Space;
import org.exoplatform.social.core.space.spi.SpaceService;
import org.exoplatform.web.application.JavascriptManager;
import org.exoplatform.web.application.RequestContext;
import org.exoplatform.web.controller.QualifiedName;
import org.exoplatform.web.url.navigation.NavigationResource;
import org.exoplatform.web.url.navigation.NodeURL;
import org.exoplatform.webui.application.WebuiRequestContext;
import org.exoplatform.webui.application.portlet.PortletRequestContext;
import org.json.JSONObject;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpUtils;
import java.util.*;

/**
 * Contains some common methods for using as utility.<br>
 *
 */
public class Utils {
  /** . */
  public static final String ACTIVITY_STREAM_TAB_SELECTED_COOKIED = "exo_social_activity_stream_tab_selected_%s";
  public static final String ACTIVITY_STREAM_VISITED_PREFIX_COOKIED = "exo_social_activity_stream_%s_visited_%s_%s";
  public static final String LAST_UPDATED_ACTIVITIES_NUM = "exo_social_last_updated_activities_num_on_%s_of_%s";
  public static final String FROM = "from";
  public static final String OLD_FROM = "old_from";
  public static final String TO = "to";
  private static final String   HOME = "home";
  
  /** . */
  public static final String NOT_SEEN_ACTIVITIES_COOKIES = "exo_social_not_seen_activities_%s";
  public static final String SEEN_ACTIVITIES_COOKIES = "exo_social_seen_activities_%s";
  
  private static Log             LOG = ExoLogger.getLogger(Utils.class);
  
  /**
   * Gets remote id of owner user (depends on URL: .../remoteId). If owner user is null, return viewer remote id
   *
   * @return remoteId of owner user
   * @since 1.2.0 GA
   */
  public static String getOwnerRemoteId() {
    String currentUserName = URLUtils.getCurrentUser(); 
    if (currentUserName != null && currentUserName.length() > 0) {
      Identity identity = getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, currentUserName, false);
      //if the user's deleted
      if (identity.isDeleted()) {
        currentUserName = null;
      }
    }
    
    //
    if (currentUserName == null || currentUserName.equals("")) {
      return getViewerRemoteId();
    }
    return currentUserName;
  }

  /**
   * Gets remote id of viewer user.
   *
   * @return remote id
   * @since 1.2.0 GA
   */
  public static String getViewerRemoteId() {
    return RequestContext.getCurrentInstance().getRemoteUser();
  }

  /**
   * Checks if the owner user is the viewer user.
   * 
   * @return true if the viewer user is the same as owner user.
   * @since 1.2.0 GA
   */
  public static boolean isOwner() {
    return Utils.getViewerRemoteId().equals(Utils.getOwnerRemoteId());
  }

  /**
   * Gets identity of owner user.
   *
   * @param loadProfile
   * @return identity
   * @since 1.2.0 GA
   */
  public static Identity getOwnerIdentity(boolean loadProfile) {
    return getUserIdentity(getOwnerRemoteId(), loadProfile);
  }

  /**
   * Gets identity of viewer user (logged-in user).
   *
   * @param loadProfile
   * @return identity
   * @since 1.2.0 GA
   */
  public static Identity getViewerIdentity(boolean loadProfile) {
    return getUserIdentity(getViewerRemoteId(), loadProfile);
  }

  /**
   * Gets identity of owner user. Do not load profile.
   *
   * @return identity
   * @since 1.2.0 GA
   */
  public static Identity getOwnerIdentity() {
    return getUserIdentity(getOwnerRemoteId(), false);
  }

  public static String getOwnerIdentityId() {
    Identity identity = getOwnerIdentity();
    return identity == null ? null : identity.getId();
  }

  /**
   * Gets identity of viewer user (logged-in user). Do not load profile.
   *
   * @return identity
   * @since 1.2.0 GA
   */
  public static Identity getViewerIdentity() {
    return getUserIdentity(getViewerRemoteId(), false);
  }

  public static TimeZone getViewerTimezone() {
    Identity userIdentity = getUserIdentity(getViewerRemoteId(), false);
    if (userIdentity == null || userIdentity.getProfile().getTimeZone() == null) {
      return TimeZone.getDefault();
    } else {
      String timeZoneId = userIdentity.getProfile().getTimeZone();
      return TimeZone.getTimeZone(timeZoneId);
    }
  }

  public static int getViewerTimezoneDSTSavings() {
    Identity userIdentity = getUserIdentity(getViewerRemoteId(), false);
    if (userIdentity == null || userIdentity.getProfile().getTimeZoneDSTSavings() == null) {
      return TimeZone.getDefault().getDSTSavings();
    } else {
      return userIdentity.getProfile().getTimeZoneDSTSavings();
    }
  }

  public static String getViewerIdentityId() {
    Identity identity = getViewerIdentity();
    return identity == null ? null : identity.getId();
  }

  /**
   * Gets identity from the remote id (user name)
   * 
   * @param userName
   * @param loadProfile
   * @return identity
   * @since 1.2.0 GA
   */
  public static Identity getUserIdentity(String userName, boolean loadProfile) {
    return Utils.getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, userName, loadProfile);
  }
  
  /**
   * Check if an user is enable/disable from the remote id (user name)
   * 
   * @param userName
   * @return true if user is enable else return false
   */
  public static boolean isEnableUser(String userName) {
    return getUserIdentity(userName, false).isEnable();
  }
  
  /**
   * Get only users who are enable
   * 
   * @param users list of all users
   * @return list of enable users
   */
  public static List<String> getEnableUsers(String[] users) {
    if (users == null) {
      return new ArrayList<String>();
    }
    List<String> results = new ArrayList<String>();
    for (String user : users) {
      if (!Utils.isEnableUser(user)) {
        continue;
      }
      results.add(user);
    }
    return results;
  }

  /**
   * Gets identity from the remote id (space name)
   * 
   * @param spaceName
   * @param loadProfile
   * @return space identity
   * @since 4.1-RC1
   */
  public static Identity getSpaceIdentity(String spaceName, boolean loadProfile) {
    return Utils.getIdentityManager().getOrCreateIdentity(SpaceIdentityProvider.NAME, spaceName, loadProfile);
  }
  
  /**
   * Gets space identity of the owner space (from remote id)
   * 
   * @return space identity
   * @since 1.2.0 GA
   */
  public static Identity getOwnerSpaceIdentity() {
    return Utils.getIdentityManager().getOrCreateIdentity(SpaceIdentityProvider.NAME, getOwnerRemoteId(), true);
  }

  /**
   * Gets list of friends of owner user
   * 
   * @return list of friends
   * @throws Exception
   * @since 1.2.0 GA
   */
  public static List<Identity> getOwnerFriends() throws Exception {
    return Utils.getIdentityManager().getConnections(getOwnerIdentity());
  }

  /**
   * Gets list of friends of viewer user
   * 
   * @return list of friends
   * @throws Exception
   * @since 1.2.0 GA
   */
  public static List<Identity> getViewerFriends() throws Exception {
    return Utils.getIdentityManager().getConnections(getViewerIdentity());
  }

  /**
   * Updates working work space.
   *
   * @since 1.2.0 GA
   */
  public static void updateWorkingWorkSpace() {
    UIWorkingWorkspace uiWorkingWS = Util.getUIPortalApplication().getChildById(UIPortalApplication.UI_WORKING_WS_ID);
    PortalRequestContext pContext = Util.getPortalRequestContext();
    pContext.addUIComponentToUpdateByAjax(uiWorkingWS);
    pContext.ignoreAJAXUpdateOnPortlets(true);
  }

  /**
   * Gets activityManager
   * @return activityManager
   * @since 1.2.0 GA
   */
  public static final ActivityManager getActivityManager() {
    return (ActivityManager) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(ActivityManager.class);
  }

  /**
   * Gets identityManager
   * @return identityManager
   * @since 1.2.0 GA
   */
  public static final IdentityManager getIdentityManager() {
    return (IdentityManager) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(IdentityManager.class);
  }

  /**
   * Gets relationshipManager
   * @return relationshipManager
   * @since 1.2.0 GA
   */
  public static final RelationshipManager getRelationshipManager() {
    return (RelationshipManager) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(RelationshipManager.class);
  }
  
  /**
   * @param identity
   * @return
   * @throws Exception
   */
  public static final Relationship getRelationship(Identity identity) throws Exception {
    if (identity.equals(getViewerIdentity())) {
      return null;
    }
    return getRelationshipManager().get(identity, getViewerIdentity());
  }

  /**
   * Gets spaceService
   * @return spaceService
   * @since 1.2.0 GA
   */
  public static final SpaceService getSpaceService() {
    return (SpaceService) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(SpaceService.class);
  }
  
  /**
   * Gets ExoRouter
   * @return ExoRouter
   */
  public static final ExoRouter getExoRouter() {
    return (ExoRouter) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(ExoRouter.class);
  }

  /**
   * Get focused comment's id
   * @return
   */
  public static String getCommentID() {
    return getValueFromRequestParam("commentId");
  }
  
  /**
   * Get activity's id
   * @return
   */
  public static String getActivityID() {
    String uriActivity = getSelectedNode();
    if (uriActivity != null) {
      if ("activity".equals(uriActivity)) {
        return getValueFromRequestParam("id");
      } else {      
        if (uriActivity.indexOf("view_full_activity") >= 0 || uriActivity.indexOf("reply_activity") >= 0) {
          return uriActivity.split("/")[3];          
        }             
      }    
    }
    return null;
  }
  
  /**
   * Check the request param of url to verify if need focus to comment box
   * @return true if need focus to comment box
   */
  public static boolean isFocusCommentBox() {
    return ("1").equals(getValueFromRequestParam("comment"));
  }

  /**
   * Check the request param of url to verify if need focus to comment reply box
   * @return true if need focus to comment reply box
   */
  public static boolean isFocusCommentReplyBox() {
    return ("2").equals(getValueFromRequestParam("comment"));
  }
  
  /**
   * Check the request param of url to verify if need expand all likers
   * @return true if need expand all likers
   */
  public static boolean isExpandLikers() {
    return ("1").equals(getValueFromRequestParam("likes"));
  }
  
  public static String getValueFromRequestParam(String param) {
    PortalRequestContext request = Util.getPortalRequestContext();
    return request.getRequestParameter(param);
  }
  
  /**
   * Gets the header parameter
   * @param param
   * @return
   */
  public static String getHeader(String param) {
    PortalRequestContext request = Util.getPortalRequestContext();
    return request.getRequest().getHeader(param);
  }
  
  /**
   * Gets query value from Referer URI in request header.
   * @param param
   * @return
   */
  public static String getValueFromRefererURI(String param) {
    String refererURI = getHeader("referer");
    String value = null;
    if (refererURI != null) {
     
      int index = refererURI.indexOf("?");
      if (index != -1) {
        String query = refererURI.substring(index + 1);
        Map<String, String[]> map = HttpUtils.parseQueryString(query);
        value = map.containsKey(param) ? map.get(param)[0] : null;
        return value;
      }
    }
    return value;
  }
  
  /**
   * Display the feedback message inline instead of show on popup
   */
  public static void displayFeedbackMessageInline(String parentId) {
    try {
      WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
      ResourceBundle res = context.getApplicationResourceBundle();
      PortletRequestContext pContext = (PortletRequestContext) context;
      JavascriptManager jm = pContext.getJavascriptManager();
      String feedbackMessageType = getValueFromRequestParam("feedbackMessage");
      if (feedbackMessageType != null) {
        String message = res.getString("Notification.feedback.message." + feedbackMessageType);
        String userName = getValueFromRequestParam("userName");
        String spaceId = getValueFromRequestParam("spaceId");
        if (userName != null)
          message = message.replace("{0}", getUserIdentity(userName, false).getProfile().getFullName());
        if (spaceId != null)
          message = message.replace("{1}", getSpaceService().getSpaceById(spaceId).getDisplayName());
        message = message.replace("'", "${simpleQuote}");
        jm.require("SHARED/socialUtil", "socialUtil").addScripts("socialUtil.feedbackMessageInline('" + parentId + "','" + message + "');");
      }
    } catch (Exception e) {
      LOG.debug("Failed to init the feedback message");
    }
  }
  
  /**
   * Get the uri.
   * 
   * @param url
   * @return
   * @since 1.2.1
   */
  public static String getURI(String url) {
    RequestContext ctx = RequestContext.getCurrentInstance();
    NodeURL nodeURL =  ctx.createURL(NodeURL.TYPE);
    NavigationResource resource = new NavigationResource(SiteType.PORTAL, Util.getPortalRequestContext().getPortalOwner(), url);
    return nodeURL.setResource(resource).toString(); 
  }
  
  /**
   * Gets selected node name.
   * 
   * @return
   * @since 1.2.2
   */
  public static String getSelectedNode() {
    PortalRequestContext request = Util.getPortalRequestContext() ;
    return request.getControllerContext().getParameter(QualifiedName.parse("gtn:path"));
  }
  
  /**
   * Get the space url.
   * 
   * @param node
   * @return
   * @since 1.2.1
   */
  public static String getSpaceURL(UserNode node) {
    RequestContext ctx = RequestContext.getCurrentInstance();
    NodeURL nodeURL =  ctx.createURL(NodeURL.TYPE);
    return nodeURL.setNode(node).toString();
  }
  
  /**
   * Gets the space home url of a space.
   * 
   * @param space
   * @return
   * @since 1.2.1
   */
  public static String getSpaceHomeURL(Space space) {
    // work-around for SOC-2366 when rename existing space
    String groupId = space.getGroupId();
    String permanentSpaceName = groupId.split("/")[2];
    
    RequestContext ctx = RequestContext.getCurrentInstance();
    NodeURL nodeURL =  ctx.createURL(NodeURL.TYPE);
    NavigationResource resource = null;
    if (permanentSpaceName.equals(space.getPrettyName())) {
      //work-around for SOC-2366 when delete space after that create new space with the same name
      resource = new NavigationResource(SiteType.GROUP, SpaceUtils.SPACE_GROUP + "/"
                                        + permanentSpaceName, permanentSpaceName);
    } else {
      resource = new NavigationResource(SiteType.GROUP, SpaceUtils.SPACE_GROUP + "/"
                                        + permanentSpaceName, space.getPrettyName());
    }
    
    return nodeURL.setResource(resource).toString(); 
  }
  
  /**
   * 
   * @param value
   */
  public static void setCookies(String key, String value) {
    //
    removeCookie(key);

    //
    PortalRequestContext request = Util.getPortalRequestContext() ;
    Cookie cookie = new Cookie(key, value);
    cookie.setPath(request.getRequest().getContextPath());
    cookie.setMaxAge(Integer.MAX_VALUE);
    request.getResponse().addCookie(cookie);
  }
  
  private static Cookie[] removeCookie(String key) {
    PortalRequestContext request = Util.getPortalRequestContext();
    Cookie[] cookies = request.getRequest().getCookies();
    if (cookies != null) {
      int found = -1;
      for (int i = 0; i < cookies.length; i++) {
        if (key.equals(cookies[i].getName())) {
          found = i;
          break;
        }
      }
      if (found > -1) {
        ArrayUtils.remove(cookies, found);
      }
      
    }
    return cookies;
  }

  /**
   * 
   * @param key
   */
  public static String getCookies(String key) {
    PortalRequestContext request = Util.getPortalRequestContext() ;

    Cookie[] cookies = request.getRequest().getCookies();
    if (cookies != null) {
      for (Cookie cookie : cookies) {
        if (key.equals(cookie.getName())) {
          return cookie.getValue();
        }
      }
    }
    return null;
  }
  
  public static boolean hasCookies(String key) {
    return (getCookies(key) != null);
  }
  
  public static long getLastVisited(String key, String mode) {
    long currentVisited = Calendar.getInstance().getTimeInMillis();
    String cookieKey = String.format(Utils.ACTIVITY_STREAM_VISITED_PREFIX_COOKIED, mode, Utils.getViewerRemoteId(), key);
    String strValue = Utils.getCookies(cookieKey);
    if(strValue == null) {
      return currentVisited;
    }
    
    return Long.parseLong(strValue);
  }
  
  private static String getCookieValue(String key, String mode) {
    long currentVisited = Calendar.getInstance().getTimeInMillis();
    String cookieKey = String.format(Utils.ACTIVITY_STREAM_VISITED_PREFIX_COOKIED, mode, Utils.getViewerRemoteId(), key);
    String strValue = Utils.getCookies(cookieKey);
    if(strValue == null) {
      return "" + currentVisited;
    }
    
    return strValue;
  }
  
  
  public static void setLastVisited(String mode) {
    String gotTo = getCookieValue(TO, mode);
    String gotFrom = getCookieValue(FROM, mode);
    
    //
    setCookie(OLD_FROM, mode, gotFrom);
    
    //
    setCookie(FROM, mode, gotTo);
    
    //
    long nextTo = Calendar.getInstance().getTimeInMillis();
    setCookie(TO, mode, "" + nextTo);
  }
  
  private static void setCookie(String key, String mode, String value) {
    String cookieKey = String.format(Utils.ACTIVITY_STREAM_VISITED_PREFIX_COOKIED, mode, Utils.getViewerRemoteId(), key);
    Utils.setCookies(cookieKey, value);
  }
  
  public static String listToString(List<String> list, String separator) {
    StringBuilder sb = new StringBuilder();
    int lastIdx = 0;
    for (String s : list) {
        if ( s == null || s.length() == 0 ) {
          continue;
        }
        lastIdx += 1;
        sb.append(s);
        if ( lastIdx < list.size() ) {
          sb.append(separator);
        }
    }
    return sb.toString();
  }
  
  /**
   * Determines current displayed page is Home or not base on selected node.
   * 
   * @return
   */
  public static boolean isHomePage() {
    String selectedNode = Utils.getSelectedNode(); 
    return ( selectedNode == null || selectedNode.length() == 0 || HOME.equals(selectedNode));  
  }
  
  /**
   * Truncates large Strings showing a portion of the String's head and tail
   * with the center cut out and replaced with '...'. Also displays the total
   * length of the truncated string so size of '...' can be interpreted.
   * Useful for large strings in UIs or hex dumps to log files.
   * 
   * @param str
   *            the string to truncate
   * @param head
   *            the amount of the head to display
   * @param tail
   *            the amount of the tail to display
   * @return the center truncated string
   */
  public static final String centerTrunc( String str, int head, int tail ) {
      StringBuffer buf = null;

      // Return as-is if String is smaller than or equal to the head plus the
      // tail plus the number of characters added to the trunc representation
      // plus the number of digits in the string length.
      if ( str.length() <= ( head + tail + 7 + str.length() / 10 ) )
      {
          return str;
      }

      buf = new StringBuffer();
      buf.append( str.substring( 0, head ) ).append( "..." );
      buf.append( str.substring( str.length() - tail ) );
      return buf.toString();
  }
  
  /**
   * Truncates large Strings showing a portion of the String's head and tail
   * with the head cut out and replaced with '...'.
   * 
   * @param str
   *            the string to truncate
   * @param head
   *            the amount of the head to display
   * @return the head truncated string
   */
  public static final String trunc( String str, int head) {
      StringBuffer buf = null;

      // Return as-is if String is smaller than or equal to the head plus the
      // tail plus the number of characters added to the trunc representation
      // plus the number of digits in the string length.
      if ( str.length() <= ( head + 7 + str.length() / 10 ) )
      {
          return str;
      }

      buf = new StringBuffer();
      buf.append( str.substring( 0, head ) ).append( "..." );
      return buf.toString();
  }
  
  /**
   * Trim space characters at the beginning and end of string. Replace multiple spaces by a single space character.
   * 
   * @param str
   * @return
   * @since 4.0.0-RC1
   */
  public static String normalizeString(String str) {
    if(str != null) {
      return str.trim().replaceAll("(\\s){2,}"," ");
    }
    return null;
  }
  
  /**
   * Check whether is being in a space context or not.
   * 
   * @return
   * @since 4.0.0-RC2
   */
  public static boolean isSpaceContext() {
    return (getSpaceByContext() != null);
  }

  /**
   * Gets the space url based on the current context.
   * 
   * @return
   * @since 4.0.0-RC2
   */
  public static String getSpaceUrlByContext() {
    Space space = getSpaceByContext();
    return (space != null ? space.getUrl() : null);
  }
  
  /**
   * Initializes user profile popup.
   * 
   * @param uiActivityId Id of activity component.
   * @since 4.0.0-GA
   */
  public static void initUserProfilePopup(String uiActivityId) {
    PortletRequestContext pContext = (PortletRequestContext) WebuiRequestContext.getCurrentInstance();
    JavascriptManager jm = pContext.getJavascriptManager();
    jm.require("SHARED/social-ui-profile", "profile")
      .addScripts("profile.initUserProfilePopup('" + uiActivityId + "', null);");
  }
  
  /**
   * Clear user profile popup.
   * 
   * @since 4.1.x
   */
  public static void clearUserProfilePopup() {
    PortletRequestContext pContext = (PortletRequestContext) WebuiRequestContext.getCurrentInstance();
    JavascriptManager jm = pContext.getJavascriptManager();
    jm.require("SHARED/social-ui-profile", "profile")
      .addScripts("profile.clearUserProfilePopup();");
  }
  
  public static Space getSpaceByContext() {
    //
    SpaceService spaceService = (SpaceService) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(SpaceService.class);
    PortalRequestContext pcontext = Util.getPortalRequestContext();
    if (!pcontext.getSiteType().equals(SiteType.GROUP) ||
        !pcontext.getSiteName().startsWith(SpaceUtils.SPACE_GROUP)) {
      return null;
    }
    String requestPath = pcontext.getControllerContext().getParameter(RequestNavigationData.REQUEST_PATH);

    Route route = ExoRouter.route(requestPath);
    if (route == null) {
      String groupId = pcontext.getControllerContext().getParameter(RequestNavigationData.REQUEST_SITE_NAME);
      if(StringUtils.isNotBlank(groupId) && groupId.startsWith(SpaceUtils.SPACE_GROUP)) {
        return spaceService.getSpaceByGroupId(groupId);
      }
    }

    //
    String spacePrettyName = route.localArgs.get("spacePrettyName");
    Space space = null;
    if(StringUtils.isNotBlank(spacePrettyName) && !spacePrettyName.startsWith(";jsessionid=")) {
      space = spaceService.getSpaceByPrettyName(spacePrettyName);
      if (space == null) {
        String groupId = String.format("%s/%s", SpaceUtils.SPACE_GROUP, spacePrettyName);
        space = spaceService.getSpaceByGroupId(groupId); 
      }
    }
    
    return space;
  }
  
  /**
   * Clears cached user data on user profile popup.
   * 
   * @since 4.1.x
   */
  public static void clearCacheOnUserPopup() {
    WebuiRequestContext reqContext = WebuiRequestContext.getCurrentInstance();
    JavascriptManager jm = reqContext.getJavascriptManager();
    jm.require("SHARED/jquery", "jq")
      .addScripts("(function($) { $('#socialUsersData').data('CacheSearch', {}); })(jq);");
  }
  
  /**
   * Get Resource bundle. If failure, log it in developer mode
   * @param msgKey key to get resource bundle
   * @return Localized value for msgKey. null if value is not found 
   */
  public static String appRes (String msgKey) {
    WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
    try {
      return context.getApplicationResourceBundle().getString(msgKey);
    } catch (MissingResourceException ex) {
      if (PropertyManager.isDevelopping()) {
        LOG.warn("Can not find resource bundle for key : " + msgKey);
      }
      return null;
    }    
  }
  
  /**
   * Gets resolved application label by input user node.
   * 
   * @param userNode user node
   * @return label value of input user node. null be returned in case of node has no label. 
   * @since 4.1-RC1
   */
  
  public static String getResolvedAppLabel (UserNode userNode) {
    ExoContainer container = ExoContainerContext.getCurrentContainer();
    UserPortalConfigService userPortalConfigService = (UserPortalConfigService)
                                                  container.getComponentInstanceOfType(UserPortalConfigService.class);
    if (userNode ==  null) {
      return null;
    }
    
    String id = userNode.getId();
    String nodeLabel = userNode.getLabel();
    
    if (nodeLabel != null) {
      UserNavigation navigation = userNode.getNavigation();
      ResourceBundle bundle = navigation.getBundle();
      return ExpressionUtil.getExpressionValue(bundle, nodeLabel);
    } else if (id != null) {
      Locale userLocale =  Util.getPortalRequestContext().getLocale();
      Locale portalLocale = SpaceUtils.getUserPortal().getLocale();
      DescriptionService descriptionService = userPortalConfigService.getDescriptionService();
      State description = descriptionService.resolveDescription(id, portalLocale, userLocale);
      if (description != null) {
        return description.getName();
      }
    }
    
    return null;
  }

  /**
   * Initializes confirmation popup.
   *  
   * @param componentId Target component.
   * @throws Exception
   */
  public static void initConfirmationPopup(String componentId) throws Exception {
    WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
    ResourceBundle res = context.getApplicationResourceBundle();
    JSONObject object = new JSONObject();
    object.put("componentId", componentId);
    object.put("Caption", res.getString("Confirmation.label.Caption"));
    object.put("OK", res.getString("Confirmation.label.OK"));
    object.put("Cancel", res.getString("Confirmation.label.Cancel"));
    
    context.getJavascriptManager().getRequireJS().require("SHARED/socialUtil", "socialUtil")
           .addScripts("socialUtil.applyConfirmPopup(" + object.toString() + ");");
  }
  
  public static boolean isUserBoundToSpace(String username,String spaceId) {
    ExoContainer container = ExoContainerContext.getCurrentContainer();
    GroupSpaceBindingService groupSpaceBindingService = container.getComponentInstanceOfType(GroupSpaceBindingService.class);
    return groupSpaceBindingService.countUserBindings(spaceId,username)>0;
  }

  /**
   * Check if the user is external
   *
   * @param userIdentity
   * @return true if the user is external
   */
  public static boolean isExternal(Identity userIdentity) {
    return userIdentity.getProfile().getProperty(Profile.EXTERNAL) != null && (userIdentity.getProfile().getProperty(Profile.EXTERNAL)).equals("true");
  }
}
