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

package keybinding

import (
	"errors"
	"strings"

	"github.com/godbus/dbus"
	. "github.com/linuxdeepin/dde-daemon/keybinding/shortcuts"
	audio "github.com/linuxdeepin/go-dbus-factory/com.deepin.daemon.audio"
	backlight "github.com/linuxdeepin/go-dbus-factory/com.deepin.daemon.helper.backlight"
	"github.com/linuxdeepin/go-gir/gio-2.0"
)

const (
	volumeMin = 0    // 最小音量值
	volumeMax = 1.5  // 最大音量值
)

const (
	gsKeyOsdAdjustVolState = "osd-adjust-volume-enabled"
)

// OSD音量调节状态类型
type OsdVolumeState int32

// Osd音量调节控制
const (
	VolumeAdjustEnable OsdVolumeState = iota
	VolumeAdjustForbidden
	VolumeAdjustHidden
)

// 音频控制器结构体
type AudioController struct {
	conn                   *dbus.Conn              // D-Bus连接
	audioDaemon            audio.Audio              // 音频服务接口
	huaweiMicLedWorkaround *huaweiMicLedWorkaround // 华为麦克风LED修复方案
	gsKeyboard             *gio.Settings           // 键盘设置
}

// 创建新的音频控制器
func NewAudioController(sessionConn *dbus.Conn,
	backlightHelper backlight.Backlight) *AudioController {
	c := &AudioController{
		conn:        sessionConn,                      // 使用传入的D-Bus连接
		audioDaemon: audio.NewAudio(sessionConn),       // 初始化音频服务接口
	}
	c.initHuaweiMicLedWorkaround(backlightHelper)     // 初始化华为麦克风LED修复方案
	c.gsKeyboard = gio.NewSettings(gsSchemaKeyboard) // 初始化键盘设置
	return c
}

// 销毁音频控制器
func (c *AudioController) Destroy() {
	if c.huaweiMicLedWorkaround != nil {
		c.huaweiMicLedWorkaround.destroy() // 销毁华为麦克风LED修复方案
		c.huaweiMicLedWorkaround = nil
	}
}

func (*AudioController) Name() string {
	return "Audio"
}

func (c *AudioController) ExecCmd(cmd ActionCmd) error {
	go func() {
		var err error
		switch cmd {
		case AudioSinkMuteToggle:
			err = c.toggleSinkMute()
		case AudioSinkVolumeUp:
			err = c.changeSinkVolume(true)
		case AudioSinkVolumeDown:
			err = c.changeSinkVolume(false)
		case AudioSourceMuteToggle:
			err = c.toggleSourceMute()
		default:
			err = ErrInvalidActionCmd{cmd}
		}

		if err != nil {
			logger.Warning("AudioController exec cmd error:", err)
		}
	}()

	return nil
}

func (c *AudioController) toggleSinkMute() error {
	var osd string
	var state = OsdVolumeState(c.gsKeyboard.GetEnum(gsKeyOsdAdjustVolState))

	// 当OsdAdjustVolumeState的值为VolumeAdjustEnable时，才会去执行静音操作
	if VolumeAdjustEnable == state {
		sink, err := c.getDefaultSink()
		if err != nil {
			return err
		}

		mute, err := sink.Mute().Get(0)
		if err != nil {
			return err
		}

		err = sink.SetMute(0, !mute)
		if err != nil {
			return err
		}
		osd = "AudioMute"
	} else if VolumeAdjustForbidden == state {
		osd = "AudioMuteAsh"
	} else {
		return nil
	}

	showOSD(osd)
	return nil
}

func (c *AudioController) toggleSourceMute() error {
	var osd string
	var state = OsdVolumeState(c.gsKeyboard.GetEnum(gsKeyOsdAdjustVolState))

	source, err := c.getDefaultSource()
	if err != nil {
		return err
	}

	mute, err := source.Mute().Get(0)
	if err != nil {
		return err
	}
	mute = !mute

	// 当OsdAdjustVolumeState的值为VolumeAdjustEnable时，才会去执行静音操作
	if VolumeAdjustEnable == state {
		err = source.SetMute(0, mute)
		if err != nil {
			return err
		}

		if mute {
			osd = "AudioMicMuteOn"
		} else {
			osd = "AudioMicMuteOff"
		}
	} else if VolumeAdjustForbidden == state {
		if mute {
			osd = "AudioMicMuteOnAsh"
		} else {
			osd = "AudioMicMuteOffAsh"
		}
	} else {
		return nil
	}

	showOSD(osd)
	return nil
}

func (c *AudioController) changeSinkVolume(raised bool) error {
	var osd string
	var state = OsdVolumeState(c.gsKeyboard.GetEnum(gsKeyOsdAdjustVolState))

	// 当OsdAdjustVolumeState的值为VolumeAdjustEnable时，才会去执行调节音量的操作
	if VolumeAdjustEnable == state {
		sink, err := c.getDefaultSink()
		if err != nil {
			return err
		}

		osd = "AudioUp"
		v, err := sink.Volume().Get(0)
		if err != nil {
			return err
		}

		var step = 0.05
		if !raised {
			step = -step
			osd = "AudioDown"
		}

		maxVolume, err := c.audioDaemon.MaxUIVolume().Get(0)
		if err != nil {
			logger.Warning(err)
			maxVolume = volumeMax
		}

		v += step
		if v < volumeMin {
			v = volumeMin
		} else if v > maxVolume {
			v = maxVolume
		}

		logger.Debug("[changeSinkVolume] will set volume to:", v)
		mute, err := sink.Mute().Get(0)
		if err != nil {
			return err
		}

		if mute {
			err = sink.SetMute(0, false)
			if err != nil {
				logger.Warning(err)
			}
		}

		err = sink.SetVolume(0, v, true)
		if err != nil {
			return err
		}
	} else if VolumeAdjustForbidden == state {
		if raised {
			osd = "AudioUpAsh"
		} else {
			osd = "AudioDownAsh"
		}
	} else {
		return nil
	}

	showOSD(osd)
	return nil
}

func (c *AudioController) getDefaultSink() (audio.Sink, error) {
	sinkPath, err := c.audioDaemon.DefaultSink().Get(0)
	if err != nil {
		return nil, err
	}

	sink, err := audio.NewSink(c.conn, sinkPath)
	if err != nil {
		return nil, err
	}
	name, err := sink.Name().Get(0)
	if err != nil {
		return nil, err
	}
	ports, err := sink.Ports().Get(0)
	if err != nil {
		return nil, err
	}
	if len(ports) == 0 && strings.Contains(name, "auto_null") {
		return nil, errors.New("default sink (auto_null) is invalid")
	}

	return sink, nil
}

func (c *AudioController) getDefaultSource() (audio.Source, error) {
	sourcePath, err := c.audioDaemon.DefaultSource().Get(0)
	if err != nil {
		return nil, err
	}
	source, err := audio.NewSource(c.conn, sourcePath)
	if err != nil {
		return nil, err
	}
	name, err := source.Name().Get(0)
	if err != nil {
		return nil, err
	}
	ports, err := source.Ports().Get(0)
	if err != nil {
		return nil, err
	}
	if len(ports) == 0 && strings.Contains(name, "auto_null") {
		return nil, errors.New("default source (auto_null) is invalid")
	}

	return source, nil
}
