/*
 * RHQ Management Platform
 * Copyright (C) 2005-2008 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.rhq.enterprise.gui.legacy.action.resource.common.monitor.visibility;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.tiles.ComponentContext;
import org.apache.struts.tiles.actions.TilesAction;

import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.ui.MetricDisplaySummary;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.resource.composite.ResourceWithAvailability;
import org.rhq.core.domain.resource.group.composite.AutoGroupComposite;
import org.rhq.enterprise.gui.legacy.AttrConstants;
import org.rhq.enterprise.gui.legacy.MessageConstants;
import org.rhq.enterprise.gui.legacy.WebUser;
import org.rhq.enterprise.gui.legacy.util.RequestUtils;
import org.rhq.enterprise.gui.legacy.util.SessionUtils;
import org.rhq.enterprise.gui.uibeans.AutoGroupCompositeDisplaySummary;
import org.rhq.enterprise.gui.util.WebUtility;
import org.rhq.enterprise.server.measurement.MeasurementChartsManagerLocal;
import org.rhq.enterprise.server.measurement.MeasurementDataManagerLocal;
import org.rhq.enterprise.server.measurement.MeasurementException;
import org.rhq.enterprise.server.measurement.MeasurementPreferences;
import org.rhq.enterprise.server.measurement.MeasurementPreferences.MetricRangePreferences;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal;
import org.rhq.enterprise.server.resource.ResourceTypeNotFoundException;
import org.rhq.enterprise.server.util.LookupUtil;

/**
 * Fetch the children resources for the server
 */
public class ListChildrenAction extends TilesAction {
    protected static final Log log = LogFactory.getLog(ListChildrenAction.class);
    MeasurementDataManagerLocal dataManager;
    MeasurementChartsManagerLocal chartsManager;

