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

package audio

import (
	"fmt"
	"math"
	"strconv"
	"sync"

	dbus "github.com/godbus/dbus"
	"github.com/linuxdeepin/go-lib/dbusutil"
	"github.com/linuxdeepin/go-lib/pulse"
)

// Sink 音频输出设备结构体
// 代表一个音频输出设备，如扬声器、耳机等
type Sink struct {
	audio   *Audio
	service *dbusutil.Service
	PropsMu sync.RWMutex
	index   uint32

	Name        string // 设备名称
	Description string // 设备描述

	// 默认音量值
	BaseVolume float64

	// 是否静音
	Mute bool

	// 当前音量
	Volume     float64 // 当前音量值
	cVolume    pulse.CVolume // 脉冲音频音量对象
	channelMap pulse.ChannelMap // 声道映射
	// 左右声道平衡值
	Balance float64
	// 是否支持左右声道调整
	SupportBalance bool
	// 前后声道平衡值
	Fade float64
	// 是否支持前后声道调整
	SupportFade bool

	// dbusutil-gen: equal=portsEqual
	// 支持的输出端口
	Ports []Port
	// 当前使用的输出端口
	ActivePort Port
	// 声卡的索引
	Card uint32

	props map[string]string // 设备属性

	// 当前是否是可插拔sink
	pluggable bool

	dsgSinkVolumePercentage float64 // 输出音量增幅配置
}

// newSink 创建新的输出设备实例
// sinkInfo: 脉冲音频输出设备信息
// audio: 音频管理实例
func newSink(sinkInfo *pulse.Sink, audio *Audio) *Sink {
	s := &Sink{
		audio:   audio,
		service: audio.service,
		index:   sinkInfo.Index,
		props:   sinkInfo.PropList,
	}
	if audio.dsgSinkVolumePercentage < 0.5 || audio.dsgSinkVolumePercentage > 1.0 {
		logger.Warning("newSink not in [0.5, 1.0] use 1.0, invalid value,: ", audio.dsgSinkVolumePercentage)
		audio.dsgSinkVolumePercentage = 1.0
	}
	s.dsgSinkVolumePercentage = audio.dsgSinkVolumePercentage
	s.update(sinkInfo)
	return s
}

// CheckPort 检测端口是否被禁用
// 检查当前活动端口是否可用
func (s *Sink) CheckPort() *dbus.Error {
	enabled, err := s.audio.IsPortEnabled(s.Card, s.ActivePort.Name)
	if err != nil {
		return err
	}

	if !enabled {
		return dbusutil.ToError(fmt.Errorf("port<%d:%s> is disabled", s.Card, s.ActivePort.Name))
	}

	return nil
}

func (s *Sink) SetDsgSinkVolumePercentage(value float64) {
	logger.Info("SetDsgSinkVolumePercentage dsg value : ", value)
	if value < 0.5 || value > 1.0 {
		logger.Warning("SetDsgSinkVolumePercentage not in [0.5, 1.0] use 1.0, invalid value,: ", s.dsgSinkVolumePercentage)
		value = 1.0
	}

	if s.dsgSinkVolumePercentage != value {
		s.dsgSinkVolumePercentage = value
	}

	// 使用浮点数比较函数，避免精度问题
	if !float64Equal(value, 1.0) {
		s.SetVolume(s.Volume, false)
	}
    logger.Info("SetDsgSinkVolumePercentage s.Volume : ", s.Volume, value)
}

// SetVolume 设置音量大小
// value: 音量大小 (0.0 - 1.0)
// isPlay: 是否播放声音反馈
func (s *Sink) SetVolume(value float64, isPlay bool) *dbus.Error {
	logger.Infof("dbus call SetVolume with value %f and isPlay %t, the sink name is %s", value, isPlay, s.Name)

	err := s.CheckPort()
	if err != nil {
		logger.Warning(err.Body...)
		return err
	}

	if !isVolumeValid(value) {
		err1 := fmt.Errorf("invalid volume value: %v", value)
		logger.Warning(err1)
		return dbusutil.ToError(err1)
	}

	if value == 0 {
		value = 0.001
		s.SetMute(true)
	} else if s.Mute {
		s.SetMute(false)
	}

	actualValue := value * s.dsgSinkVolumePercentage
	logger.Debugf("SetVolume value : %v, actualValue : %v", value, actualValue)
	s.PropsMu.Lock()
	cv := s.cVolume.SetAvg(actualValue)
	s.PropsMu.Unlock()

	if s.audio != nil && s.audio.context() != nil {
		s.audio.context().SetSinkVolumeByIndex(s.index, cv)
	}

	GetConfigKeeper().SetVolume(s.audio.getCardNameById(s.Card), s.ActivePort.Name, actualValue)

	if isPlay {
		s.playFeedback()
	}
	return nil
}

