package d2player

import (
	"sync"

	"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)

// KeyMap represents the key mappings of the game. Each game event
// can be associated to 2 different keys. A key of -1 means none
type KeyMap struct {
	mutex    sync.RWMutex
	mapping  map[d2enum.Key]d2enum.GameEvent
	controls map[d2enum.GameEvent]*KeyBinding
}

// NewKeyMap returns a new instance of a KeyMap
func NewKeyMap() *KeyMap {
	return &KeyMap{
		mapping:  make(map[d2enum.Key]d2enum.GameEvent),
		controls: make(map[d2enum.GameEvent]*KeyBinding),
	}
}

// SetPrimaryBinding binds the first key for gameEvent
func (km *KeyMap) SetPrimaryBinding(gameEvent d2enum.GameEvent, key d2enum.Key) {
	if key == d2enum.KeyEscape {
		return
	}

	km.mutex.Lock()
	defer km.mutex.Unlock()

	if km.controls[gameEvent] == nil {
		km.controls[gameEvent] = &KeyBinding{}
	}

	currentKey := km.controls[gameEvent].Primary
	delete(km.mapping, currentKey)
	km.mapping[key] = gameEvent

	km.controls[gameEvent].Primary = key
}

// SetSecondaryBinding binds the second key for gameEvent
func (km *KeyMap) SetSecondaryBinding(gameEvent d2enum.GameEvent, key d2enum.Key) {
	if key == d2enum.KeyEscape {
		return
	}

	km.mutex.Lock()
	defer km.mutex.Unlock()

	if km.controls[gameEvent] == nil {
		km.controls[gameEvent] = &KeyBinding{}
	}

	currentKey := km.controls[gameEvent].Secondary
	delete(km.mapping, currentKey)
	km.mapping[key] = gameEvent

	if km.controls[gameEvent].Primary == key {
		km.controls[gameEvent].Primary = d2enum.Key(-1)
	}

	km.controls[gameEvent].Secondary = key
}

func (km *KeyMap) getGameEvent(key d2enum.Key) d2enum.GameEvent {
	km.mutex.RLock()
	defer km.mutex.RUnlock()

	return km.mapping[key]
}

// GetKeysForGameEvent returns the bindings for a givent game event
func (km *KeyMap) GetKeysForGameEvent(gameEvent d2enum.GameEvent) *KeyBinding {
	km.mutex.RLock()
	defer km.mutex.RUnlock()

	return km.controls[gameEvent]
}

// KeyBinding holds the primary and secondary keys assigned to a GameEvent
type KeyBinding struct {
	Primary   d2enum.Key
	Secondary d2enum.Key
}

func getDefaultKeyMap() *KeyMap {
	keyMap := NewKeyMap()

	defaultControls := map[d2enum.GameEvent]KeyBinding{
		d2enum.ToggleCharacterPanel:     {d2enum.KeyA, d2enum.KeyC},
		d2enum.ToggleInventoryPanel:     {d2enum.KeyB, d2enum.KeyI},
		d2enum.ToggleHelpScreen:         {d2enum.KeyH, -1},
		d2enum.TogglePartyPanel:         {d2enum.KeyP, -1},
		d2enum.ToggleMessageLog:         {d2enum.KeyM, -1},
		d2enum.ToggleQuestLog:           {d2enum.KeyQ, -1},
		d2enum.ToggleChatOverlay:        {d2enum.KeyEnter, -1},
		d2enum.ToggleAutomap:            {d2enum.KeyTab, -1},
		d2enum.CenterAutomap:            {d2enum.KeyHome, -1},
		d2enum.ToggleSkillTreePanel:     {d2enum.KeyT, -1},
		d2enum.ToggleRightSkillSelector: {d2enum.KeyS, -1},
		d2enum.UseSkill1:                {d2enum.KeyF1, -1},
		d2enum.UseSkill2:                {d2enum.KeyF2, -1},
		d2enum.UseSkill3:                {d2enum.KeyF3, -1},
		d2enum.UseSkill4:                {d2enum.KeyF4, -1},
		d2enum.UseSkill5:                {d2enum.KeyF5, -1},
		d2enum.UseSkill6:                {d2enum.KeyF6, -1},
		d2enum.UseSkill7:                {d2enum.KeyF7, -1},
		d2enum.UseSkill8:                {d2enum.KeyF8, -1},
		d2enum.UseSkill9:                {-1, -1},
		d2enum.UseSkill10:               {-1, -1},
		d2enum.UseSkill11:               {-1, -1},
		d2enum.UseSkill12:               {-1, -1},
		d2enum.UseSkill13:               {-1, -1},
		d2enum.UseSkill14:               {-1, -1},
		d2enum.UseSkill15:               {-1, -1},
		d2enum.UseSkill16:               {-1, -1},
		d2enum.ToggleBelts:              {d2enum.KeyTilde, -1},
		d2enum.UseBeltSlot1:             {d2enum.Key1, -1},
		d2enum.UseBeltSlot2:             {d2enum.Key2, -1},
		d2enum.UseBeltSlot3:             {d2enum.Key3, -1},
		d2enum.UseBeltSlot4:             {d2enum.Key4, -1},
		d2enum.ToggleRunWalk:            {d2enum.KeyR, -1},
		d2enum.HoldRun:                  {d2enum.KeyControl, -1},
		d2enum.HoldShowGroundItems:      {d2enum.KeyAlt, -1},
		d2enum.HoldShowPortraits:        {d2enum.KeyZ, -1},
		d2enum.HoldStandStill:           {d2enum.KeyShift, -1},
		d2enum.ClearScreen:              {d2enum.KeySpace, -1},
	}
	for gameEvent, keys := range defaultControls {
		keyMap.SetPrimaryBinding(gameEvent, keys.Primary)
		keyMap.SetSecondaryBinding(gameEvent, keys.Secondary)
	}

	return keyMap
}
