package main

import (
	"math"
	"time"
)

// momentaryOutput is an output that can be triggered for 500ms.
type momentaryOutput struct {
	// output of the block.
	output bool
	// scheduledOff is when the block should be outputting false again.
	scheduledOff time.Time
}

func (m *momentaryOutput) process() {
	m.output = m.scheduledOff.After(time.Now())
}

func (m *momentaryOutput) trigger() {
	m.scheduledOff = time.Now().Add(time.Millisecond * 500)
}

// thresholdOutput outputs true if a given value is above a setpoint/threshold.
// It contains debounce logic for processing noisy analog signals.
type thresholdOutput struct {
	// output of the block.
	output bool
	// debounce is when the debouncer should be inactive again.
	debounce time.Time
	// threshold is the setpoint of the block.
	threshold float64
	// hysteresis around the process setpoint (min/max is threshold +- hysteresis)
	hysteresis float64
}

func (t *thresholdOutput) process(value float64) {
	if time.Now().Before(t.debounce) {
		return
	}
	new := t.output
	if t.output {
		new = value > (t.threshold - t.hysteresis)
	} else {
		new = value > (t.threshold + t.hysteresis)
	}
	if new != t.output {
		t.output = new
		t.debounce = time.Now().Add(time.Second * 5)
	}
}

// ringbufferInput accumulates analog data up to limit samples, and calculates
// an average.
type ringbufferInput struct {
	data  []float32
	limit uint
	// avg is the mean average of the samples in data, or 0.0 if no data is
	// present yet. This is the main output of the block.
	avg float32
}

func (r *ringbufferInput) process(input float32) {
	// TODO(q3k): use actual ringbuffer
	// TODO(q3k): optimize average calculation
	// TODO(q3k): precalculate value in mbar
	r.data = append(r.data, input)
	trim := len(r.data) - int(r.limit)
	if trim > 0 {
		r.data = r.data[trim:]
	}
	avg := float32(0.0)
	for _, v := range r.data {
		avg += v
	}
	if len(r.data) != 0 {
		avg /= float32(len(r.data))
	}
	r.avg = avg
}

// saturated returns true if the number of samples is at the configured limit.
func (r *ringbufferInput) saturated() bool {
	return len(r.data) >= int(r.limit)
}

type pfeifferVoltsToMbar struct {
	mbar float32
}

func (p *pfeifferVoltsToMbar) process(volts float32) {
	// Per Pirani probe docs.
	bar := math.Pow(10.0, float64(volts)-8.5)
	p.mbar = float32(bar * 1000.0)
}
