package cli

import 

// Buffer size of the input channel. The value is chosen for no particular
// reason.
const inputChSize = 128

// A generic main loop manager.
type loop struct {
	inputCh  chan event
	handleCb handleCb

	redrawCb redrawCb

	redrawCh    chan struct{}
	redrawFull  bool
	redrawMutex *sync.Mutex

	returnCh chan loopReturn
}

type loopReturn struct {
	buffer string
	err    error
}

// A placeholder type for events.
type event interface{}

// Callback for redrawing the editor UI to the terminal.
type redrawCb func(flag redrawFlag)

func (redrawFlag) {}

// Flag to redrawCb.
type redrawFlag uint

// Bit flags for redrawFlag.
const (
	// fullRedraw signals a "full redraw". This is set on the first RedrawCb
	// call or when Redraw has been called with full = true.
	fullRedraw redrawFlag = 1 << iota
	// finalRedraw signals that this is the final redraw in the event loop.
	finalRedraw
)

// Callback for handling a terminal event.
type handleCb func(event)

func (event) {}

// newLoop creates a new Loop instance.
func () *loop {
	return &loop{
		inputCh:  make(chan event, inputChSize),
		handleCb: dummyHandleCb,
		redrawCb: dummyRedrawCb,

		redrawCh:    make(chan struct{}, 1),
		redrawFull:  false,
		redrawMutex: new(sync.Mutex),

		returnCh: make(chan loopReturn, 1),
	}
}

// HandleCb sets the handle callback. It must be called before any Read call.
func ( *loop) ( handleCb) {
	.handleCb = 
}

// RedrawCb sets the redraw callback. It must be called before any Read call.
func ( *loop) ( redrawCb) {
	.redrawCb = 
}

// Redraw requests a redraw. If full is true, a full redraw is requested. It
// never blocks.
func ( *loop) ( bool) {
	.redrawMutex.Lock()
	defer .redrawMutex.Unlock()
	if  {
		.redrawFull = true
	}
	select {
	case .redrawCh <- struct{}{}:
	default:
	}
}

// Input provides an input event. It may block if the internal event buffer is
// full.
func ( *loop) ( event) {
	.inputCh <- 
}

// Return requests the main loop to return. It never blocks. If Return has been
// called before during the current loop iteration, it has no effect.
func ( *loop) ( string,  error) {
	select {
	case .returnCh <- loopReturn{, }:
	default:
	}
}

// HasReturned returns whether Return has been called during the current loop
// iteration.
func ( *loop) () bool {
	return len(.returnCh) == 1
}

// Run runs the event loop, until the Return method is called. It is generic
// and delegates all concrete work to callbacks. It is fully serial: it does
// not spawn any goroutines and never calls two callbacks in parallel, so the
// callbacks may manipulate shared states without synchronization.
func ( *loop) () ( string,  error) {
	for {
		var  redrawFlag
		if .extractRedrawFull() {
			 |= fullRedraw
		}
		.redrawCb()
		select {
		case  := <-.inputCh:
			// Consume all events in the channel to minimize redraws.
		:
			for {
				.handleCb()
				select {
				case  := <-.returnCh:
					.redrawCb(finalRedraw)
					return .buffer, .err
				default:
				}
				select {
				case  = <-.inputCh:
					// Continue the loop of consuming all events.
				default:
					break 
				}
			}
		case  := <-.returnCh:
			.redrawCb(finalRedraw)
			return .buffer, .err
		case <-.redrawCh:
		}
	}
}

func ( *loop) () bool {
	.redrawMutex.Lock()
	defer .redrawMutex.Unlock()

	 := .redrawFull
	.redrawFull = false
	return 
}