/* -*- 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 "VolumeControl.h"
#include <iostream>
#include <future>

#include "CustomAudioVolumeJson.h"
#include "JackDetect.h"

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

void VolumeControl::init()
{
    // 1. get backend

    // 2. created backend factory
    switch (m_type) {
    case BackendType::PIPEWIRE: {
        m_pBackendFactory = std::make_shared<PipewireBackendFactory>();
        m_pManagerFactory = std::make_shared<PipewireManagerFactory>();
        break;
    }
    case BackendType::PULSEAUDIO: {
        m_pBackendFactory = std::make_shared<PulseaudioBackendFactory>();
        m_pManagerFactory = std::make_shared<PulseaudioManagerFactory>();
        break;
    }
    case BackendType::ALSA:
    case BackendType::JACK:
    default:
        AUDIO_ERROR("Can not support {} backend, exit.", getBackendName(m_type));
        return;
    }

    // 3. created backend
    m_pBackend = m_pBackendFactory->createBackend();
    m_pAudioManager = m_pManagerFactory->createManager();
    m_pAudioManager->init();
    m_pAudioManager->connect();

    AudioMethod::getInstance().setManager(m_pAudioManager);
    AudioMethod::getInstance().init();

    auto moduleList = m_pAudioManager->getModuleList();
    AUDIO_DEBUG("The size of the loaded module is {}.", moduleList.size());
    std::thread([this, moduleList]() {
        for (const auto& [k, v] : m_settingsMap) {
            bool status = m_statusFunctions[v[0]]();

            if (!status)
                continue;

            auto it = std::find_if(moduleList.begin(), moduleList.end(), [v](const std::string& name) {
                return name == v[1];
            });
            if (it == moduleList.end()) {
                AudioMethod::getInstance().loadModule(v[1], v[2]);
            }
            else {
                AUDIO_DEBUG("Module {} is loaded.", v[1]);
            }
        }
    }).detach();

    JackDetect monitor([](const std::string& card,
                        const std::string& jack,
                        AlsaJackType type,
                        bool plugged) {
        AUDIO_DEBUG("Card: {} jack: {} plugged: {}.", card, jack, plugged);
        if (plugged)
            return;

        if (type == AlsaJackType::ALSA_JACK_FRONTMIC ||
            type == AlsaJackType::ALSA_JACK_REARMIC ||
            type == AlsaJackType::ALSA_JACK_LINEIN ||
            type == AlsaJackType::ALSA_JACK_HEADPHONEMIC ||
            type == AlsaJackType::ALSA_JACK_INPUTMIC) {
            auto list = AudioMethod::getInstance().getAvailablePortList(NodeType::INPUT);
            if (list.size() <= 1) {
                AUDIO_DEBUG("{} jack unplugin, unload module-echo-cancel.", jack);
                AudioMethod::getInstance().unloadModule("module-echo-cancel");
            }
        }
        else if (type == AlsaJackType::ALSA_JACK_HEADPHONE ||
                 type == AlsaJackType::ALSA_JACK_HEADPHONE2 ||
                 type == AlsaJackType::ALSA_JACK_LINEOUT) {
            auto list = AudioMethod::getInstance().getAvailablePortList(NodeType::OUTPUT);
            if (list.size() <= 1) {
                AUDIO_DEBUG("{} jack unplugin, unload module-remap-sink.", jack);
                AudioMethod::getInstance().unloadModule("module-remap-sink");
            }
        }
    });

    monitor.start();

    AudioMethod::getInstance().run();
}
}
