package org.exoplatform.platform.common.rest;

import org.exoplatform.application.gadget.Gadget;
import org.exoplatform.application.gadget.GadgetRegistryService;
import org.exoplatform.common.http.HTTPStatus;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.container.component.RequestLifeCycle;
import org.exoplatform.platform.common.navigation.NavigationUtils;
import org.exoplatform.portal.config.DataStorage;
import org.exoplatform.portal.config.model.Application;
import org.exoplatform.portal.config.model.*;
import org.exoplatform.portal.mop.description.DescriptionService;
import org.exoplatform.portal.mop.navigation.NavigationService;
import org.exoplatform.portal.pom.spi.portlet.Portlet;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.rest.resource.ResourceContainer;
import org.exoplatform.services.security.ConversationState;

import javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.*;
import java.net.URI;
import java.util.LinkedList;
import java.util.List;

/**
 * WS REST which permit to access to all user's dashboards
 * <p>
 * And for each dashboard, you can access to all gadgets installed
 * <p>
 * <ul>
 * <li>/dashboards to access to all dashboards</li>
 * <li>dashboards/{userName}/{dashboardName} to access to all gadgets from a dashboard</li>
 * </ul>
 * 
 * @author Clement
 *
 */
@Path(DashboardInformationRESTService.WS_ROOT_PATH)
public class DashboardInformationRESTService implements ResourceContainer {

  private static final Log LOG = ExoLogger.getLogger(DashboardInformationRESTService.class);

  protected final static String WS_ROOT_PATH = "/dashboards";
  protected final static String STANDALONE_ROOT_PATH = "/standalone";

  private final DataStorage dataStorageService;
  private final GadgetRegistryService gadgetRegistryService;
  
  private List<JsonGadgetInfo> gadgetsInfo;
    private NavigationService navigationService_;
    private DescriptionService descriptionService_;
    private PageNavigation navigation;

    public DashboardInformationRESTService(DataStorage dataStorageService, GadgetRegistryService gadgetRegistryService,DescriptionService descriptionService,NavigationService navigationService) {
   this.dataStorageService = dataStorageService;
    this.gadgetRegistryService = gadgetRegistryService;
      this.navigationService_= navigationService;
      this.descriptionService_= descriptionService;
  }


  /*=======================================================================
   * WS REST methods
   *======================================================================*/
  
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @RolesAllowed("users")
  @SuppressWarnings("unchecked")
  public Response getDashboards(@Context UriInfo uriInfo) {

      CacheControl cacheControl = new CacheControl();
      cacheControl.setNoCache(true);
      cacheControl.setNoStore(true);
      try {
          LinkedList<JsonDashboardInfo> list = new LinkedList<JsonDashboardInfo>();
          // Try to get all user nodes which corresponds to dashboards
          String userId = ConversationState.getCurrent().getIdentity().getUserId();
          //Loading User Navigation only
          navigation = NavigationUtils.loadPageNavigation(userId,navigationService_, descriptionService_);

      // --- There is at least one user navigation
      if (navigation != null) {
          //Get navigations
          List<NavigationFragment> fragments = navigation.getFragments() ;
          //Get dashboard tabs
          for (NavigationFragment frag:fragments) {
              List<PageNode> pagesNode=frag.getNodes();
              // Fetch all nodes to add dashboards to the final list
              String wsSubPath = "";
              String dashboardSubPath = "";
              URI wsURI = null;
              URI dashboardURI = null;
              for (PageNode pageNode:pagesNode){
                  Application<Portlet> appDashboard = (Application<Portlet>) extractDashboard(dataStorageService.getPage(pageNode.getPageReference()));
                  if(appDashboard == null) {
                    continue;
                  }
                  // Dashboard only into TransientApplication
                  if(appDashboard.getState() instanceof TransientApplicationState) {
                      JsonDashboardInfo info = new JsonDashboardInfo();
                      info.setId(pageNode.getName());
                      info.setLabel(pageNode.getLabel());
                      // Create URI to WS REST
                      wsSubPath = PortalContainer.getCurrentRestContextName() + "/private" + WS_ROOT_PATH + "/" + userId + "/" + getPageName(pageNode.getPageReference());
                      wsURI = uriInfo.getBaseUriBuilder().replaceMatrix(wsSubPath).build();
                      // Create URI to dashboard into portal
                      dashboardSubPath = PortalContainer.getCurrentPortalContainerName() + "/u/" + userId + "/" + pageNode.getName();
                      dashboardURI = uriInfo.getBaseUriBuilder().replaceMatrix(dashboardSubPath).build();

                      info.setLink(wsURI.toString());
                      info.setHtml(dashboardURI.toString());
                      list.add(info);
                  }
              }
          }
          if (LOG.isDebugEnabled()) {
              LOG.debug("Getting Dashboards Information");
          }
      }
      // Response to client
      return Response.ok(list, MediaType.APPLICATION_JSON).cacheControl(cacheControl).build();
    } catch (Exception e) {
        LOG.error("An error occurred while getting dashboards information.", e);
        return Response.status(HTTPStatus.INTERNAL_ERROR).cacheControl(cacheControl).build();
    }
  }
  