// SetBalance 设置左右声道平衡值
// value: 声道平衡值 (-1.0 到 1.0)
// isPlay: 是否播放声音反馈
func (s *Sink) SetBalance(value float64, isPlay bool) *dbus.Error {
	logger.Infof("dbus call SetBalance with value %f and isPlay %t, the sink name is %s", value, isPlay, s.Name)

	err := s.CheckPort()
	if err != nil {
		logger.Warning(err.Body...)
		return err
	}

	if value < -1.00 || value > 1.00 {
		err1 := fmt.Errorf("invalid volume value: %v", value)
		logger.Warning(err1)
		return dbusutil.ToError(err1)
	}

	s.PropsMu.RLock()
	cv := s.cVolume.SetBalance(s.channelMap, value)
	s.PropsMu.RUnlock()
	s.audio.context().SetSinkVolumeByIndex(s.index, cv)

	if s.audio.defaultSink != nil && s.audio.defaultSink.Name == s.Name {
		s.audio.defaultSinkBalance = value
	}

	GetConfigKeeper().SetBalance(s.audio.getCardNameById(s.Card), s.ActivePort.Name, value)

	if isPlay {
		s.playFeedback()
	}
	return nil
}

// SetFade 设置前后声道平衡值
// value: 声道平衡值 (-1.0 到 1.0)
func (s *Sink) SetFade(value float64) *dbus.Error {
	logger.Infof("dbus call SetFade with value %f, the sink name is %s", value, s.Name)

	err := s.CheckPort()
	if err != nil {
		logger.Warning(err.Body...)
		return err
	}

	if value < -1.00 || value > 1.00 {
		err1 := fmt.Errorf("invalid volume value: %v", value)
		logger.Warning(err1)
		return dbusutil.ToError(err1)
	}

	s.PropsMu.RLock()
	cv := s.cVolume.SetFade(s.channelMap, value)
	s.PropsMu.RUnlock()
	s.audio.context().SetSinkVolumeByIndex(s.index, cv)
	s.playFeedback()
	return nil
}

// setVBF 设置同时设置音量和平衡
// v: volume音量值
// b: balance左右平衡值
// f: fade前后平衡值
func (s *Sink) setVBF(v, b, f float64) *dbus.Error {
	if !isVolumeValid(v) {
		return dbusutil.ToError(fmt.Errorf("invalid volume value: %v", v))
	}

	if b < -1.00 || b > 1.00 {
		return dbusutil.ToError(fmt.Errorf("invalid balance value: %v", b))
	}

	if f < -1.00 || b > 1.00 {
		return dbusutil.ToError(fmt.Errorf("invalid fade value: %v", f))
	}

	s.PropsMu.RLock()
	cv := s.cVolume.SetAvg(v)
	cv = cv.SetBalance(s.channelMap, b)
	cv = cv.SetFade(s.channelMap, f)
	s.PropsMu.RUnlock()
	s.audio.context().SetSinkVolumeByIndex(s.index, cv)

	return nil
}

// 是否静音
func (s *Sink) SetMute(value bool) *dbus.Error {
	logger.Infof("dbus call SetMute with value %t, the sink name is %s", value, s.Name)

	err := s.CheckPort()
	if err != nil {
		logger.Warning(err.Body...)
		return err
	}

	s.audio.context().SetSinkMuteByIndex(s.index, value)
	if s.audio.globalMuteEnabled {
		GetConfigKeeper().SetMuteOutput(value)
	} else {
		GetConfigKeeper().SetMute(s.audio.getCardNameById(s.Card), s.ActivePort.Name, value)
	}

	if !value {
		s.playFeedback()
	}
	return nil
}

