package main

import (
	"fmt"
	"net"
	"syscall"
	"strings"
	"strconv"
	"os"
	"os/exec"
	"io/ioutil"
	"time"

	"github.com/BurntSushi/toml"
)

type Config struct {
	Separator string
	IP IPConfig
	Mounts MountConfig
	Music MusicConfig
}

type IPConfig struct {
	Label string
	Interfaces []string
	IPv4Only bool `toml:"ipv4only"`
}

type MountConfig struct {
	Label string
	MountPoints []string `toml:"mountpoints"`
}

type MusicConfig struct {
	LabelPlaying string `toml:"labelplaying"`
	LabelPaused string `toml:"labelpaused"`
	LabelStopped string `toml:"labelstopped"`
}

func diskSizes(mounts []string) string {
	var b strings.Builder
	var statfs syscall.Statfs_t
	const div = float64(1024 * 1024 * 1024)
	const unit = "G"
	sep := ""
	for _, mount := range mounts {
		b.WriteString(sep)
		err := syscall.Statfs(mount, &statfs)
		if err != nil {
			panic(err)
		}
		availBytes := float64(statfs.Bavail * uint64(statfs.Bsize))
		avail := strconv.FormatFloat(availBytes / div, 'f', 2, 64)
		b.WriteString(fmt.Sprintf("%v : %v %v", mount, avail, unit))
		sep = " "
	}
	return b.String()
}

func ips(interfaces []string, IPv4Only bool) string {
	var b strings.Builder
	sep := ""
	for _, interfaceName := range interfaces {
		b.WriteString(sep)
		b.WriteString(interfaceName)
		b.WriteString(": ")
		if len(interfaces) > 1 {
			b.WriteString(" [")
		}
		b.WriteString(ip(interfaceName, IPv4Only))
		if len(interfaces) > 1 {
			b.WriteString(" ]")
		}
		sep = " "
	}
	return b.String()
}

func ip(interfaceName string, IPv4Only bool) string {
	var b strings.Builder
	sep := ""
	in, err := net.InterfaceByName(interfaceName)
	if err != nil {
		panic(err)
	}
	addrs, err := in.Addrs()
	if err != nil {
		panic(err)
	}
	for _, addr := range addrs {
		var ip net.IP
		switch v := addr.(type) {
		case *net.IPAddr:
			ip = v.IP
		case *net.IPNet:
			ip = v.IP
		default:
		}
		if IPv4Only {
			if ip.To4() != nil {
				b.WriteString(ip.String())
				b.WriteString(sep)
				sep = " "
			}
		} else {
			b.WriteString(sep)
			b.WriteString(ip.String())
			sep = " "
		}
	}
	return b.String()
}

func clock() string {
	t := time.Now()
	return t.Format("Monday 2006-01-02 15:04:05")
}

func nowPlayingMPD() string {
	cmd := exec.Command("mpc", "current")
	out, err := cmd.Output()
	if err != nil {
		panic(err)
	}
	sOut := string(out)
	sOut = strings.ReplaceAll(sOut, "\n", "")
	return sOut
}

// Returns 0 if playing, 1 if paused and 2 if stopped
func statusMPD() int {
	cmd := exec.Command("mpc", "status")
	out, err := cmd.Output()
	if err != nil {
		panic(err)
	}
	sOut := string(out)
	if strings.Contains(sOut, "playing") {
		return 0
	} else if strings.Contains(sOut, "paused") {
		return 1
	} else {
		return 2
	}
}

func main() {
	var configPath = "/etc/statusbar.conf"
	var content []byte
	var err error
	if _, err := os.Stat(configPath); err != nil {
		content, err = ioutil.ReadAll(os.Stdin)
	} else {
		content, err = ioutil.ReadFile(configPath)
	}
	if err != nil {
		panic(err)
	}
	var config Config
	_, err = toml.Decode(string(content), &config)
	if err != nil {
		panic(err)
	}
	for true {
		var b strings.Builder
		switch statusMPD() {
		case 0:
			b.WriteString(config.Music.LabelPlaying)
		case 1:
			b.WriteString(config.Music.LabelPaused)
		case 2:
			b.WriteString(config.Music.LabelStopped)
		default:
			b.WriteString("Unknown MPD status")
		}
		b.WriteString(nowPlayingMPD())
		b.WriteString(config.Separator)
		b.WriteString(config.IP.Label)
		b.WriteString("[ ")
		b.WriteString(ips(config.IP.Interfaces, config.IP.IPv4Only))
		b.WriteString(" ]")
		b.WriteString(config.Separator)
		b.WriteString(config.Mounts.Label)
		b.WriteString("[ ")
		b.WriteString(diskSizes(config.Mounts.MountPoints))
		b.WriteString(" ]")
		b.WriteString(config.Separator)
		b.WriteString(clock())
		fmt.Println(b.String())
		time.Sleep(10 * time.Second)
	}
}
