package gioui

import (
	"gioui.org/io/clipboard"
	"gioui.org/io/key"
)

var noteMap = map[key.Name]int{
	"Z": -12,
	"S": -11,
	"X": -10,
	"D": -9,
	"C": -8,
	"V": -7,
	"G": -6,
	"B": -5,
	"H": -4,
	"N": -3,
	"J": -2,
	"M": -1,
	",": 0,
	"L": 1,
	".": 2,
	"Q": 0,
	"2": 1,
	"W": 2,
	"3": 3,
	"E": 4,
	"R": 5,
	"5": 6,
	"T": 7,
	"6": 8,
	"Y": 9,
	"7": 10,
	"U": 11,
	"I": 12,
	"9": 13,
	"O": 14,
	"0": 15,
	"P": 16,
}

// KeyEvent handles incoming key events and returns true if repaint is needed.
func (t *Tracker) KeyEvent(e key.Event, gtx C) {
	if e.State == key.Press {
		switch e.Name {
		case "V":
			if e.Modifiers.Contain(key.ModShortcut) {
				gtx.Execute(clipboard.ReadCmd{Tag: t})
				return
			}
		case "Z":
			if e.Modifiers.Contain(key.ModShortcut) {
				t.Model.Undo().Do()
				return
			}
		case "Y":
			if e.Modifiers.Contain(key.ModShortcut) {
				t.Model.Redo().Do()
				return
			}
		case "D":
			if e.Modifiers.Contain(key.ModShortcut) {
				t.Model.UnitDisabled().Bool().Toggle()
				return
			}
		case "L":
			if e.Modifiers.Contain(key.ModShortcut) {
				t.Model.LoopToggle().Bool().Toggle()
				return
			}
		case "N":
			if e.Modifiers.Contain(key.ModShortcut) {
				t.NewSong().Do()
				return
			}
		case "S":
			if e.Modifiers.Contain(key.ModShortcut) {
				t.SaveSong().Do()
				return
			}
		case "O":
			if e.Modifiers.Contain(key.ModShortcut) {
				t.OpenSong().Do()
				return
			}
		case "I":
			if e.Modifiers.Contain(key.ModShortcut) {
				if e.Modifiers.Contain(key.ModShift) {
					t.DeleteInstrument().Do()
				} else {
					t.AddInstrument().Do()
				}
				return
			}
		case "T":
			if e.Modifiers.Contain(key.ModShortcut) {
				if e.Modifiers.Contain(key.ModShift) {
					t.DeleteTrack().Do()
				} else {
					t.AddTrack().Do()
				}
				return
			}
		case "E":
			if e.Modifiers.Contain(key.ModShortcut) {
				t.InstrEnlarged().Bool().Toggle()
				return
			}
		case "W":
			if e.Modifiers.Contain(key.ModShortcut) && canQuit {
				t.Quit().Do()
				return
			}
		case "F1":
			t.OrderEditor.scrollTable.Focus()
			return
		case "F2":
			t.TrackEditor.scrollTable.Focus()
			return
		case "F3":
			t.InstrumentEditor.Focus()
			return
		case "F5":
			t.SongPanel.RewindBtn.Action.Do()
			t.SongPanel.NoteTracking.Bool.Set(!e.Modifiers.Contain(key.ModCtrl))
			return
		case "F6", "Space":
			t.SongPanel.PlayingBtn.Bool.Toggle()
			t.SongPanel.NoteTracking.Bool.Set(!e.Modifiers.Contain(key.ModCtrl))
			return
		case "F7":
			t.SongPanel.RecordBtn.Bool.Toggle()
			return
		case "F8":
			t.SongPanel.NoteTracking.Bool.Toggle()
			return
		case "F12":
			t.Panic().Bool().Toggle()
			return
		case `\`, `<`, `>`:
			if e.Modifiers.Contain(key.ModShift) {
				t.OctaveNumberInput.Int.Add(1)
			} else {
				t.OctaveNumberInput.Int.Add(-1)
			}
		case key.NameTab:
			if e.Modifiers.Contain(key.ModShift) {
				switch {
				case t.OrderEditor.scrollTable.Focused():
					t.InstrumentEditor.unitEditor.sliderList.Focus()
				case t.TrackEditor.scrollTable.Focused():
					t.OrderEditor.scrollTable.Focus()
				case t.InstrumentEditor.Focused():
					if t.InstrumentEditor.enlargeBtn.Bool.Value() {
						t.InstrumentEditor.unitEditor.sliderList.Focus()
					} else {
						t.TrackEditor.scrollTable.Focus()
					}
				default:
					t.InstrumentEditor.Focus()
				}
			} else {
				switch {
				case t.OrderEditor.scrollTable.Focused():
					t.TrackEditor.scrollTable.Focus()
				case t.TrackEditor.scrollTable.Focused():
					t.InstrumentEditor.Focus()
				case t.InstrumentEditor.Focused():
					t.InstrumentEditor.unitEditor.sliderList.Focus()
				default:
					if t.InstrumentEditor.enlargeBtn.Bool.Value() {
						t.InstrumentEditor.Focus()
					} else {
						t.OrderEditor.scrollTable.Focus()
					}
				}
			}
		}
		t.JammingPressed(e)
	} else { // e.State == key.Release
		t.JammingReleased(e)
	}
}

func (t *Tracker) JammingPressed(e key.Event) byte {
	if val, ok := noteMap[e.Name]; ok {
		if _, ok := t.KeyPlaying[e.Name]; !ok {
			n := noteAsValue(t.OctaveNumberInput.Int.Value(), val)
			instr := t.InstrumentEditor.instrumentDragList.TrackerList.Selected()
			t.KeyPlaying[e.Name] = t.InstrNoteOn(instr, n)
			return n
		}
	}
	return 0
}

func (t *Tracker) JammingReleased(e key.Event) bool {
	if noteID, ok := t.KeyPlaying[e.Name]; ok {
		noteID.NoteOff()
		delete(t.KeyPlaying, e.Name)
		return true
	}
	return false
}