// 设置此设备的当前使用端口
func (s *Sink) SetPort(name string) *dbus.Error {
	logger.Infof("dbus call SetPort with name %s, the sink name is %s", name, s.Name)

	s.audio.context().SetSinkPortByIndex(s.index, name)
	return nil
}

func (s *Sink) getPath() dbus.ObjectPath {
	return dbus.ObjectPath(dbusPath + "/Sink" + strconv.Itoa(int(s.index)))
}

func (*Sink) GetInterfaceName() string {
	return dbusInterface + ".Sink"
}

func (s *Sink) update(sinkInfo *pulse.Sink) {
	if s == nil {
		return
	}
	s.PropsMu.Lock()

	s.Name = sinkInfo.Name
	s.Description = sinkInfo.Description
	s.Card = sinkInfo.Card
	s.BaseVolume = sinkInfo.BaseVolume.ToPercent()
	s.cVolume = sinkInfo.Volume
	s.channelMap = sinkInfo.ChannelMap

	if (s.setPropMute(sinkInfo.Mute)) {
		s.audio.setMuteLED(nodeF1KeyLED, sinkInfo.Mute)
		s.audio.setMuteLED(nodeF8KeyLED, sinkInfo.Mute)
	}
	if s.dsgSinkVolumePercentage < 0.5 || s.dsgSinkVolumePercentage > 1.0 {
		logger.Warning("update not in [0.5, 1.0] use 1.0, invalid value,: ", s.dsgSinkVolumePercentage)
		s.dsgSinkVolumePercentage = 1.0
	}
	volume := floatPrecision(sinkInfo.Volume.Avg() / s.dsgSinkVolumePercentage)
	logger.Debug("### update ", sinkInfo.Volume.Avg(), s.dsgSinkVolumePercentage,  volume)
	s.setPropVolume(volume)

	s.setPropSupportFade(false)
	s.setPropFade(sinkInfo.Volume.Fade(sinkInfo.ChannelMap))
	s.setPropSupportBalance(true)

	//TODO BUG77028 判断激活Sink的平衡值，如果不与已保存的平衡值相等则更新为已保存的值
	updateBalance := sinkInfo.Volume.Balance(sinkInfo.ChannelMap)
	if s.audio.defaultSink != nil && s.audio.defaultSink.Name == sinkInfo.Name {
		var remainder = s.audio.defaultSinkBalance - updateBalance
		var isEqual = remainder <= 0.00001 && remainder >= -0.00001
		if !isEqual {
			cv := s.cVolume.SetBalance(s.channelMap, s.audio.defaultSinkBalance)
			s.audio.context().SetSinkVolumeByIndex(sinkInfo.Index, cv)
			updateBalance = s.audio.defaultSinkBalance
		}
	}
	s.setPropBalance(updateBalance)

	newActivePort := toPort(sinkInfo.ActivePort)
	var activePortChanged bool

	s.setPropPorts(toPorts(sinkInfo.Ports))
	activePortChanged = s.setPropActivePort(newActivePort)

	s.props = sinkInfo.PropList
	s.PropsMu.Unlock()

	if activePortChanged && s.audio.defaultSinkName == s.Name {
		logger.Debugf("default sink update active port %s", sinkInfo.ActivePort.Name)
		s.audio.resumeSinkConfig(s)
	}
}

func (s *Sink) GetMeter() (meter dbus.ObjectPath, busErr *dbus.Error) {
	//TODO
	return "/", nil
}

func (s *Sink) playFeedback() {
	s.PropsMu.RLock()
	name := s.Name
	s.PropsMu.RUnlock()
	playFeedbackWithDevice(name)
}

func (s *Sink) setMute(v bool) {
	logger.Debugf("Sink #%d setMute %v", s.index, v)
	s.audio.context().SetSinkMuteByIndex(s.index, v)
}

// float64Equal 安全比较两个float64值是否相等
// 使用epsilon比较，避免浮点数精度问题
func float64Equal(a, b float64) bool {
	const epsilon = 1e-9
	return math.Abs(a-b) <= epsilon
}
