package skills

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

type fbformat struct {
	bpp     int
	roffset int
	goffset int
	boffset int
	aoffset int
	format  string
}

var fbformats []fbformat = []fbformat{
	{32, 0, 8, 16, 24, "rgba"},
	{32, 16, 8, 0, 24, "bgra"},
	{32, 8, 16, 24, 0, "argb"},
	{32, 3, 2, 8, 0, "abgr"},
	{24, 0, 8, 16, 0, "rgb24"},
	{24, 16, 8, 0, 0, "bgr24"},
	{16, 11, 5, 0, 0, "rgb565le"},
	{16, 0, 5, 11, 0, "rgb565be"},
}

// DevicesFramebuffer returns a list of framebuffer devices found on the system
func DevicesFramebuffer() ([]HWDevice, error) {
	devices := []HWDevice{}

	for i := 0; i < 32; i++ {
		path := fmt.Sprintf("/dev/fb%d", i)
		_, err := os.Stat(path)
		if err != nil {
			break
		}

		buf := bytes.NewBuffer(nil)

		cmd := exec.Command("fbset", "-s", "-fb", path)
		cmd.Env = []string{}
		cmd.Stdout = buf
		cmd.Run()

		device := HWDevice{
			Id:    path,
			Name:  path,
			Extra: parseFramebufferDevice(buf),
			Media: "video",
		}

		devices = append(devices, device)
	}

	return devices, nil
}

func parseFramebufferDevice(data *bytes.Buffer) string {
	reGeometry := regexp.MustCompile(`geometry ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)`)
	reRGBA := regexp.MustCompile(`rgba ([0-9]+/[0-9]+),([0-9]+/[0-9]+),([0-9]+/[0-9]+),([0-9]+/[0-9]+)`)

	geometry := ""
	format := fbformat{}

	scanner := bufio.NewScanner(data)
	scanner.Split(bufio.ScanLines)

	for scanner.Scan() {
		line := scanner.Text()

		matches := reGeometry.FindStringSubmatch(line)
		if matches != nil {
			geometry = fmt.Sprintf("%sx%s", matches[1], matches[2])
			if matches[1] != matches[3] || matches[2] != matches[4] {
				geometry += fmt.Sprintf(" (%sx%s)", matches[3], matches[4])
			}

			if x, err := strconv.Atoi(matches[5]); err == nil {
				format.bpp = x
			}
		}

		matches = reRGBA.FindStringSubmatch(line)
		if matches != nil {
			format.roffset = parseFramebufferDeviceOffset(matches[1])
			format.goffset = parseFramebufferDeviceOffset(matches[2])
			format.boffset = parseFramebufferDeviceOffset(matches[3])
			//format.aoffset = parseFramebufferDeviceOffset(matches[4])
		}
	}

	if len(geometry) == 0 || format.bpp == 0 {
		return ""
	}

	for _, f := range fbformats {
		if f.bpp == format.bpp && f.roffset == format.roffset && f.goffset == format.goffset && f.boffset == format.boffset {
			return geometry + " " + f.format
		}
	}

	return ""
}

func parseFramebufferDeviceOffset(format string) int {
	c := strings.Split(format, "/")

	bits := 0
	offset := 0

	if x, err := strconv.Atoi(c[0]); err == nil {
		bits = x
	} else {
		return 0
	}

	if x, err := strconv.Atoi(c[1]); err == nil {
		offset = x
	} else {
		return 0
	}

	if bits == 0 {
		return 0
	}

	return offset
}
