package consumer

import (
	"fmt"
	"os/exec"
	"time"
	zlog "amuz.es/src/infra/goutils/logger/zap"
	"bytes"
	"amuz.es/src/infra/goutils/handler"
	"amuz.es/src/infra/cpu_ctrl/producer"
	"go.uber.org/zap"
)

var ()

type fanControl struct {
	processorCount   int
	handler          *handler.Handler
	fanSpeedConsumer chan producer.FanspeedInfo
	sampleDuration   time.Duration
	logger           *zap.SugaredLogger
}

type FanControl interface {
	Consumer() chan<- producer.FanspeedInfo
	StartControl()
}

func NewFanControl(processorCount int, sampleDuration time.Duration, handler *handler.Handler) FanControl {
	return &fanControl{
		processorCount:   processorCount,
		handler:          handler,
		fanSpeedConsumer: make(chan producer.FanspeedInfo, processorCount),
		sampleDuration:   sampleDuration,
		logger:           zlog.New(nil, "fanspeed"),
	}
}

func (c *fanControl) Consumer() chan<- producer.FanspeedInfo { return c.fanSpeedConsumer }

func (c *fanControl) StartControl() {
	defer c.handler.DecreaseWait()
	defer func() {
		if err := recover(); err != nil {
			c.handler.NotifyError(err.(error))
		}
	}()
	defer c.logger.Info("Fan control stopped")
	c.logger.Info("Fan control started")

	ticker := time.Tick(c.sampleDuration)
	pastFanSpeedList, newFanSpeedList := make([]int, c.processorCount), make([]int, c.processorCount)

	go func() {
		for changedFanspeed := range c.fanSpeedConsumer {
			newFanSpeedList[changedFanspeed.Id] = changedFanspeed.FanSpeed
		}
	}()

	for {
		select {
		case <-ticker:
			if !compareFanSpeed(pastFanSpeedList, newFanSpeedList) {
				copy(pastFanSpeedList, newFanSpeedList)
				go c.applyFanspeed(pastFanSpeedList)
			}
		case <-c.handler.Done():
			return
		}
	}
}
func (c *fanControl) applyFanspeed(newFanSpeedList []int) {
	args := make([]string, 0)
	args = append(args, "raw", "0x3a", "0x01", )
	for _, item := range newFanSpeedList {
		args = append(args, fmt.Sprintf("0x%x", item))
	}
	args = append(args,
		"0x0", "0x0", "0x0", "0x0", "0x0", "0x0",
	)
	cmd := exec.Command("ipmitool", args...)
	if err := cmd.Run(); err != nil {
		c.handler.NotifyError(err)
		return
	}
	buf := bytes.NewBufferString("")
	for _, item := range newFanSpeedList {
		buf.WriteString(fmt.Sprintf("0x%x", item))
		buf.WriteRune(' ')
	}
	c.logger.Infof("Commit fan speed with %s", buf.String())
}

func compareFanSpeed(old, new []int) bool {
	new = new[:len(old)] // this line is the key
	for i, v := range old {
		if v != new[i] { // here is no bounds checking for b[i]
			return false
		}
	}
	return true
}
