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

// 键盘快捷键工具函数
// 提供系统操作、状态查询等辅助功能
package keybinding

import (
	"bytes"
	"errors"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync/atomic"
	"time"

	dbus "github.com/godbus/dbus"
	"github.com/linuxdeepin/dde-daemon/keybinding/util"
	wm "github.com/linuxdeepin/go-dbus-factory/com.deepin.wm"
	gio "github.com/linuxdeepin/go-gir/gio-2.0"
	"github.com/linuxdeepin/go-lib/strv"
	"github.com/linuxdeepin/go-x11-client/ext/dpms"
)

// nolint
const (
	suspendStateUnknown = iota + 1
	suspendStateLidOpen
	suspendStateFinish
	suspendStateWakeup
	suspendStatePrepare
	suspendStateLidClose
	suspendStateButtonClick
)

type networkDevice struct {
	Udi                 string
	Path                dbus.ObjectPath
	State               uint32
	Interface           string
	ClonedAddress       string
	HwAddress           string
	Driver              string
	Managed             bool
	Vendor              string
	UniqueUuid          string
	UsbDevice           bool
	ActiveAp            dbus.ObjectPath
	SupportHotspot      bool
	Mode                uint32
	MobileNetworkType   string
	MobileSignalQuality uint32
	InterfaceFlags      uint32
}

// resetGSettings 重置GSettings配置到默认值
// resetGSettings - 重置GSettings配置到默认值
// 参数: gs - 要重置的GSettings对象
func resetGSettings(gs *gio.Settings) {
	for _, key := range gs.ListKeys() {
		userVal := gs.GetUserValue(key)
		if userVal != nil {
			// TODO unref userVal
			logger.Debug("reset gsettings key", key)
			gs.Reset(key)
		}
	}
}

// resetKWin 重置KWin快捷键到默认设置
// resetKWin - 重置KWin快捷键到默认设置
// 参数: wmObj - KWin的DBus代理对象
// 返回值: 错误信息
func resetKWin(wmObj wm.Wm) error {
	accels, err := util.GetAllKWinAccels(wmObj)
	if err != nil {
		return err
	}
	for _, accel := range accels {
		// logger.Debug("resetKwin each accel:", accel.Id, accel.Keystrokes, accel.DefaultKeystrokes)
		if !strv.Strv(accel.Keystrokes).Equal(accel.DefaultKeystrokes) &&
			len(accel.DefaultKeystrokes) > 0 && accel.DefaultKeystrokes[0] != "" {
			accelJson, err := util.MarshalJSON(&util.KWinAccel{
				Id:         accel.Id,
				Keystrokes: accel.DefaultKeystrokes,
			})
			if err != nil {
				logger.Warning(err)
				continue
			}
			logger.Debug("resetKwin SetAccel", accelJson)
			ok, err := wmObj.SetAccel(0, accelJson)
			// 目前 wm 的实现，调用 SetAccel 如果遇到冲突情况，会导致目标快捷键被清空。
			if !ok {
				logger.Warning("wm.SetAccel failed, id: ", accel.Id)
				continue
			}
			if err != nil {
				logger.Warning("failed to set accel:", err, accel.Id)
				continue
			}
		}
	}
	return nil
}

// showOSD 显示屏幕提示信息
// showOSD - 显示屏幕提示信息
// 参数: signal - 要显示的提示信号(如"CapsLockOn")
func showOSD(signal string) {
	logger.Debug("show OSD", signal)
	sessionDBus, _ := dbus.SessionBus()
	go sessionDBus.Object("com.deepin.dde.osd", "/").Call("com.deepin.dde.osd.ShowOSD", 0, signal)
}

const sessionManagerDest = "com.deepin.SessionManager"
const sessionManagerObjPath = "/com/deepin/SessionManager"

// systemLock 锁定系统
// systemLock - 锁定系统
func systemLock() {
	sessionDBus, err := dbus.SessionBus()
	if err != nil {
		logger.Warning(err)
		return
	}
	go sessionDBus.Object(sessionManagerDest, sessionManagerObjPath).Call(sessionManagerDest+".RequestLock", 0)
}

// canSuspend 检查系统是否支持待机
// canSuspend - 检查系统是否支持待机
// 返回值: 是否支持待机
func (m *Manager) canSuspend() bool {
	can, err := m.sessionManager.CanSuspend(0) // 当前能否待机
	if err != nil {
		logger.Warning(err)
		return false
	}
	return can
}