  @GET
  @Path("/{userName}/{dashboardName}")
  @Produces(MediaType.APPLICATION_JSON)
  @SuppressWarnings("unchecked")
  @RolesAllowed("users")
  public Response getGadgetInformation(@PathParam("userName") String userName, 
                                       @PathParam("dashboardName") String dashboardName,
                                       @Context UriInfo uriInfo) {
    
    // Initialize gadgetInfo list
    gadgetsInfo = new LinkedList<JsonGadgetInfo>();
    
    RequestLifeCycle.begin(PortalContainer.getInstance());

    CacheControl cacheControl = new CacheControl();
    cacheControl.setNoCache(true);
    cacheControl.setNoStore(true);
    try {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Getting Gadgets Information");
      }
      
      Page page = dataStorageService.getPage("user::" + userName + "::" + dashboardName);
      if(page != null && page.getChildren() != null && page.getChildren().size() > 0) {
        Application<Portlet> appDashboard = (Application<Portlet>) extractDashboard(page);
        
        if(appDashboard != null) { 
          // Get gadgets from dashboard and fill it to the list
          Dashboard dashboard = dataStorageService.loadDashboard(appDashboard.getStorageId());
          
          // Extract all gadgets from dashboard
          extractGadgets(dashboard);
          
          // For each url into jsonObjects, modify some informations
          int i = 0;
          String standaloneSubPath = "";
          URI standaloneURI = null;
          String iconSubPath = "";
          URI iconURI = null;
          for(JsonGadgetInfo info : gadgetsInfo) {
            standaloneSubPath = info.getGadgetUrl();
            standaloneURI = uriInfo.getBaseUriBuilder().replaceMatrix(standaloneSubPath).build();
            info.setGadgetUrl(standaloneURI.toString());

            // We change icon url only if storage url is not completed
            if(info.getGadgetIcon() != null && info.getGadgetIcon().length() > 0 && !info.getGadgetIcon().startsWith("http")) {
              iconSubPath = info.getGadgetIcon();
              iconURI = uriInfo.getBaseUriBuilder().replaceMatrix(iconSubPath).build();
              info.setGadgetIcon(iconURI.toString());
            }
            
            gadgetsInfo.set(i++, info);
          }
        }
      }
      
      // Response to client
      return Response.ok(gadgetsInfo, MediaType.APPLICATION_JSON).cacheControl(cacheControl).build();
    } 
    catch (Exception e) {
      LOG.error("An error occurred while getting dashboards information.", e);
      return Response.status(HTTPStatus.INTERNAL_ERROR).cacheControl(cacheControl).build();
    }
    finally {
      try {
        RequestLifeCycle.end();
      } catch (Exception e) {
        LOG.warn("An exception has occurred while proceed RequestLifeCycle.end() : " + e.getMessage());
      }
    }
  }
  
  

  /*=======================================================================
   * Private methods and classes
   *======================================================================*/

  /**
   * This recursive method extract all gadgets information from a container Tree to DTO
   * @throws Exception 
   */
  @SuppressWarnings({ "rawtypes", "unchecked" })
  private void extractGadgets(Container container) throws Exception {
    
    if(container != null) {
      List<ModelObject> children = container.getChildren();
      if(children != null) {
        for (Object child : children) {
          if (child instanceof Application) {
            Application application = (Application)child;
            if(ApplicationType.GADGET == application.getType()) {
              String gadgetName = dataStorageService.getId(application.getState());
              Gadget gadget = gadgetRegistryService.getGadget(gadgetName);
  
              if(gadget != null) {
                JsonGadgetInfo info = new JsonGadgetInfo();
                info.setGadgetName(gadget.getTitle());
                info.setGadgetUrl(PortalContainer.getCurrentPortalContainerName() + STANDALONE_ROOT_PATH + "/" + application.getStorageId());
                info.setGadgetIcon(gadget.getThumbnail());
                info.setGadgetDescription(gadget.getDescription());
                gadgetsInfo.add(info);
              }
              else {
                LOG.warn("Gadget with name " + gadgetName + " is no longer registered in Gadget Registry");
              }
            }
          }
          else if(child instanceof Container) {
            Container childContainer = (Container) child;
            extractGadgets(childContainer);
          }
        }
      }
    }
  }
  
  /**
   * This recursive method extract a dashboard from a container Tree
   * @throws Exception 
   */
  @SuppressWarnings("rawtypes")
  private ModelObject extractDashboard(Container container) throws Exception {
    
    if(container != null) {
      List<ModelObject> children = container.getChildren();
      for (Object child : children) {
        if (child instanceof Application) {
          Application application = (Application)child;
          if(ApplicationType.PORTLET == application.getType()) {
            return application;
          }
        }
        else if(child instanceof Container) {
          Container childContainer = (Container) child;
          return extractDashboard(childContainer);
        }
      }
    }
    return null;
  }


  /**
   * DTO Object used to create JSON response
   * <p>
   * Represents a Dashboard
   * 
   * @author Clement
   *
   */
  public static class JsonDashboardInfo {
    String id;
    String label;
    String html;
    String link;
    
    public String getId() {
      return id;
    }
    public void setId(String id) {
      this.id = id;
    }
    public String getLabel() {
      return label;
    }
    public void setLabel(String label) {
      this.label = label;
    }
    public String getHtml() {
      return html;
    }
    public void setHtml(String html) {
      this.html = html;
    }
    public String getLink() {
      return link;
    }
    public void setLink(String link) {
      this.link = link;
    }
  }

  /**
   * DTO Object used to create JSON response
   * <p>
   * Represents a Gadget
   * 
   * @author Clement
   *
   */
  public static class JsonGadgetInfo {
    String gadgetName;
    String gadgetUrl;
    String gadgetIcon;
    String gadgetDescription;
    
    public String getGadgetName() {
      return gadgetName;
    }
    public void setGadgetName(String name) {
      this.gadgetName = name;
    }
    public String getGadgetUrl() {
      return gadgetUrl;
    }
    public void setGadgetUrl(String gadgetUrl) {
      this.gadgetUrl = gadgetUrl;
    }
    public String getGadgetIcon() {
      return gadgetIcon;
    }
    public void setGadgetIcon(String gadgetIcon) {
      this.gadgetIcon = gadgetIcon;
    }
    public String getGadgetDescription() {
      return gadgetDescription;
    }
    public void setGadgetDescription(String gadgetDescription) {
      this.gadgetDescription = gadgetDescription;
    }
  }


  /**
   * Simple utility method to extract a page name from a page ref
   * @param pageRef
   * @return
   */
  private String getPageName(String pageRef) {
      String pageName = "";

      String[] refs = pageRef.split("::");
      pageName = refs[refs.length-1];

      return pageName;
  }
}
