// 6 july 2014

package ui

import (
	"runtime"
	"sync"
	"unsafe"
)

// Go initializes package ui.
// TODO write this bit
func Go() error {
	runtime.LockOSThread()
	if err := uiinit(); err != nil {
		return err
	}
	go uiissueloop()
	uimsgloop()
	return nil
}

// To ensure that Do() and Stop() only do things after Go() has been called, this channel accepts the requests to issue. The issuing is done by uiissueloop() below.
var issuer = make(chan func())

// Do performs f on the main loop, as if it were an event handler.
// It waits for f to execute before returning.
// Do cannot be called within event handlers or within Do itself.
func Do(f func()) {
	done := make(chan struct{})
	defer close(done)
	issuer <- func() {
		f()
		done <- struct{}{}
	}
	<-done
}

// Stop informs package ui that it should stop.
// Stop then returns immediately.
// Some time after this request is received, Go() will return without performing any final cleanup.
// Stop will not have an effect until any event handlers or dialog boxes presently active return.
// (TODO make sure this is the case for dialog boxes)
func Stop() {
	issuer <- uistop
}

func uiissueloop() {
	for f := range issuer {
		issue(f)
	}
}

type event struct {
	// All events internally return bool; those that don't will be wrapped around to return a dummy value.
	do		func() bool
	lock		sync.Mutex
}

// do should never be nil; TODO should we make setters panic instead?

func newEvent() *event {
	return &event{
		do:	func() bool {
			return false
		},
	}
}

func (e *event) set(f func()) {
	e.lock.Lock()
	defer e.lock.Unlock()

	if f == nil {
		f = func() {}
	}
	e.do = func() bool {
		f()
		return false
	}
}

func (e *event) setbool(f func() bool) {
	e.lock.Lock()
	defer e.lock.Unlock()

	if f == nil {
		f = func() bool {
			return false
		}
	}
	e.do = f
}

// This is the common code for running an event.
// It runs on the main thread without a message pump; it provides its own.
func (e *event) fire() bool {
	e.lock.Lock()
	defer e.lock.Unlock()

	return e.do()
}

// Common code for performing a requested action (ui.Do() or ui.Stop()).
// This should run on the main thread.
// Implementations of issue() should call this.
func perform(fp unsafe.Pointer) {
	f := (*func())(fp)
	(*f)()
}