// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

package keybinding

import (
	"fmt"
	"time"

	. "github.com/linuxdeepin/dde-daemon/keybinding/shortcuts"
)

func (m *Manager) shouldShowCapsLockOSD() bool {
	return m.gsKeyboard.GetBoolean(gsKeyShowCapsLockOSD)
}

// 初始化所有快捷键处理函数
func (m *Manager) initHandlers() {
	logger.Debug("initHandlers", len(m.handlers))

	// 非操作快捷键处理函数
	m.handlers[ActionTypeNonOp] = func(ev *KeyEvent) {
		logger.Debug("non-Op do nothing")
	}

	// 回调类型快捷键处理函数
	m.handlers[ActionTypeCallback] = func(ev *KeyEvent) {
		action := ev.Shortcut.GetAction()
		fn, ok := action.Arg.(func(ev *KeyEvent))
		if !ok {
			logger.Warning(ErrTypeAssertionFail)
			return
		}

		if fn != nil {
			go fn(ev)
		}
	}

	// 执行命令类型快捷键处理函数
	m.handlers[ActionTypeExecCmd] = func(ev *KeyEvent) {
		action := ev.Shortcut.GetAction()
		arg, ok := action.Arg.(*ActionExecCmdArg)
		if !ok {
			logger.Warning(ErrTypeAssertionFail)
			return
		}

		go func() {
			var err error
			if arg.Cmd == "deepin-camera" {
				err = m.handleCheckCamera()
				if err != nil {
					logger.Warning(err)
				}
			} else {
				err = m.execCmd(arg.Cmd, true)
				if err != nil {
					logger.Warning("execCmd error:", err)
				}
			}

		}()
	}

	// 显示NumLock状态提示
	m.handlers[ActionTypeShowNumLockOSD] = func(ev *KeyEvent) {
		if _useWayland {
			m.handleKeyEventByWayland("numlock")
		} else {
			state, err := queryNumLockState(m.conn)
			if err != nil {
				logger.Warning(err)
				return
			}
			save := m.gsKeyboard.GetBoolean(gsKeySaveNumLockState)
			switch state {
			case NumLockOn:
				if save {
					m.NumLockState.Set(int32(NumLockOn))
				}
				showOSD("NumLockOn")
			case NumLockOff:
				if save {
					m.NumLockState.Set(int32(NumLockOff))
				}
				showOSD("NumLockOff")
			}
		}
	}

	// 显示CapsLock状态提示
	m.handlers[ActionTypeShowCapsLockOSD] = func(ev *KeyEvent) {
		if _useWayland {
			m.handleKeyEventByWayland("capslock")
		} else {
			if !m.shouldShowCapsLockOSD() {
				return
			}

			state, err := queryCapsLockState(m.conn)
			if err != nil {
				logger.Warning(err)
				return
			}

			switch state {
			case CapsLockOff:
				showOSD("CapsLockOff")
			case CapsLockOn:
				showOSD("CapsLockOn")
			}
		}
	}

	// 打开MIME类型关联程序
	m.handlers[ActionTypeOpenMimeType] = func(ev *KeyEvent) {
		action := ev.Shortcut.GetAction()
		mimeType, ok := action.Arg.(string)
		if !ok {
			logger.Warning(ErrTypeAssertionFail)
			return
		}

		go func() {
			err := m.execCmd(queryCommandByMime(mimeType), true)
			if err != nil {
				logger.Warning("execCmd error:", err)
			}
		}()
	}

	// 运行桌面文件
	m.handlers[ActionTypeDesktopFile] = func(ev *KeyEvent) {
		action := ev.Shortcut.GetAction()

		go func() {
			err := m.runDesktopFile(action.Arg.(string))
			if err != nil {
				logger.Warning("runDesktopFile error:", err)
			}
		}()
	}

	// 注册各控制器处理函数
	m.handlers[ActionTypeAudioCtrl] = buildHandlerFromController(m.audioController)
	m.handlers[ActionTypeMediaPlayerCtrl] = buildHandlerFromController(m.mediaPlayerController)
	m.handlers[ActionTypeDisplayCtrl] = buildHandlerFromController(m.displayController)
	m.handlers[ActionTypeKbdLightCtrl] = buildHandlerFromController(m.kbdLightController)
	m.handlers[ActionTypeTouchpadCtrl] = buildHandlerFromController(m.touchPadController)

	// 系统休眠处理函数
	m.handlers[ActionTypeSystemSuspend] = func(ev *KeyEvent) {
		if m.gsPower.GetBoolean("sleep-lock") {
			m.systemSuspendByFront()
		} else {
			m.systemSuspend()
		}
	}

	// 系统注销处理函数
	m.handlers[ActionTypeSystemLogOff] = func(ev *KeyEvent) {
		m.systemLogout()
	}

	// 系统离开处理函数
	m.handlers[ActionTypeSystemAway] = func(ev *KeyEvent) {
		m.systemAway()
	}

	// 键盘布局切换处理函数
	m.handlers[ActionTypeSwitchKbdLayout] = func(ev *KeyEvent) {
		logger.Warning("Switch Kbd Layout shortcut was disbaled by TASK-67900")
	}

	// 显示控制中心处理函数
	m.handlers[ActionTypeShowControlCenter] = func(ev *KeyEvent) {
		err := m.execCmd("dbus-send --session --dest=com.deepin.dde.ControlCenter  --print-reply /com/deepin/dde/ControlCenter com.deepin.dde.ControlCenter.Show",
			false)
		if err != nil {
			logger.Warning("failed to show control center:", err)
		}
	}

	// 初始化快捷键管理器
	if m.shortcutManager == nil {
		m.shortcutManager = NewShortcutManager(m.conn, m.keySymbols, m.handleKeyEvent)
	}

	// 设置所有修饰键释放回调
	m.shortcutManager.SetAllModKeysReleasedCallback(func() {
		switch m.switchKbdLayoutState {
		case SKLStateWait:
			showOSD("DirectSwitchLayout")
			m.terminateSKLWait()
		case SKLStateOSDShown:
			showOSD("SwitchLayoutDone")
		case SKLStateNone:
			return
		}
		m.switchKbdLayoutState = SKLStateNone
	})
}

