package org.rhq.enterprise.gui.coregui.client.inventory.groups.detail.monitoring.table;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Set;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.types.ListGridFieldType;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;

import org.rhq.core.domain.criteria.Criteria;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.ui.MetricDisplaySummary;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.resource.group.composite.ResourceGroupComposite;
import org.rhq.enterprise.gui.coregui.client.CoreGUI;
import org.rhq.enterprise.gui.coregui.client.UserSessionManager;
import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table.MeasurementTableDataSource;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.type.ResourceTypeRepository;
import org.rhq.enterprise.gui.coregui.client.util.MeasurementUtility;
import org.rhq.enterprise.gui.coregui.client.util.preferences.MeasurementUserPreferences;
import org.rhq.enterprise.gui.coregui.client.util.preferences.UserPreferences;

/**
 * A simple data source to read in metric data summaries for a resource.
 * This doesn't support paging - everything is returned in one query. Since
 * the number of metrics per resource is relatively small (never more than tens of them),
 * we just load them all in at once.
 *
 * @author John Mazzitelli
 * @author Simeon PInder
 */
public class GroupMeasurementTableDataSource extends MeasurementTableDataSource {

    public static final String FIELD_MEMBERS_REPORTING = "membersReporting";

    private int groupId;
    private ResourceGroupComposite groupComposite;

    public GroupMeasurementTableDataSource(ResourceGroupComposite groupComposite, int groupId) {
        super(groupId);
        this.groupComposite = groupComposite;
        this.groupId = groupId;
    }

    /**
     * The view that contains the list grid which will display this datasource's data will call this
     * method to get the field information which is used to control the display of the data.
     *
     * @return list grid fields used to display the datasource data
     */
    public ArrayList<ListGridField> getListGridFields() {
        ArrayList<ListGridField> fields = new ArrayList<ListGridField>(6);

        ListGridField memberCountField = new ListGridField(FIELD_MEMBERS_REPORTING, MSG
            .common_title_members_reporting());
        memberCountField.setWidth("15%");
        fields.add(memberCountField);

        ListGridField nameField = new ListGridField(FIELD_METRIC_LABEL, MSG.common_title_name());
        //launching modal window, not normal link so javascript target set.
        nameField.setType(ListGridFieldType.LINK);
        nameField.setTarget("javascript");
        nameField.setWidth("30%");
        fields.add(nameField);

        ListGridField alertsField = new ListGridField(FIELD_ALERT_COUNT, MSG.common_title_alerts());
        alertsField.setWidth("10%");
        fields.add(alertsField);

        ListGridField minField = new ListGridField(FIELD_MIN_VALUE, MSG.view_resource_monitor_table_min());
        minField.setWidth("15%");
        fields.add(minField);

        ListGridField maxField = new ListGridField(FIELD_MAX_VALUE, MSG.view_resource_monitor_table_max());
        maxField.setWidth("15%");
        fields.add(maxField);

        ListGridField avgField = new ListGridField(FIELD_AVG_VALUE, MSG.view_resource_monitor_table_avg());
        avgField.setWidth("15%");
        fields.add(avgField);

        return fields;
    }

    @Override
    public ListGridRecord copyValues(MetricDisplaySummary from) {
        MeasurementUtility.formatSimpleMetrics(from);

        ListGridRecord record = new ListGridRecord();
        record.setAttribute(FIELD_METRIC_LABEL, from.getLabel());
        record.setAttribute(FIELD_ALERT_COUNT, String.valueOf(from.getAlertCount()));
        record.setAttribute(FIELD_MIN_VALUE, getMetricStringValue(from.getMinMetric()));
        record.setAttribute(FIELD_MAX_VALUE, getMetricStringValue(from.getMaxMetric()));
        record.setAttribute(FIELD_AVG_VALUE, getMetricStringValue(from.getAvgMetric()));
        record.setAttribute(FIELD_METRIC_DEF_ID, from.getDefinitionId());
        record.setAttribute(FIELD_METRIC_SCHED_ID, from.getScheduleId());
        record.setAttribute(FIELD_METRIC_UNITS, from.getUnits());
        record.setAttribute(FIELD_METRIC_NAME, from.getMetricName());
        record.setAttribute(FIELD_MEMBERS_REPORTING, from.getNumberCollecting());
        return record;
    }

    @Override
    protected void executeFetch(final DSRequest request, final DSResponse response, final Criteria unused) {
        final ResourceGroupComposite groupComposite = this.groupComposite;
        final ResourceGroup group = groupComposite.getResourceGroup();
        // Load the fully fetched ResourceType.
        ResourceType groupType = group.getResourceType();
        ResourceTypeRepository.Cache.getInstance().getResourceTypes(
            groupType.getId(),
            EnumSet.of(ResourceTypeRepository.MetadataType.content, ResourceTypeRepository.MetadataType.operations,
                ResourceTypeRepository.MetadataType.measurements, ResourceTypeRepository.MetadataType.events,
                ResourceTypeRepository.MetadataType.resourceConfigurationDefinition),
            new ResourceTypeRepository.TypeLoadedCallback() {
                public void onTypesLoaded(ResourceType type) {
                    group.setResourceType(type);
                    //metric definitions
                    Set<MeasurementDefinition> definitions = type.getMetricDefinitions();

                    //build id mapping for measurementDefinition instances Ex. Free Memory -> MeasurementDefinition[100071]
                    final HashMap<String, MeasurementDefinition> measurementDefMap = new HashMap<String, MeasurementDefinition>();
                    for (MeasurementDefinition definition : definitions) {
                        measurementDefMap.put(definition.getDisplayName(), definition);
                    }
                    //bundle definition ids for asynch call.
                    int[] definitionArrayIds = new int[definitions.size()];
                    final String[] displayOrder = new String[definitions.size()];
                    measurementDefMap.keySet().toArray(displayOrder);
                    //sort the charting data ex. Free Memory, Free Swap Space,..System Load
                    Arrays.sort(displayOrder);

                    //organize definitionArrayIds for ordered request on server.
                    int index = 0;
                    for (String definitionToDisplay : displayOrder) {
                        definitionArrayIds[index++] = measurementDefMap.get(definitionToDisplay).getId();
                    }

                    UserPreferences prefs = UserSessionManager.getUserPreferences();
                    MeasurementUserPreferences mprefs = new MeasurementUserPreferences(prefs);
                    ArrayList<Long> range = mprefs.getMetricRangePreferences().getBeginEndTimes();

                    //now retrieve metric display sumamries
                    GWTServiceLookup.getMeasurementChartsService().getMetricDisplaySummariesForCompatibleGroup(groupId,
                        definitionArrayIds, Long.valueOf(range.get(0)).longValue(),
                        Long.valueOf(range.get(1)).longValue(), false,
                        new AsyncCallback<ArrayList<MetricDisplaySummary>>() {
                            @Override
                            public void onSuccess(ArrayList<MetricDisplaySummary> result) {
                                ArrayList<MetricDisplaySummary> validSummaries = new ArrayList<MetricDisplaySummary>();
                                for (MetricDisplaySummary mds : result) {
                                    if (mds.getValuesPresent()) {//include only populated datapoints.
                                        validSummaries.add(mds);
                                    }
                                }
                                response.setData(buildRecords(validSummaries));
                                processResponse(request.getRequestId(), response);
                            }

                            @Override
                            public void onFailure(Throwable caught) {
                                CoreGUI.getErrorHandler().handleError("Cannot load metrics", caught);
                            }
                        });
                }
            });
    }
}
