package main

import (
	"context"
	"encoding/base64"
	"time"

	"github.com/urfave/cli/v3"

	"git.maze.io/go/ham/protocol/aprs/aprsis"
	"git.maze.io/go/ham/radio"
	"git.maze.io/ham/hamview"
	"git.maze.io/ham/hamview/cmd"
)

type aprsisConfig struct {
	Broker   hamview.BrokerConfig   `yaml:"broker"`
	Receiver hamview.APRSISConfig   `yaml:"receiver"`
	Radio    map[string]*radio.Info `yaml:"radio"`
	Include  []string               `yaml:"include"`
}

func (config *aprsisConfig) Includes() []string {
	includes := config.Include
	config.Include = nil
	return includes
}

func runAPRSIS(ctx context.Context, command *cli.Command) error {
	var config = aprsisConfig{
		Receiver: hamview.APRSISConfig{
			Listen: hamview.DefaultAPRSISListen,
			Server: hamview.DefaultAPRSISServer,
		},
	}
	if err := cmd.Load(logger, command.String(cmd.FlagConfig), &config); err != nil {
		return err
	}

	logger.Infof("receiver: starting APRS-IS proxy on tcp://%s to tcp://%s",
		config.Receiver.Listen,
		config.Receiver.Server)
	proxy, err := aprsis.NewProxy(config.Receiver.Listen, config.Receiver.Server)
	if err != nil {
		return err
	}

	proxy.OnClient = func(callsign string, client *aprsis.ProxyClient) {
		go receiveAPRSIS(&config.Broker, callsign, client, config.Radio[callsign])
	}

	return waitForInterrupt()
}

func receiveAPRSIS(config *hamview.BrokerConfig, callsign string, client *aprsis.ProxyClient, extra *radio.Info) {
	defer func() { _ = client.Close() }()

	if extra == nil {
		logger.Warnf("receiver: no radio info configured for %s!", callsign)
	}

	broker, err := hamview.NewBroker(config)
	if err != nil {
		logger.Errorf("receiver: can't setup to broker: %v", err)
		return
	}
	defer func() { _ = broker.Close() }()

	info := client.Info()
	if extra != nil {
		info.Manufacturer = pick(info.Manufacturer, extra.Manufacturer)
		info.Device = pick(info.Device, extra.Device)
		info.FirmwareDate = pickTime(info.FirmwareDate, extra.FirmwareDate)
		info.FirmwareVersion = pick(info.FirmwareVersion, extra.FirmwareVersion)
		info.Antenna = pick(info.Antenna, extra.Antenna)
		info.Modulation = pick(info.Modulation, extra.Modulation)
		info.Position = pickPosition(info.Position, extra.Position)
		info.Frequency = pickFloat64(info.Frequency, extra.Frequency)
		info.Bandwidth = pickFloat64(info.Bandwidth, extra.Bandwidth)
		info.Power = pickFloat64(info.Power, extra.Power)
		info.Gain = pickFloat64(info.Gain, extra.Gain)
		info.LoRaSF = pickUint8(info.LoRaSF, extra.LoRaSF)
		info.LoRaCR = pickUint8(info.LoRaCR, extra.LoRaCR)
	}

	if err = broker.StartRadio("aprs", info); err != nil {
		logger.Fatalf("receiver: can't start broker: %v", err)
		return
	}

	id := base64.RawURLEncoding.EncodeToString([]byte(callsign))

	logger.Infof("receiver: start receiving packets from station: %s", callsign)
	for packet := range client.RawPackets() {
		logger.Debugf("aprs packet: %#+v", packet)
		if err := broker.PublishPacket("aprs/packet/"+id, packet); err != nil {
			logger.Error(err)
		}
	}
	logger.Infof("receiver: stopped receiving packets from station: %s", callsign)
}

func pick(ss ...string) string {
	for _, s := range ss {
		if s != "" {
			return s
		}
	}
	return ""
}

func pickFloat64(vv ...float64) float64 {
	for _, v := range vv {
		if v != 0 {
			return v
		}
	}
	return 0
}

func pickPosition(vv ...*radio.Position) *radio.Position {
	for _, v := range vv {
		if v != nil {
			return v
		}
	}
	return nil
}

func pickTime(tt ...time.Time) time.Time {
	for _, t := range tt {
		if !t.Equal(time.Time{}) {
			return t
		}
	}
	return time.Time{}
}

func pickUint8(vv ...uint8) uint8 {
	for _, v := range vv {
		if v != 0 {
			return v
		}
	}
	return 0
}