func (m *Manager) sklWait() {
	defer func() {
		logger.Debug("sklWait end")
		m.sklWaitQuit = nil
	}()

	m.sklWaitQuit = make(chan int)
	timer := time.NewTimer(350 * time.Millisecond)
	select {
	case <-m.sklWaitQuit:
		return
	case _, ok := <-timer.C:
		if !ok {
			logger.Error("Invalid ticker event")
			return
		}

		logger.Debug("timer fired")
		if m.switchKbdLayoutState == SKLStateWait {
			m.switchKbdLayoutState = SKLStateOSDShown
			showOSD("SwitchLayout")
		}
	}
}

func (m *Manager) terminateSKLWait() {
	if m.sklWaitQuit != nil {
		close(m.sklWaitQuit)
	}
}

// 控制器接口，用于处理特定类型的快捷键
type Controller interface {
	ExecCmd(cmd ActionCmd) error // 执行快捷键命令
	Name() string                // 获取控制器名称
}

// 创建基于控制器的快捷键处理函数
func buildHandlerFromController(c Controller) KeyEventFunc {
	return func(ev *KeyEvent) {
		if c == nil {
			logger.Warning("controller is nil")
			return
		}
		name := c.Name()

		action := ev.Shortcut.GetAction()
		cmd, ok := action.Arg.(ActionCmd)
		if !ok {
			logger.Warning(ErrTypeAssertionFail)
			return
		}
		logger.Debugf("%v Controller exec cmd %v", name, cmd)
		if err := c.ExecCmd(cmd); err != nil {
			logger.Warning(name, "Controller exec cmd err:", err)
		}
	}
}

// 无效操作命令错误
type ErrInvalidActionCmd struct {
	Cmd ActionCmd
}

func (err ErrInvalidActionCmd) Error() string {
	return fmt.Sprintf("invalid action cmd %v", err.Cmd)
}

// 空值错误
type ErrIsNil struct {
	Name string
}

func (err ErrIsNil) Error() string {
	return fmt.Sprintf("%s is nil", err.Name)
}
