package main

import (
	"fmt"
	"image"
	"image/color"
	"net"
	"time"

	"git.openpdu.org/OpenPDU/openpdu/i2c"
	"git.openpdu.org/OpenPDU/openpdu/outlet"
	"git.openpdu.org/OpenPDU/openpdu/syslog"
	"github.com/spf13/viper"
	"golang.org/x/image/font"
	"golang.org/x/image/font/basicfont"
	"golang.org/x/image/math/fixed"
	"periph.io/x/periph/devices/ssd1306"
)

func init() {
	syslog.Info("display setup")
	viper.SetDefault("Display.Type", "none")
	viper.SetDefault("Display.W", 128)
	viper.SetDefault("Display.H", 32)
	viper.SetDefault("Display.Rotated", false)
	viper.SetDefault("Display.SwapTopBottom", false)
	viper.SetDefault("Display.networkinterface", "eth0")
}

func displayLoop() {
	for {
		syslog.Info("ssd1306 display starting loop")

		if viper.GetString("Display.Type") != "ssd1306" {
			syslog.Warning("ssd1306 disabled")
			time.Sleep(1 * time.Second)
			continue
		}

		if i2c.I2Cbus == nil {
			syslog.Warning("ssd1306 i2cbus not found")
			time.Sleep(1 * time.Second)
			continue
		}

		opts := ssd1306.Opts{
			W:             viper.GetInt("Display.W"),
			H:             viper.GetInt("Display.H"),
			Rotated:       viper.GetBool("Display.Rotated"),
			Sequential:    true,
			SwapTopBottom: viper.GetBool("Display.SwapTopBottom"),
		}

		// Open a handle to a ssd1306 connected on the I²C bus:
		dev, err := ssd1306.NewI2C(i2c.I2Cbus, &opts)
		if err != nil {
			syslog.Err(err.Error())
		}

		syslog.Info("ssd1306 display setup completed")

		for {
			err := disp(dev)
			if err != nil {
				syslog.Err(err.Error())
				time.Sleep(1 * time.Second)
				syslog.Warning("ssd1306 display disp error")
				continue
			}
		}
	}
}

func addLabel(img *image.RGBA, x, y int, label string) {
	col := color.RGBA{200, 100, 0, 255}
	point := fixed.Point26_6{fixed.Int26_6(x * 64), fixed.Int26_6(y * 64)}

	d := &font.Drawer{
		Dst:  img,
		Src:  image.NewUniform(col),
		Face: basicfont.Face7x13,
		Dot:  point,
	}
	d.DrawString(label)
}

func getIPs() map[string]string {
	out := map[string]string{}
	ifaces, _ := net.Interfaces()
	// handle err
	for _, i := range ifaces {
		if i.Name == "lo" {
			continue
		}
		addrs, _ := i.Addrs()
		// handle err
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			// process IP address
			if ip.To4() == nil {
				continue
			}
			if len(out) < 1 {
				out[i.Name] = ip.String()
			}
		}
	}
	return out
}

func dispOutletStatusString() string {
	out := ""
	for o := range outlet.Outlets {
		if outlet.Outlets[o].Channel.Status() {
			out += fmt.Sprint(outlet.Outlets[o].Num)
		} else {
			out += "."
		}
	}
	return out
}

func disp(dev *ssd1306.Dev) error {
	img := image.NewRGBA(image.Rect(0, 0, 128, 32))
	iface := viper.GetString("Display.NetworkInterface")
	ips := getIPs()
	ip, found := ips[iface]
	if !found {
		ip = "unknown"
	}
	lineHeight := 12
	posX := 12
	posY := lineHeight
	addLabel(img, posX, posY, ip)
	posY += lineHeight
	addLabel(img, posX, posY, dispOutletStatusString())
	return dev.Draw(img.Bounds(), img, image.Point{})
}
