package main

import (
	"fmt"
	"log"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/xproto"
	"github.com/BurntSushi/xgb/xtest"
)

type watcher struct {
	conn                 *xgb.Conn
	root                 xproto.Window
	activeAtom, nameAtom xproto.Atom
	prevWindowName       string
	lastWindowID         xproto.Window
}

func NewWindowWatcher() *watcher {
	return &watcher{}
}

func (w *watcher) Setup() error {
	X, err := xgb.NewConn()
	if err != nil {
		return err
	}

	// Get the window id of the root window.
	setup := xproto.Setup(X)

	if err := xtest.Init(X); err != nil {
		return err
	}

	w.conn = X
	w.root = setup.DefaultScreen(X).Root

	// Get the atom id (i.e., intern an atom) of "_NET_ACTIVE_WINDOW".
	aname := "_NET_ACTIVE_WINDOW"
	activeAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
		aname).Reply()
	if err != nil {
		return fmt.Errorf("Couldn't get _NET_ACTIVE_WINDOW atom: %s", err)
	}

	// Get the atom id (i.e., intern an atom) of "_NET_WM_NAME".
	aname = "_NET_WM_NAME"
	nameAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
		aname).Reply()
	if err != nil {
		return fmt.Errorf("Couldn't get _NET_WM_NAME atom: %s", err)
	}

	w.activeAtom = activeAtom.Atom
	w.nameAtom = nameAtom.Atom

	return nil
}

func (w *watcher) Run() {
	for {
		w.watch()
		time.Sleep(2 * time.Second)
	}
}

func (w *watcher) watch() {
	// From github.com/BurntSushi/xgb's examples.
	reply, err := xproto.GetProperty(w.conn, false, w.root, w.activeAtom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
	if err != nil {
		log.Fatal(err)
	}
	windowID := xproto.Window(xgb.Get32(reply.Value))

	reply, err = xproto.GetProperty(w.conn, false, windowID, w.nameAtom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
	if err != nil {
		log.Fatal(err)
	}

	w.lastWindowID = windowID

	windowName := string(reply.Value)
	if w.prevWindowName != windowName {
		w.prevWindowName = windowName

		w.loadWindowConfiguration(windowName)
	}
}

func (w *watcher) loadWindowConfiguration(windowName string) {
	if loadedConfiguration == nil {
		fmt.Println("Window name switched, but no configuration:", windowName)
		return
	}

	for _, conf := range loadedConfiguration.Apps {
		for _, re := range conf.windowTitleRegexps {
			if re.MatchString(windowName) {
				currentConfiguration = conf
				fmt.Printf("Applying configuration for app %q\n", conf.Name)
				return
			}
		}
	}
	currentConfiguration = nil
}
