package main

import (
	"fmt"
	"math"
	"os"
	"strconv"
	"strings"
	"time"
)

// adc is an abstract ADC-based analog input.
type adc interface {
	// Read returns the ADC value in volts.
	Read() (float32, error)
}

// bbADC implements adc using a BeagleBone's built-in ADC.
type bbADC struct {
	path string
}

// newBBADC returns a BeagleBone ADC for a given channel number.
func newBBADC(num int) (*bbADC, error) {
	path := fmt.Sprintf("/sys/bus/iio/devices/iio:device0/in_voltage%d_raw", num)
	if _, err := os.Stat(path); err != nil {
		return nil, fmt.Errorf("could not access: %w", err)
	}
	return &bbADC{
		path: path,
	}, nil
}

func (b *bbADC) Read() (float32, error) {
	by, err := os.ReadFile(b.path)
	if err != nil {
		return 0, err
	}
	d := strings.TrimSpace(string(by))
	v, err := strconv.ParseUint(d, 10, 64)
	if err != nil {
		return 0, err
	}
	// The ADC Vref/Vdd is at 1.8V and is 10-bit (0-4095).
	vadc := float32(v) * 1.8 / 4096.0
	// The ADC is connected through a resistor divider.
	r1 := float32(1000.0)
	r2 := float32(4698.0)
	vin := vadc / (r1 / (r1 + r2))
	return vin, nil
}

// fakeADC implements an adc that outputs a sine wave. This is used for testing.
type fakeADC struct {
}

func (b *fakeADC) Read() (float32, error) {
	t := float64(time.Now().UnixMilli()) / 1000
	v := (math.Sin(t/10)+1)*(6.5/2) + 2
	return float32(v), nil
}
