package telemetry

import (
	"sync"
	"time"
)

// Distribution is like a Sample, but instead of representing
// a single datapoint it represents a group of aggregated datapoints.
// The timestamp should be the time at which the first sample was
// ingested into the distribution.
//
// Many samples can be fed into a SampleAggregator,
// and the output is one Distribution per unique metric
//
// TODO: should this be converted to an interface w/ an implementation for
// more flexibility?
type Distribution struct {
	MetricID MetricID
	Unit     string

	RollupDimensions [][]string

	Timestamp time.Time

	SampleCount int32
	Maximum     float64
	Minimum     float64
	Sum         float64
	SEH1        *SEH1

	mux sync.Mutex // for atomically adding samples
}

// NewDistributionFromSample takes a sample and instantiates a distribution from it
func NewDistributionFromSample(s *Sample) *Distribution {
	dist := &Distribution{
		MetricID:         s.MetricID,
		Unit:             s.Unit,
		RollupDimensions: s.RollupDimensions,
		Timestamp:        s.Timestamp,

		SampleCount: int32(0),
		Maximum:     float64(0),
		Minimum:     float64(0),
		Sum:         float64(0),
		SEH1:        NewSEH1(),
	}
	// Add this sample to the distribution
	dist.AddSample(s)
	return dist
}

// AddSample Adds a sample to this distribution. Does not do any validation
// that the Metric in the sample matches the Metric in the Distribution!
func (d *Distribution) AddSample(sample *Sample) {
	d.mux.Lock()
	defer d.mux.Unlock()

	// Incorporate sample's value into this distribution
	if d.SampleCount == 0 || d.Minimum > sample.Value {
		d.Minimum = sample.Value
	}
	if d.SampleCount == 0 || d.Maximum < sample.Value {
		d.Maximum = sample.Value
	}
	d.SampleCount++
	d.Sum += sample.Value
	d.SEH1.Include(sample)
}
