package main // import "amuz.es/src/infra/cpu_ctrl"

import (
	"os"
	"os/signal"
	"syscall"
	"fmt"
	zlog "amuz.es/src/infra/goutils/logger/zap"
	"go.uber.org/zap/zapcore"
	"go.uber.org/multierr"
	"amuz.es/src/infra/goutils/logger/rotater"
	"amuz.es/src/infra/goutils/handler"
	"amuz.es/src/infra/cpu_ctrl/daemon"
	"amuz.es/src/infra/cpu_ctrl/producer"
	"amuz.es/src/infra/cpu_ctrl/consumer"
	"errors"
	"go.uber.org/zap"
)

func finalCloser() {
	if err := recover(); err != nil {
		fmt.Fprintln(os.Stderr, err.(error).Error())
		os.Exit(1)
	}
}

// 로그 초기화
func initLogger() func() {
	// 로깅설정
	formatter := zapcore.NewConsoleEncoder(zlog.LogCommonFormat)

	level := zap.InfoLevel
	if *verbose{
		level=zap.DebugLevel
	}
	// 전역 로거 초기화
	var err error
	logger, err = zlog.Init(
		true,
		formatter,
		name,
		"Stderr",
		"",
		nil,
		level,
	)

	if err != nil {
		panic(err)
	}
	// 로깅종료 및 exitcode 설정
	return func() {}
}

// 애플리케이션이 종료를 위해 대기하는 부분
func initContext(handler *handler.Handler) (func(), func()) {
	exitSignal := make(chan os.Signal, 1)
	// return waiter
	return func() {
		daemon.NotifyDaemon(daemon.DaemonStarted)
		// 시그널 처리
		signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1)
		for {
			select {
			case <-handler.Done():
				logger.Info("self destruct to close this application")
				return
			case initialErr := <-handler.Error():
				// 복구불가능한 에러(들) 모아서 넘겨주는 부분
				merged := initialErr
				logger.Error("main: ", initialErr)
				for {
					select {
					case anotherErr := <-handler.Error():
						merged = multierr.Append(merged, anotherErr)
						logger.Error("main: ", anotherErr)
					default:
						// panic을 발생시켜 컨텍스트를 에러를 전달한다.
						panic(merged)
					}
				}
			case sysSignal := <-exitSignal:
				//handle signal
				switch sysSignal {
				case syscall.SIGUSR1:
					rotater.Rotate()
				default:
					logger.Info(sysSignal.String(), " received")
					return
				}
			}
		}
	}, // return closer
		func() {
			daemon.NotifyDaemon(daemon.DaemonStopping)
			logger.Info("main: main context waiting..")
			// http 서버 기다린다.
			handler.GracefulWait()
			close(exitSignal)
		}
}

// 메인 웹서버 초기화
func initProcessor(handler *handler.Handler) func() {

	FanoutSpeed := func(sender <-chan producer.FanspeedInfo, receivers ...chan<- producer.FanspeedInfo) {
		defer func() {
			for _, receiver := range receivers {
				close(receiver)
			}
			if err := recover(); err != nil {
				handler.NotifyError(err.(error))
			}
		}()
		for speed := range sender {
			for _, receiver := range receivers {
				select {
				case receiver <- speed:
				default:
					logger.Warn("Some Fanspeed consumer blocked!")
				}
			}
		}
	}

	FanoutTempeture := func(sender <-chan producer.TempetureInfo, receivers ...chan<- producer.TempetureInfo) {
		defer func() {

			for _, receiver := range receivers {
				close(receiver)
			}
			if err := recover(); err != nil {
				handler.NotifyError(err.(error))
			}
		}()
		for tempeture := range sender {
			for _, receiver := range receivers {
				select {
				case receiver <- tempeture:
				default:
					logger.Warn("Some Tempeture consumer blocked!")
				}
			}
		}
	}

	processorCount := producer.GetProcessorCount()
	if processorCount == 0 {
		panic(errors.New("cpu not found!"))
	}

	tempetureInfoChan, fanspeedChan := producer.AggregateProcessorChannel(
		handler,
		*SampleInterval, processorCount,
		*P, *I, *D,
		*SetPoint,
	)

	fanController := consumer.NewFanControl(processorCount, *SampleInterval, handler)
	metricLogger := consumer.NewInfluxMetric((*InfluxHost).String(), processorCount, handler)


	handler.IncreaseWait()
	go fanController.StartControl()
	handler.IncreaseWait()
	go metricLogger.StartLogging()

	go FanoutTempeture(tempetureInfoChan, metricLogger.TempetureConsumer())
	go FanoutSpeed(fanspeedChan, fanController.Consumer(), metricLogger.FanSpeedConsumer())

	return func() {}
}
