/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 4 -*-
 * -*- coding: utf-8 -*-
 *
 * Copyright (C) 2025 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "MultiAudioCombineModule.h"

#include "AudioMethod.h"
#include "AudioContext.h"

namespace UkuiAudioFramwork
{
MultiAudioCombineModule& MultiAudioCombineModule::getInstance()
{
    static MultiAudioCombineModule instance;
    return instance;
}

void MultiAudioCombineModule::setJson(std::shared_ptr<MultiAudioCombineJson> json)
{
    m_pJson = json;
}


bool MultiAudioCombineModule::getMultiAudioCombineStatus(const NodeType& type) const
{
    auto str = EnumToInt(type) ? "output-combine" : "input-combine";
    auto status = m_pJson->getValue(str, "status");

    if (status)
        return *status;

    AUDIO_ERROR("Get {} type multi audio combine status failed.", EnumToInt(type));
    return false;
}

void MultiAudioCombineModule::setMultiAudioCombine(const NodeType& type, bool status, const std::vector<std::string>& list)
{
    if (NodeType::OUTPUT != type) {
        AUDIO_ERROR("Devices with combined {} types are not currently supported.", EnumToInt(type));
        return;
    }

    MultiAudioCombineInfo info;
    info.type = EnumToInt(type);
    info.status = status;
    m_pJson->setValue("output-combine", "status", status);

    if (status) {
        auto devList = AudioMethod::getInstance().getSinkList();
        std::vector<std::string> combineDevList {};

        for (const auto& dev : devList) {
            if (std::string::npos != dev.name.find("a2dp_sink")) {
                combineDevList.push_back(dev.name);
            }
        }

        /**
         * When setting the Bluetooth combined output device, when waking up from sleep or actively
         * reconnecting Bluetooth, since Bluetooth is not sure when to connect to the Bluetooth audio,
         * a reset operation is performed here. If the setting fails to take effect after more than 3 times,
         * it means that the number of connected Bluetooth audios is less than 2 and cannot be combined output.
         */
        if (combineDevList.size() < 2) {
            if (m_retryTimes >= 3) {
                AUDIO_ERROR("Set the number of combinations to more than {} times, Unable to combine Bluetooth devices with device number {}, "
                            "Please check the number of Bluetooth devices connected.", m_retryTimes, combineDevList.size());
                m_retryTimes = 0;
                return;
            }

            std::thread([type, status, list, combineDevList, this]() {
                std::this_thread::sleep_for(std::chrono::milliseconds(1000));
                ++m_retryTimes;
                AUDIO_WARN("Failed to set the combined output device. The currently connected device is less than 2. Trying to "
                           "reset the combined output device for the {} time", m_retryTimes);
                setMultiAudioCombine(type, status, list);
            }).detach();
            return;
        }

        std::string param = "slaves=";
        param.append(combineDevList.at(0));
        param.append(",");
        param.append(combineDevList.at(1));

        AUDIO_DEBUG("Turn on the combined device function with {}.", param);
        AudioMethod::getInstance().loadModule("module-combine-sink", param);

        info.deviceList = combineDevList;
        m_pJson->insert(info);
    }
    else {
        // 卸载组合输出模块
        m_pJson->insert(info);
        AUDIO_DEBUG("Turn off the combined device function.");
        AudioMethod::getInstance().unloadModule("module-combine-sink");
        AudioMethod::getInstance().deviceAdjust(NodeType::OUTPUT);
    }
}
}
