class MetricUtils {

    protected static GetMetricDataRequest makeGetMetricDataRequest(ReadRecordsRequest readRecordsRequest)
    {
        Split split = readRecordsRequest.getSplit();
        String serializedMetricStats = split.getProperty(MetricStatSerDe.SERIALIZED_METRIC_STATS_FIELD_NAME);
        List<MetricStat> metricStats = MetricStatSerDe.deserialize(serializedMetricStats);
        GetMetricDataRequest dataRequest = new GetMetricDataRequest();
        com.amazonaws.services.cloudwatch.model.Metric metric = new com.amazonaws.services.cloudwatch.model.Metric();
        metric.setNamespace(split.getProperty(NAMESPACE_FIELD));
        metric.setMetricName(split.getProperty(METRIC_NAME_FIELD));

        List<MetricDataQuery> metricDataQueries = new ArrayList<>();
        int metricId = 1;
        for (MetricStat nextMetricStat : metricStats) {
            metricDataQueries.add(new MetricDataQuery().withMetricStat(nextMetricStat).withId("m" + metricId++));
        }

        dataRequest.withMetricDataQueries(metricDataQueries);

        ValueSet timeConstraint = readRecordsRequest.getConstraints().getSummary().get(TIMESTAMP_FIELD);
        if (timeConstraint instanceof SortedRangeSet && !timeConstraint.isNullAllowed()) {
            //SortedRangeSet is how >, <, between is represented which are easiest and most common when
            //searching logs so we attempt to push that down here as an optimization. SQL can represent complex
            //overlapping ranges which Cloudwatch can not support so this is not a replacement for applying
            //constraints using the ConstraintEvaluator.

            Range basicPredicate = ((SortedRangeSet) timeConstraint).getSpan();

            if (!basicPredicate.getLow().isNullValue()) {
                Long lowerBound = (Long) basicPredicate.getLow().getValue();
                //TODO: confirm timezone handling
                logger.info("makeGetMetricsRequest: with startTime " + (lowerBound * 1000) + " " + new Date(lowerBound * 1000));
                dataRequest.withStartTime(new Date(lowerBound * 1000));
            }
            else {
                //TODO: confirm timezone handling
                dataRequest.withStartTime(new Date(0));
            }

            if (!basicPredicate.getHigh().isNullValue()) {
                Long upperBound = (Long) basicPredicate.getHigh().getValue();
                //TODO: confirm timezone handling
                logger.info("makeGetMetricsRequest: with endTime " + (upperBound * 1000) + " " + new Date(upperBound * 1000));
                dataRequest.withEndTime(new Date(upperBound * 1000));
            }
            else {
                //TODO: confirm timezone handling
                dataRequest.withEndTime(new Date(System.currentTimeMillis()));
            }
        }
        else {
            //TODO: confirm timezone handling
            dataRequest.withStartTime(new Date(0));
            dataRequest.withEndTime(new Date(System.currentTimeMillis()));
        }

        return dataRequest;
    }

    private  MetricUtils();

}