package bootcommand

import (
	"fmt"
	"log"
	"os"
	"strings"
	"time"
	"unicode"

	"github.com/hashicorp/packer/common"
)

const KeyLeftShift uint32 = 0xFFE1

type VNCKeyEvent interface {
	KeyEvent(uint32, bool) error
}

type vncDriver struct {
	c          VNCKeyEvent
	interval   time.Duration
	specialMap map[string]uint32
	// keyEvent can set this error which will prevent it from continuing
	err error
}

func NewVNCDriver(c VNCKeyEvent, interval time.Duration) *vncDriver {
	// We delay (default 100ms) between each key event to allow for CPU or
	// network latency. See PackerKeyEnv for tuning.
	keyInterval := common.PackerKeyDefault
	if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil {
		keyInterval = delay
	}
	// override interval based on builder-specific override.
	if interval > time.Duration(0) {
		keyInterval = interval
	}

	// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
	sMap := make(map[string]uint32)
	sMap["bs"] = 0xFF08
	sMap["del"] = 0xFFFF
	sMap["down"] = 0xFF54
	sMap["end"] = 0xFF57
	sMap["enter"] = 0xFF0D
	sMap["esc"] = 0xFF1B
	sMap["f1"] = 0xFFBE
	sMap["f2"] = 0xFFBF
	sMap["f3"] = 0xFFC0
	sMap["f4"] = 0xFFC1
	sMap["f5"] = 0xFFC2
	sMap["f6"] = 0xFFC3
	sMap["f7"] = 0xFFC4
	sMap["f8"] = 0xFFC5
	sMap["f9"] = 0xFFC6
	sMap["f10"] = 0xFFC7
	sMap["f11"] = 0xFFC8
	sMap["f12"] = 0xFFC9
	sMap["home"] = 0xFF50
	sMap["insert"] = 0xFF63
	sMap["left"] = 0xFF51
	sMap["leftalt"] = 0xFFE9
	sMap["leftctrl"] = 0xFFE3
	sMap["leftshift"] = 0xFFE1
	sMap["leftsuper"] = 0xFFEB
	sMap["menu"] = 0xFF67
	sMap["pagedown"] = 0xFF56
	sMap["pageup"] = 0xFF55
	sMap["return"] = 0xFF0D
	sMap["right"] = 0xFF53
	sMap["rightalt"] = 0xFFEA
	sMap["rightctrl"] = 0xFFE4
	sMap["rightshift"] = 0xFFE2
	sMap["rightsuper"] = 0xFFEC
	sMap["spacebar"] = 0x020
	sMap["tab"] = 0xFF09
	sMap["up"] = 0xFF52

	return &vncDriver{
		c:          c,
		interval:   keyInterval,
		specialMap: sMap,
	}
}

func (d *vncDriver) keyEvent(k uint32, down bool) error {
	if d.err != nil {
		return nil
	}
	if err := d.c.KeyEvent(k, down); err != nil {
		d.err = err
		return err
	}
	time.Sleep(d.interval)
	return nil
}

// Flush does nothing here
func (d *vncDriver) Flush() error {
	return nil
}

func (d *vncDriver) SendKey(key rune, action KeyAction) error {
	keyShift := unicode.IsUpper(key) || strings.ContainsRune(shiftedChars, key)
	keyCode := uint32(key)
	log.Printf("Sending char '%c', code 0x%X, shift %v", key, keyCode, keyShift)

	switch action {
	case KeyOn:
		if keyShift {
			d.keyEvent(KeyLeftShift, true)
		}
		d.keyEvent(keyCode, true)
	case KeyOff:
		if keyShift {
			d.keyEvent(KeyLeftShift, false)
		}
		d.keyEvent(keyCode, false)
	case KeyPress:
		if keyShift {
			d.keyEvent(KeyLeftShift, true)
		}
		d.keyEvent(keyCode, true)
		d.keyEvent(keyCode, false)
		if keyShift {
			d.keyEvent(KeyLeftShift, false)
		}
	}
	return d.err
}

func (d *vncDriver) SendSpecial(special string, action KeyAction) error {
	keyCode, ok := d.specialMap[special]
	if !ok {
		return fmt.Errorf("special %s not found.", special)
	}
	log.Printf("Special code '<%s>' found, replacing with: 0x%X", special, keyCode)

	switch action {
	case KeyOn:
		d.keyEvent(keyCode, true)
	case KeyOff:
		d.keyEvent(keyCode, false)
	case KeyPress:
		d.keyEvent(keyCode, true)
		d.keyEvent(keyCode, false)
	}

	return d.err
}