package com.custardsource.parfait.timing;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

public class InProgressSnapshot {
    private static final Logger LOG = Logger.getLogger(InProgressSnapshot.class);

    private final List<String> names = new ArrayList<String>();
    private final List<String> descriptions = new ArrayList<String>();
    private final List<Class<?>> classes = new ArrayList<Class<?>>();
    private final List<Map<String, Object>> values = Lists.newArrayList();

    private InProgressSnapshot(EventTimer timer, ThreadContext context) {
        names.add("Thread name");
        descriptions.add("Thread name");
        classes.add(String.class);

        names.add("Event");
        descriptions.add("Event");
        classes.add(String.class);

        for (ThreadMetric metric : timer.getMetricSuite().metrics()) {
            names.add(metric.getMetricName());
            descriptions.add(metric.getDescription());
            classes.add(Long.class);
        }

        Collection<String> contextKeys = (context == null) ? Collections.<String> emptyList()
                : context.allKeys();

        for (String contextEntry : contextKeys) {
            names.add(contextEntry);
            descriptions.add(contextEntry);
            classes.add(String.class);
        }

        for (Map.Entry<Thread, EventMetricCollector> entry : timer.getCollectorThreadMap()
                .entrySet()) {
            StepMeasurements m = entry.getValue().getInProgressMeasurements();
            String event = m.getBackTrace();
            Map<ThreadMetric, Long> snapshotValues = m.snapshotValues();
            Map<String, Object> keyedValues = new HashMap<String, Object>();
            keyedValues.put("Thread name", entry.getKey().getName());
            keyedValues.put("Event", event);
            for (ThreadMetric metric : timer.getMetricSuite().metrics()) {
                keyedValues.put(metric.getMetricName(), snapshotValues.get(metric));
                LOG.trace(String.format("Thread %s in event %s, metric %s has value %s", entry
                        .getKey(), event, metric, snapshotValues.get(metric)));
            }
            for (String contextEntry : contextKeys) {
                keyedValues.put(contextEntry, context.getForThread(entry.getKey(), contextEntry));
            }
            values.add(keyedValues);
        }

    }

    public int getColumnCount() {
        return names.size();
    }

    public List<Map<String, Object>> getValues() {
        return ImmutableList.copyOf(values);
    }

    public Collection<String> getColumnDescriptions() {
        return ImmutableList.copyOf(descriptions);
    }

    public Collection<String> getColumnNames() {
        return ImmutableList.copyOf(names);
    }

    public List<Class<?>> getColumnClasses() {
        return ImmutableList.copyOf(classes);
    }

    public static final Function<InProgressSnapshot, String> TO_STRING = new Function<InProgressSnapshot, String>() {
        @Override
        public String apply(InProgressSnapshot from) {
            StringBuilder result = new StringBuilder();

            for (String column : from.getColumnNames()) {
                result.append(StringUtils.leftPad(column, 20)).append("\t");
            }
            result.append("\n");
            for (Map<String, Object> rowData : from.getValues()) {
                for (String column : from.getColumnNames()) {
                    result.append(StringUtils.leftPad(String.valueOf(rowData.get(column)), 20))
                            .append("\t");
                }
                result.append("\n");

            }
            return result.toString();
        }
    };

    public static InProgressSnapshot capture(EventTimer timer, ThreadContext context) {
        return new InProgressSnapshot(timer, context);
    }

    public String asString() {
        return TO_STRING.apply(this);
    }
}
@@ -1814,24 +1814,90 @@
 urements();%0A
+            if (m == null) %7B%0A                break;%0A            %7D%0A
             

Fix bug where a thread, once 'used', would throw an exception if no events were in-progress on it