// systemSuspend 执行系统待机操作
func (m *Manager) systemSuspend() {
	if !m.canSuspend() {
		logger.Info("can not suspend")
		return
	}

	logger.Debug("suspend")
	err := m.sessionManager.RequestSuspend(0)
	if err != nil {
		logger.Warning("failed to suspend:", err)
	}
}

// systemSuspendByFront 通过前端界面执行待机操作
// 解决待机闪屏问题，前端会在待机前显示纯黑界面
// systemSuspendByFront - 通过前端界面执行待机操作
// 解决待机闪屏问题，前端会在待机前显示纯黑界面
func (m *Manager) systemSuspendByFront() {
	if !m.canExcuteSuspendOrHiberate || m.prepareForSleep {
		logger.Info("Avoid waking up and immediately going into suspend.")
		return
	}

	if !m.canSuspend() {
		logger.Info("can not suspend")
		return
	}

	logger.Debug("suspend")
	err := m.shutdownFront.Suspend(0)
	if err != nil {
		logger.Warning("failed to suspend:", err)
	}
}

// canHibernate 检查系统是否支持休眠
// canHibernate - 检查系统是否支持休眠
// 返回值: 是否支持休眠
func (m *Manager) canHibernate() bool {
	can, err := m.sessionManager.CanHibernate(0) // 能否休眠
	if err != nil {
		logger.Warning(err)
		return false
	}
	return can
}

// systemSuspend 系统级待机操作
// systemSuspend - 执行系统待机操作
func systemSuspend() {
	sessionDBus, _ := dbus.SessionBus()
	go sessionDBus.Object(sessionManagerDest, sessionManagerObjPath).Call(sessionManagerDest+".RequestSuspend", 0)
}

// systemHibernate 执行系统休眠操作
// systemHibernate - 执行系统休眠操作
func (m *Manager) systemHibernate() {
	if !m.canHibernate() {
		logger.Info("can not Hibernate")
		return
	}

	logger.Debug("Hibernate")
	err := m.sessionManager.RequestHibernate(0)
	if err != nil {
		logger.Warning("failed to Hibernate:", err)
	}
}

// systemHibernateByFront 通过前端界面执行休眠操作
// systemHibernateByFront - 通过前端界面执行休眠操作
func (m *Manager) systemHibernateByFront() {
	if !m.canExcuteSuspendOrHiberate || m.prepareForSleep {
		logger.Info("Avoid waking up and immediately going into suspend.")
		return
	}

	if !m.canHibernate() {
		logger.Info("can not Hibernate")
		return
	}

	logger.Debug("Hibernate")
	err := m.shutdownFront.Hibernate(0)
	if err != nil {
		logger.Warning("failed to Hibernate:", err)
	}
}

// canShutdown 检查系统是否支持关机
// canShutdown - 检查系统是否支持关机
// 返回值: 是否支持关机
func (m *Manager) canShutdown() bool {
	can, err := m.sessionManager.CanShutdown(0) // 当前能否关机
	if err != nil {
		logger.Warning(err)
		return false
	}
	return can
}

// hasShutdownInhibit 检查是否有阻止关机的进程
// hasShutdownInhibit - 检查是否有阻止关机的进程
// 返回值: 是否有阻止关机的进程
func (m *Manager) hasShutdownInhibit() bool {
	// 先检查是否有delay 或 block shutdown的inhibitor
	inhibitors, err := m.login1Manager.ListInhibitors(0)
	if err != nil {
		logger.Warning("failed to call login ListInhibitors:", err)
	} else {
		for _, inhibit := range inhibitors {
			logger.Infof("inhibit is: %+v", inhibit)
			if strings.Contains(inhibit.What, "shutdown") {
				return true
			}
		}
	}
	return false
}

// hasMultipleDisplaySession 检查是否有多个图形会话
// hasMultipleDisplaySession - 检查是否有多个图形会话
// 返回值: 是否有多个图形会话
func (m *Manager) hasMultipleDisplaySession() bool {
	// 检查是否有多个图形session,有多个图形session就需要显示阻塞界面
	sessions, err := m.displayManager.Sessions().Get(0)
	if err != nil {
		logger.Warning(err)
		return false
	}
	return len(sessions) >= 2
}

