package door

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"time"
	"unicode"
)

// For debugging input reader routines.
const DEBUG_INPUT = false

var ErrInactivity error = fmt.Errorf("Inactivity")
var ErrTimeout error = fmt.Errorf("Timeout")
var ErrDisconnected error = fmt.Errorf("Disconnected")

var DefaultTimeout time.Duration = time.Duration(60) * time.Second

func (d *Door) WaitKey(timeout time.Duration) (rune, Extended, error) {
	/*
		d.readerMutex.Lock()
		if d.ReaderClosed {
			d.readerMutex.Unlock()
			return 0, NOP, ErrDisconnected
		}
		d.readerMutex.Unlock()
	*/

	// Probably faster to just read from closed channel and get ok = false.

	select {
	case r, ok := <-d.readerChannel:
		if ok {
			if DEBUG_INPUT {
				log.Println("WaitKey:", r)
			}
			// return bio.GetKey()
			return r.R, r.Ex, r.Err
		} else {
			log.Println("WaitKey: Disconnected")
			// Reader has closed.
			// Disconnected = true
			return 0, NOP, ErrDisconnected
		}
	case <-time.After(timeout):
		return 0, NOP, ErrTimeout
	}
}

// Outputs spaces and backspaces
// If you have set a background color, this shows the input area.
func DisplayInput(max int) string {
	return strings.Repeat(" ", max) + strings.Repeat("\x08", max)
}

// Input a string of max length.
// This displays the input area if a bg color was set.
// This handles timeout, input, backspace, and enter.
func (d *Door) Input(max int) string {
	var line []rune = make([]rune, 0, max)
	var length int

	// draw input area
	d.Write(DisplayInput(max))

	var r rune
	var ex Extended
	var err error

	for {
		r, ex, err = d.WaitKey(DefaultTimeout)
		if err != nil {
			// timeout/hangup
			return ""
		}

		if ex != NOP {
			continue
		}

		uw := UnicodeWidth(r)

		if strconv.IsPrint(r) {
			if length+uw <= max {
				d.Write(string(r))
				line = append(line, r)
				length += uw
			} else {
				d.Write("\x07")
			}
		} else {
			// Non-print
			switch r {
			case 0x7f, 0x08:
				if len(line) > 0 {
					d.Write("\x08 \x08")
					rlen := len(line)
					if UnicodeWidth(line[rlen-1]) == 2 {
						d.Write("\x08 \x08")
						length -= 2
					} else {
						length--
					}
					line = line[0 : rlen-1]
				}
			case 0x0d:
				return string(line)
			}
		}
	}
}

func (d *Door) GetOneOf(possible string) rune {
	var r rune
	var err error

	for {
		r, _, err = d.WaitKey(DefaultTimeout)
		if err != nil {
			return rune(0)
		}
		r := unicode.ToUpper(r)
		if strings.ContainsRune(possible, r) {
			// return upper case rune
			return r
		}
	}
}