package main

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"

	"github.com/spf13/cobra"
)

// Version and BuildTime are injected via ldflags at build time.
var Version = "unknown"
var BuildTime = "unknown"

func parseHex(s string, bits int) (uint64, error) {
	return strconv.ParseUint(s, 0, bits)
}

type config struct {
	vendor       string
	product      string
	device       string
	vendorID     uint64
	productID    uint64
	deviceNumber uint32
}

func newConfig() *config {
	return &config{}
}

func main() {
	rootCmd := &cobra.Command{
		Use:   "bridge",
		Short: "ANT+ to Bluetooth LE bridge",
		Long: `Bridge an ANT+ cycling power trainer over Bluetooth Low Energy.
This allows these trainers to be used with modern devices that don't support ANT+.`,
		Version: fmt.Sprintf("%s (built %s)", Version, BuildTime),
		RunE: func(cmd *cobra.Command, args []string) error {
			c := newConfig()

			c.vendor = cmd.Flags().Lookup("vendor").Value.String()
			c.product = cmd.Flags().Lookup("product").Value.String()
			c.device = cmd.Flags().Lookup("device").Value.String()

			vendorID, err := parseHex(c.vendor, 16)
			if err != nil {
				return fmt.Errorf("invalid --vendor %q: %w", c.vendor, err)
			}
			productID, err := parseHex(c.product, 16)
			if err != nil {
				return fmt.Errorf("invalid --product %q: %w", c.product, err)
			}
			deviceNumber, err := parseHex(c.device, 32)
			if err != nil {
				return fmt.Errorf("invalid --device %q: %w", c.device, err)
			}

			c.vendorID = vendorID
			c.productID = productID
			c.deviceNumber = uint32(deviceNumber)

			logMsgf("ANT Bridge %s built %s", Version, BuildTime)
			logMsgf("vendor: 0x%04x/%d", vendorID, vendorID)
			logMsgf("product: 0x%04x/%d", productID, productID)
			logMsgf("device: 0x%03x/%d", deviceNumber, deviceNumber)

			if err := exec.Command("hciconfig", "hci0", "down").Run(); err != nil {
				logPrintf("hciconfig down failed: %v\n", err)
			}
			if err := exec.Command("hciconfig", "hci0", "up").Run(); err != nil {
				logPrintf("hciconfig up failed: %v\n", err)
			}

			events := make(chan Event, 1)
			go ListenForTrainer(uint16(c.vendorID), uint16(c.productID), c.deviceNumber, events)
			ExposeBluetooth(events)

			return nil
		},
	}

	rootCmd.Flags().String("vendor", "0x0fcf", "USB vendor ID (hex, e.g. 0x0fcf)")
	rootCmd.Flags().String("product", "0x1008", "USB product ID (hex, e.g. 0x1008)")
	rootCmd.Flags().String("device", "3001", "ANT+ device number (hex or decimal, e.g. 3001 or 0xbb9)")
	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}