    @SuppressWarnings("unchecked")
    @Override
    public ActionForward execute(ComponentContext context, ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception {

        dataManager = LookupUtil.getMeasurementDataManager();
        chartsManager = LookupUtil.getMeasurementChartsManager();

        WebUser user = SessionUtils.getWebUser(request.getSession());
        Subject subject = user.getSubject();
        Resource resource = (Resource) request.getAttribute(AttrConstants.RESOURCE_ATTR);

        // Get metric time range
        MeasurementPreferences preferences = user.getMeasurementPreferences();
        MetricRangePreferences rangePreferences = preferences.getMetricRangePreferences();
        long begin = rangePreferences.begin;
        long end = rangePreferences.end;

        List<AutoGroupComposite> children;
        List<AutoGroupCompositeDisplaySummary> displaySummary;
        int parentId = -1;
        int resourceTypeId = -1;
        if (resource == null) {
            parentId = WebUtility.getOptionalIntRequestParameter(request, AttrConstants.AUTOGROUP_PARENT_ATTR, -1);
            if (parentId > 0) // get data for individual autogroup children
            {
                resourceTypeId = WebUtility.getOptionalIntRequestParameter(request, AttrConstants.AUTOGROUP_TYPE_ATTR,
                    -1);
                children = getAutoGroupChildren(subject, parentId, resourceTypeId);
                if (children == null)
                    children = new ArrayList<AutoGroupComposite>();
                displaySummary = new ArrayList<AutoGroupCompositeDisplaySummary>(children.size() + 1);

                // get the parent
                ResourceManagerLocal resourceManager = LookupUtil.getResourceManager();
                AutoGroupComposite parentComposite = resourceManager.getResourceAutoGroup(subject, parentId);
                if (parentComposite != null) {
                    parentComposite.setMainResource(true);
                    List<MetricDisplaySummary> metricSummaries = null;
                    metricSummaries = chartsManager.getMetricDisplaySummariesForMetrics(subject, parentId,
                        DataType.MEASUREMENT, begin, end, true, true);
                    displaySummary.add(0, new AutoGroupCompositeDisplaySummary(parentComposite, metricSummaries));
                }
                /* We have n children with each child representing exactly 1 resource.
                 * As we are in an autogroup, all children have the same type, so we can just feed them to the backend
                 * in one call with all n resources.
                 */

                // Loop over children, get resources, call some ..forMultiMetrics..
                List<Integer> resourceIds = new ArrayList<Integer>();
                for (AutoGroupComposite child : children) {
                    List resources = child.getResources();
                    ResourceWithAvailability rwa = (ResourceWithAvailability) resources.get(0);
                    resourceIds.add(rwa.getResource().getId());
                }

                // Map<ResourceId, List<Summaries for that resource>
                Map<Integer, List<MetricDisplaySummary>> summaries = dataManager
                    .findNarrowedMetricDisplaySummariesForResourcesAndParent(subject, resourceTypeId, parentId,
                        resourceIds, begin, end);
                for (AutoGroupComposite child : children) {
                    if (parentComposite != null)
                        child.increaseDepth(1);

                    List resources = child.getResources();
                    ResourceWithAvailability rwa = (ResourceWithAvailability) resources.get(0);
                    List<MetricDisplaySummary> sumList = summaries.get(rwa.getResource().getId());
                    displaySummary.add(new AutoGroupCompositeDisplaySummary(child, sumList));
                }

            } else {
                RequestUtils.setError(request, MessageConstants.ERR_RESOURCE_NOT_FOUND);
                return null;
            }
        } else { // resource != null
            // get children of that single resource, which can be individual resources or autogroups
            children = getResourceChildren(resource, subject);

            displaySummary = new ArrayList<AutoGroupCompositeDisplaySummary>(children.size());
            for (AutoGroupComposite child : children) {
                List<MetricDisplaySummary> metrics = getMetrics(resource, child, subject, begin, end);

                displaySummary.add(new AutoGroupCompositeDisplaySummary(child, metrics));
            }
        }

        context.putAttribute(AttrConstants.CTX_SUMMARIES, displaySummary);

        return null;
    }

    public List<MetricDisplaySummary> getMetrics(Resource parentResource, AutoGroupComposite resourceGroupComposite,
        Subject subject, long beginTime, long endTime) throws MeasurementException {
        if (log.isTraceEnabled()) {
            log.trace("finding metric summaries for resourceType ["
                + resourceGroupComposite.getResourceType().getName());
        }

        List<MetricDisplaySummary> metricSummaries = null;
        // TODO GH: Why are we only getting the first one? --> single resource case
        //

        List resources = resourceGroupComposite.getResources();
        if ((resources != null) && (resources.size() == 1)) {
            ResourceWithAvailability resource = (ResourceWithAvailability) resources.get(0);

            metricSummaries = chartsManager.getMetricDisplaySummariesForMetrics(subject,
                resource.getResource().getId(), DataType.MEASUREMENT, beginTime, endTime, true, true);
        } else if ((resources != null) && (resources.size() > 1)) {
            List<Resource> res = new ArrayList<Resource>();
            for (Object o : resources) {
                if (o instanceof ResourceWithAvailability) {
                    ResourceWithAvailability rwa = (ResourceWithAvailability) o;
                    res.add(rwa.getResource());
                }
            }

            Map<Integer, List<MetricDisplaySummary>> sumMap = dataManager
                .findNarrowedMetricDisplaySummariesForCompatibleResources(subject, res, beginTime, endTime);
            metricSummaries = sumMap.values().iterator().next();

            // fill in some data that does not come from the backend
            for (MetricDisplaySummary tmp : metricSummaries) {
                tmp.setParentId(parentResource.getId());
                tmp.setChildTypeId(resourceGroupComposite.getResourceType().getId());
            }
        }

        return metricSummaries;
    }

    private List<AutoGroupComposite> getResourceChildren(Resource resource, Subject subject) {
        ResourceManagerLocal resourceManager = LookupUtil.getResourceManager();
        List<AutoGroupComposite> children = resourceManager.findChildrenAutoGroups(subject, resource.getId());

        AutoGroupComposite resourceGroupComposite = resourceManager.getResourceAutoGroup(subject, resource.getId());
        if (resourceGroupComposite != null)
            resourceGroupComposite.setMainResource(true);
        else
            return new ArrayList<AutoGroupComposite>();

        // now increase everyone's depth by one to account for the resource
        for (AutoGroupComposite child : children) {
            child.setDepth(child.getDepth() + 1);
        }

        children.add(0, resourceGroupComposite);

        Resource parentResource = resourceManager.getParentResource(resource.getId());
        AutoGroupComposite parentGroupComposite = null;
        if (parentResource != null) {
            parentGroupComposite = resourceManager.getResourceAutoGroup(subject, parentResource.getId());
        }

        if (parentGroupComposite != null) {
            // now increase everyone's depth by one to account for the parent
            for (AutoGroupComposite child : children) {
                child.increaseDepth(1);
            }

            children.add(0, parentGroupComposite);
        }

        return children;
    }

    /**
     * Get a list of the individual 'child resources' of that autogroup.
     */
    private List<AutoGroupComposite> getAutoGroupChildren(Subject subject, int parentId, int resourceTypeId) {
        List<AutoGroupComposite> children = new ArrayList<AutoGroupComposite>();
        ResourceManagerLocal resourceManager = LookupUtil.getResourceManager();
        ResourceTypeManagerLocal resourceTypeMananger = LookupUtil.getResourceTypeManager();
        Resource parentResource = resourceManager.getResourceById(subject, parentId);
        ResourceType resourceType = null;
        try {
            resourceType = resourceTypeMananger.getResourceTypeById(subject, resourceTypeId);
        } catch (ResourceTypeNotFoundException e) {
            return children; // empty list if we don't know the child type
        }

        if ((resourceType != null) && (parentResource != null)) {

            // first get the resources in the autogroup
            List<ResourceWithAvailability> resourcesForAutoGroup = resourceManager.findResourcesByParentAndType(subject,
                parentResource, resourceType);

            int i = 0;
            int[] resourceIds = new int[resourcesForAutoGroup.size()];
            for (ResourceWithAvailability resourceInAutoGroup : resourcesForAutoGroup) {
                int id = resourceInAutoGroup.getResource().getId();
                resourceIds[i++] = id;
            }

            // And then the composite to return
            List<AutoGroupComposite> composites = resourceManager.findResourcesAutoGroups(subject, resourceIds);

            return composites;
        }

        return children; // empty
    }
}