package x

import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/xgbkb"

import "github.com/jezek/xgbutil"
import "github.com/jezek/xgb/xproto"
import "github.com/jezek/xgbutil/xevent"
import "github.com/jezek/xgbutil/keybind"
import "github.com/jezek/xgbutil/mousebind"

type Backend struct {
	x         *xgbutil.XUtil
	doChannel chan func()
	windows   map[xproto.Window] *window
	open      bool
}

func NewBackend () (tomo.Backend, error) {
	backend := &Backend {
		doChannel: make(chan func (), 32),
		windows:   make(map[xproto.Window] *window),
		open:      true,
	}

	var err error
	backend.x, err = xgbutil.NewConn()
	if err != nil { return nil, err }

	keybind  .Initialize(backend.x)
	xgbkb    .Initialize(backend.x)
	mousebind.Initialize(backend.x)
	
	return backend, nil
}

func (backend *Backend) Run () error {
	backend.assert()
	pingBefore,
	pingAfter,
	pingQuit := xevent.MainPing(backend.x)
	for {
		select {
		case <- pingBefore:
			<- pingAfter
		case callback := <- backend.doChannel:
			callback()
		case <- pingQuit:
			return nil // FIXME: if we exited due to an error say so
		}
		for _, window := range backend.windows {
			window.afterEvent()
		}
	}
}

func (backend *Backend) Stop () {
	backend.assert()
	if !backend.open { return }
	backend.open = false
	
	toClose := []*window { }
	for _, panel := range backend.windows {
		toClose = append(toClose, panel)
	}
	for _, panel := range toClose {
		panel.Close()
	}
	xevent.Quit(backend.x)
	backend.x.Conn().Close()
}

func (backend *Backend) Do (callback func ()) {
	backend.assert()
	backend.doChannel <- callback
}

func (backend *Backend) assert () {
	if backend == nil { panic("nil backend") }
}