// systemShutdown 执行系统关机操作
// systemShutdown - 执行系统关机操作
// 会根据系统状态决定是否显示关机界面
func (m *Manager) systemShutdown() {
	// 如果是锁定状态，那么不需要后端进行关机响应，前端会显示关机或者关机阻塞界面；
	// 快捷键关机流程：判断是否存在多用户或任何shutdown阻塞项(block or delay),存在则跳转dde-shutdown界面，不存在进行sessionManager的注销
	if !m.canShutdown() {
		logger.Info("can not Shutdown")
	}

	locked, err := m.sessionManager.Locked().Get(0)
	if err != nil {
		logger.Warning("sessionManager get locked state error:", err)
		return
	}
	if locked {
		logger.Info("current session is locked")
		return
	}

	if m.hasShutdownInhibit() || m.hasMultipleDisplaySession() {
		logger.Info("exist shutdown inhibit(delay or block) or multiple display session")
		err := m.shutdownFront.Shutdown(0)
		if err != nil {
			logger.Warning(err)
		}
	} else {
		logger.Debug("keybinding start request SessionManager shutdown")
		err := m.sessionManager.RequestShutdown(0)
		if err != nil {
			logger.Warning("failed to Shutdown:", err)
		}
	}
}

// checkDPMSCapable 检查系统是否支持DPMS（显示器电源管理）
// checkDPMSCapable - 检查系统是否支持DPMS(显示器电源管理)
func (m *Manager) checkDPMSCapable() {
	version, err := dpms.GetVersion(m.conn, 1, 1).Reply(m.conn)
	if err != nil {
		logger.Warning(err)
		return
	} else {
		logger.Debugf("dpms ext version %d.%d", version.ServerMajorVersion,
			version.ServerMinorVersion)
	}

	capable, err := dpms.Capable(m.conn).Reply(m.conn)
	if err != nil {
		logger.Warning(err)
		return
	}

	m.capableOfDPMS = capable.Capable
}

// isDPMSOff 检查显示器是否已关闭
// isDPMSOff - 检查显示器是否已关闭
// 返回值: 显示器是否已关闭
func (m *Manager) isDPMSOff() bool {
	if m.capableOfDPMS {
		info, err := dpms.Info(m.conn).Reply(m.conn)
		if err != nil {
			logger.Warning(err)
			return false
		}

		return info.State && info.PowerLevel != dpms.DPMSModeOn
	}

	return false
}

// systemTurnOffScreen 关闭显示器屏幕
// systemTurnOffScreen - 关闭显示器屏幕
// 会处理锁定状态下的屏幕关闭逻辑
func (m *Manager) systemTurnOffScreen() {
	if time.Now().Sub(m.lastDPMSOffTime) < time.Second {
		logger.Info("========= ignore ")
		return
	}
	const settingKeyScreenBlackLock = "screen-black-lock"
	logger.Info("DPMS Off")
	var err error
	var useWayland bool
	if len(os.Getenv("WAYLAND_DISPLAY")) != 0 {
		useWayland = true
	} else {
		useWayland = false
	}

	bScreenBlackLock := m.gsPower.GetBoolean(settingKeyScreenBlackLock)
	if bScreenBlackLock {
		// 等待lock界面显示，再熄屏防止闪屏
		visible, err := m.lockFront.Visible().Get(0)
		if err != nil {
			logger.Warning(err)
		}

		if !visible {
			// 标记需要等待visible状态
			atomic.StoreInt64(&m.waitLockVisibleFlag, 1)
			m.doLock(true)

			select {
			case <-m.waitLockVisibleChan:
				logger.Info("lock is visible")
				// FIXME: 这个属性也不是太可靠
				time.Sleep(500 * time.Millisecond)
			case <-time.After(3 * time.Second):
				if atomic.SwapInt64(&m.waitLockVisibleFlag, 0) == 0 {
					// 已经发送了visible状态，需要吃掉
					<-m.waitLockVisibleChan
				} else {
					logger.Warning("wait lock time out")
				}
			}
		}
	}

	doPrepareSuspend()

	if useWayland {
		err = exec.Command("dde_wldpms", "-s", "Off").Run()
	} else {
		err = dpms.ForceLevelChecked(m.conn, dpms.DPMSModeOff).Check(m.conn)
	}
	if err != nil {
		logger.Warning("Set DPMS off error:", err)
	}

	if bScreenBlackLock && m.isWmBlackScreenActive() {
		m.setWmBlackScreenActive(false)
	}
	undoPrepareSuspend()
	ioutil.WriteFile("/tmp/dpms-state", []byte("1"), 0644)

	// 记录最近一次DPMS off 的状态
	go func() {
		time.Sleep(time.Second)
		for m.isDPMSOff() {
			m.lastDPMSOffTime = time.Now()
			time.Sleep(500 * time.Millisecond)
		}
	}()
}

