package gui

import (
	"fmt"

	"github.com/d-tsuji/clipboard"
	"github.com/hajimehoshi/ebiten/v2"
)

var modifiableKeys = map[ebiten.Key]uint8{
	ebiten.KeyA: 'A',
	ebiten.KeyB: 'B',
	ebiten.KeyC: 'C',
	ebiten.KeyD: 'D',
	ebiten.KeyE: 'E',
	ebiten.KeyF: 'F',
	ebiten.KeyG: 'G',
	ebiten.KeyH: 'H',
	ebiten.KeyI: 'I',
	ebiten.KeyJ: 'J',
	ebiten.KeyK: 'K',
	ebiten.KeyL: 'L',
	ebiten.KeyM: 'M',
	ebiten.KeyN: 'N',
	ebiten.KeyO: 'O',
	ebiten.KeyP: 'P',
	ebiten.KeyQ: 'Q',
	ebiten.KeyR: 'R',
	ebiten.KeyS: 'S',
	ebiten.KeyT: 'T',
	ebiten.KeyU: 'U',
	ebiten.KeyV: 'V',
	ebiten.KeyW: 'W',
	ebiten.KeyX: 'X',
	ebiten.KeyY: 'Y',
	ebiten.KeyZ: 'Z',
}

func (g *GUI) handleInput() error {

	if err := g.handleMouse(); err != nil {
		return err
	}

	switch true {

	case ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyShift):

		switch true {
		case g.keyState.RepeatPressed(ebiten.KeyC):
			content, selection := g.terminal.GetActiveBuffer().GetSelection()
			if selection == nil {
				return nil
			}
			return clipboard.Set(content)
		case g.keyState.RepeatPressed(ebiten.KeyV):
			paste, err := clipboard.Get()
			if err != nil {
				return err
			}
			return g.terminal.WriteToPty([]byte(paste))
		case g.keyState.RepeatPressed(ebiten.KeyBracketLeft):
			g.RequestScreenshot("")
		}

	case ebiten.IsKeyPressed(ebiten.KeyControl):

		for key, ch := range modifiableKeys {
			if g.keyState.RepeatPressed(key) {
				if ch >= 97 && ch < 123 {
					return g.terminal.WriteToPty([]byte{ch - 96})
				} else if ch >= 65 && ch < 91 {
					return g.terminal.WriteToPty([]byte{ch - 64})
				}
			}
		}

		switch true {
		case g.keyState.RepeatPressed(ebiten.KeyMinus):
			g.fontManager.DecreaseSize()
			cellSize := g.fontManager.CharSize()
			cols, rows := g.size.X/cellSize.X, g.size.Y/cellSize.Y
			if err := g.terminal.SetSize(uint16(rows), uint16(cols)); err != nil {
				return err
			}
			return nil
		case g.keyState.RepeatPressed(ebiten.KeyEqual):
			g.fontManager.IncreaseSize()
			cellSize := g.fontManager.CharSize()
			cols, rows := g.size.X/cellSize.X, g.size.Y/cellSize.Y
			if err := g.terminal.SetSize(uint16(rows), uint16(cols)); err != nil {
				return err
			}
			return nil
		default:
			return nil
		}

	case ebiten.IsKeyPressed(ebiten.KeyAlt):

		for key, ch := range modifiableKeys {
			if g.keyState.RepeatPressed(key) {
				return g.terminal.WriteToPty([]byte{0x1b, ch})
			}
		}

	case g.keyState.RepeatPressed(ebiten.KeyArrowUp):
		if g.terminal.GetActiveBuffer().IsApplicationCursorKeysModeEnabled() {
			return g.terminal.WriteToPty([]byte{
				0x1b,
				'O',
				'A',
			})
		} else {
			return g.terminal.WriteToPty([]byte(fmt.Sprintf("\x1b[%sA", g.getModifierStr())))
		}
	case g.keyState.RepeatPressed(ebiten.KeyArrowDown):
		if g.terminal.GetActiveBuffer().IsApplicationCursorKeysModeEnabled() {
			return g.terminal.WriteToPty([]byte{
				0x1b,
				'O',
				'B',
			})
		} else {
			return g.terminal.WriteToPty([]byte(fmt.Sprintf("\x1b[%sB", g.getModifierStr())))
		}
	case g.keyState.RepeatPressed(ebiten.KeyArrowRight):
		if g.terminal.GetActiveBuffer().IsApplicationCursorKeysModeEnabled() {
			return g.terminal.WriteToPty([]byte{
				0x1b,
				'O',
				'C',
			})
		} else {
			return g.terminal.WriteToPty([]byte(fmt.Sprintf("\x1b[%sC", g.getModifierStr())))
		}
	case g.keyState.RepeatPressed(ebiten.KeyArrowLeft):
		if g.terminal.GetActiveBuffer().IsApplicationCursorKeysModeEnabled() {
			return g.terminal.WriteToPty([]byte{
				0x1b,
				'O',
				'D',
			})
		} else {
			return g.terminal.WriteToPty([]byte(fmt.Sprintf("\x1b[%sD", g.getModifierStr())))
		}
	case g.keyState.RepeatPressed(ebiten.KeyEnter):
		if g.terminal.GetActiveBuffer().IsNewLineMode() {
			return g.terminal.WriteToPty([]byte{0x0d, 0x0a})
		}
		return g.terminal.WriteToPty([]byte{0x0d})
	case g.keyState.RepeatPressed(ebiten.KeyNumpadEnter):
		if g.terminal.GetActiveBuffer().IsApplicationCursorKeysModeEnabled() {
			g.terminal.WriteToPty([]byte{
				0x1b,
				'O',
				'M',
			})
		} else {
			if g.terminal.GetActiveBuffer().IsNewLineMode() {
				if err := g.terminal.WriteToPty([]byte{0x0d, 0x0a}); err != nil {
					return err
				}
			}
			return g.terminal.WriteToPty([]byte{0x0d})
		}
	case g.keyState.RepeatPressed(ebiten.KeyTab):
		return g.terminal.WriteToPty([]byte{0x09}) // tab

	case g.keyState.RepeatPressed(ebiten.KeyEscape):
		return g.terminal.WriteToPty([]byte{0x1b}) // escape
	case g.keyState.RepeatPressed(ebiten.KeyBackspace):
		if ebiten.IsKeyPressed(ebiten.KeyAlt) {
			return g.terminal.WriteToPty([]byte{0x17}) // ctrl-w/delete word
		} else {
			return g.terminal.WriteToPty([]byte{0x7f}) //0x7f is DEL
		}
	case g.keyState.RepeatPressed(ebiten.KeyF1):
		return g.terminal.WriteToPty([]byte("\x1bOP"))
	case g.keyState.RepeatPressed(ebiten.KeyF2):
		return g.terminal.WriteToPty([]byte("\x1bOQ"))
	case g.keyState.RepeatPressed(ebiten.KeyF3):
		return g.terminal.WriteToPty([]byte("\x1bOR"))
	case g.keyState.RepeatPressed(ebiten.KeyF4):
		return g.terminal.WriteToPty([]byte("\x1bOS"))
	case g.keyState.RepeatPressed(ebiten.KeyF5):
		return g.terminal.WriteToPty([]byte("\x1b[15~"))
	case g.keyState.RepeatPressed(ebiten.KeyF6):
		return g.terminal.WriteToPty([]byte("\x1b[17~"))
	case g.keyState.RepeatPressed(ebiten.KeyF7):
		return g.terminal.WriteToPty([]byte("\x1b[18~"))
	case g.keyState.RepeatPressed(ebiten.KeyF8):
		return g.terminal.WriteToPty([]byte("\x1b[19~"))
	case g.keyState.RepeatPressed(ebiten.KeyF9):
		return g.terminal.WriteToPty([]byte("\x1b[20~"))
	case g.keyState.RepeatPressed(ebiten.KeyF10):
		return g.terminal.WriteToPty([]byte("\x1b[21~"))
	case g.keyState.RepeatPressed(ebiten.KeyF11):
		return g.terminal.WriteToPty([]byte("\x1b[22~"))
	case g.keyState.RepeatPressed(ebiten.KeyF12):
		return g.terminal.WriteToPty([]byte("\x1b[23~"))
	case g.keyState.RepeatPressed(ebiten.KeyInsert):
		return g.terminal.WriteToPty([]byte("\x1b[2~"))
	case g.keyState.RepeatPressed(ebiten.KeyDelete):
		return g.terminal.WriteToPty([]byte("\x1b[3~"))
	case g.keyState.RepeatPressed(ebiten.KeyHome):
		if g.terminal.GetActiveBuffer().IsApplicationCursorKeysModeEnabled() {
			return g.terminal.WriteToPty([]byte(fmt.Sprintf("\x1b[1%s~", g.getModifierStr())))
		} else {
			return g.terminal.WriteToPty([]byte("\x1b[H"))
		}
	case g.keyState.RepeatPressed(ebiten.KeyEnd):
		return g.terminal.WriteToPty([]byte(fmt.Sprintf("\x1b[4%s~", g.getModifierStr())))
	case g.keyState.RepeatPressed(ebiten.KeyPageUp):
		return g.terminal.WriteToPty([]byte(fmt.Sprintf("\x1b[5%s~", g.getModifierStr())))
	case g.keyState.RepeatPressed(ebiten.KeyPageDown):
		return g.terminal.WriteToPty([]byte(fmt.Sprintf("\x1b[6%s~", g.getModifierStr())))
	default:
		input := ebiten.AppendInputChars(nil)
		for _, runePressed := range input {
			if err := g.terminal.WriteToPty([]byte(string(runePressed))); err != nil {
				return err
			}
		}
	}

	return nil
}