// systemLogout 执行系统注销操作
// systemLogout - 执行系统注销操作
func (m *Manager) systemLogout() {
	can, err := m.sessionManager.CanLogout(0)
	if err != nil {
		logger.Warning(err)
		return
	}

	if !can {
		logger.Info("can not logout")
		return
	}

	logger.Debug("logout")
	err = m.sessionManager.RequestLogout(0)
	if err != nil {
		logger.Warning("failed to logout:", err)
	}
}

// systemAway 锁定系统（离开模式）
// systemAway - 锁定系统(离开模式)
func (m *Manager) systemAway() {
	err := m.sessionManager.RequestLock(0)
	if err != nil {
		logger.Warning(err)
	}
}

// queryCommandByMime 根据MIME类型查询默认打开程序
// queryCommandByMime - 根据MIME类型查询默认打开程序
// 参数: mime - MIME类型(如"text/plain")
// 返回值: 默认打开程序的执行命令
func queryCommandByMime(mime string) string {
	app := gio.AppInfoGetDefaultForType(mime, false)
	if app == nil {
		return ""
	}
	defer app.Unref()

	return app.GetExecutable()
}

// getRfkillWlanState 获取无线网卡硬件开关状态
// getRfkillWlanState - 获取无线网卡硬件开关状态
// 返回值:
//   int - 状态值(0:解除阻塞, 1:阻塞)
//   error - 错误信息
func getRfkillWlanState() (int, error) {
	dir := "/sys/class/rfkill"
	fileInfoList, err := ioutil.ReadDir(dir)
	if err != nil {
		return 0, err
	}

	for _, fileInfo := range fileInfoList {
		typeFile := filepath.Join(dir, fileInfo.Name(), "type")
		typeBytes, err := readTinyFile(typeFile)
		if err != nil {
			continue
		}
		if bytes.Equal(bytes.TrimSpace(typeBytes), []byte("wlan")) {
			stateFile := filepath.Join(dir, fileInfo.Name(), "state")
			stateBytes, err := readTinyFile(stateFile)
			if err != nil {
				return 0, err
			}
			stateBytes = bytes.TrimSpace(stateBytes)
			state, err := strconv.Atoi(string(stateBytes))
			if err != nil {
				return 0, err
			}
			return state, nil

		}
	}
	return 0, errors.New("not found rfkill with type wlan")
}

// readTinyFile 读取小文件内容
// readTinyFile - 读取小文件内容(小于8字节)
// 参数: file - 文件路径
// 返回值: 文件内容
func readTinyFile(file string) ([]byte, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	buf := make([]byte, 8)
	n, err := f.Read(buf)
	if err != nil {
		return nil, err
	}
	return buf[:n], nil
}

// shouldUseDDEKwin 检查是否使用DDE KWin
// shouldUseDDEKwin - 检查是否使用DDE KWin
// 返回值: 是否使用DDE KWin
func shouldUseDDEKwin() bool {
	_, err := os.Stat("/usr/bin/kwin_no_scale")
	return err == nil
}

// doLock 执行锁定操作
// doLock - 执行锁定操作
// 参数: autoStartAuth - 是否自动启动认证
func (m *Manager) doLock(autoStartAuth bool) {
	logger.Info("Lock Screen")
	err := m.lockFront.ShowAuth(0, autoStartAuth)
	if err != nil {
		logger.Warning("failed to call lockFront ShowAuth:", err)
	}
}

// doPrepareSuspend 准备待机操作
// doPrepareSuspend - 准备待机操作
// 通知电源管理模块准备进入待机状态
func doPrepareSuspend() {
	sessionDBus, _ := dbus.SessionBus()
	obj := sessionDBus.Object("com.deepin.daemon.Power", "/com/deepin/daemon/Power")
	err := obj.Call("com.deepin.daemon.Power.SetPrepareSuspend", 0, suspendStateButtonClick).Err
	if err != nil {
		logger.Warning(err)
	}
}

// undoPrepareSuspend 取消待机准备
// undoPrepareSuspend - 取消待机准备
// 通知电源管理模块取消待机准备
func undoPrepareSuspend() {
	sessionDBus, _ := dbus.SessionBus()
	obj := sessionDBus.Object("com.deepin.daemon.Power", "/com/deepin/daemon/Power")
	err := obj.Call("com.deepin.daemon.Power.SetPrepareSuspend", 0, suspendStateFinish).Err
	if err != nil {
		logger.Warning(err)
	}
}
