/* -*- 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 "PaSinkInputStream.h"
#include "PaSinkStream.h"
#include "AudioMethod.h"

namespace UkuiAudioFramwork
{
void PaSinkInputStream::copy(const pa_sink_input_info& other)
{
    if (PA_INVALID_INDEX != index) {
        if (!pa_cvolume_equal(&volume, &other.volume) && index == other.index) {
            volumeChanged(paCvolumeToValue(other.volume));
        }

        if (mute != other.mute && index == other.index) {
            muteChanged(!mute);
        }
    }

    index = other.index;
    name = other.name;
    owner_module = other.owner_module;
    client = other.client;
    sink = other.sink;
    sample_spec = other.sample_spec;
    channel_map = other.channel_map;
    volume = other.volume;
    buffer_usec = other.buffer_usec;
    sink_usec = other.sink_usec;
    resample_method = other.resample_method ? other.resample_method : "";
    driver = other.driver;
    mute = other.mute;
    proplist = other.proplist;
    corked = other.corked;
    has_volume = other.has_volume;
    volume_writable = other.volume_writable;
    format = other.format;

    auto mediaRole = pa_proplist_gets(proplist, PA_PROP_MEDIA_ROLE);
    role = mediaRole ? mediaRole : "Playback Stream";
    auto iconName = pa_proplist_gets(proplist, PA_PROP_APPLICATION_ICON_NAME);
    m_appIconName = iconName ? iconName : "application-x-desktop";
    auto binary = pa_proplist_gets(proplist, PA_PROP_APPLICATION_PROCESS_BINARY);
    m_processBinary = binary ? binary : name;
}

std::string PaSinkInputStream::getMediaRole() const
{
    return role;
}

std::string PaSinkInputStream::getAppIconName() const
{
    return m_appIconName;
}

std::string PaSinkInputStream::getProcessBinary() const
{
    return m_processBinary;
}

std::string PaSinkInputStream::getDriver() const
{
    return driver;
}

uint32_t PaSinkInputStream::getSink() const
{
    return sink;
}

std::string PaSinkInputStream::toString() const
{
    std::string vol = "[";
    for (int i = 0; i < volume.channels; ++i) {
        vol += std::to_string(volume.values[i]);
        vol += ", ";
    }
    vol += "]";

    return FORMAT("name={}, index={}, owner_module={}, sink={}, driver={}, volume={}, mute={}",
                  name, index, owner_module, sink, driver, vol, mute);
}

NodeType PaSinkInputStream::getNodeType() const
{
    return NodeType::SINK_INPUT;
}

uint32_t PaSinkInputStream::getIndex() const
{
    return index;
}

std::string PaSinkInputStream::getName() const
{
    return name;
}

pa_proplist* PaSinkInputStream::getProplist() const
{
    return proplist;
}

uint32_t PaSinkInputStream::getCard() const
{
    // TODO
    return {};
}

void PaSinkInputStream::setVolume(int vol)
{
    auto v = volume;

    for (int i = 0; i < v.channels; ++i) {
        v.values[i] = vol;
    }

    if (pa_cvolume_equal(&v, &volume))
        return;

    pa_context_set_sink_input_volume(m_pContext, index, &v, nullptr, nullptr);
}

const pa_cvolume PaSinkInputStream::getVolume() const
{
    return volume;
}

void PaSinkInputStream::setBalance(double v)
{
    // TODO
}

double PaSinkInputStream::getBalance() const
{
    // TODO
    return {};
}

void PaSinkInputStream::setMute(bool mute)
{
    pa_context_set_sink_input_mute(m_pContext, index, mute, nullptr, nullptr);
}

bool PaSinkInputStream::getMute() const
{
    return mute;
}

DefaultDeviceInfo PaSinkInputStream::getDefaultDevice() const
{
    DefaultDeviceInfo info;
    std::shared_ptr<IStream> s = nullptr;
    for (const auto& c : m_info.cardInfoList) {
        auto it = std::find_if(c->sinks.begin(), c->sinks.end(), [this](const std::shared_ptr<IStream>& stream) {
            return sink == stream->getIndex();
        });
        if (it != c->sinks.end()) {
            s = *it;
            break;
        }
    }

    if (s) {
        info.idx = s->getIndex();
        info.name = s->getName();
        info.cardName = std::dynamic_pointer_cast<PaSinkStream>(s)->getCardName();
        info.activePortName = std::dynamic_pointer_cast<PaSinkStream>(s)->getActivePortName();
        info.activePortLabel = std::dynamic_pointer_cast<PaSinkStream>(s)->getActivePortDescription();
    }

    return info;
}

void PaSinkInputStream::setDefaultDevice(const std::string& port, const std::string& card)
{    
    std::shared_ptr<IStream> sink = nullptr;
    for (const auto& c : m_info.cardInfoList) {
        if (strcmp(card.c_str(), c->card.name.c_str()))
            continue;

        for(const auto& s : c->sinks) {
            auto ports = std::dynamic_pointer_cast<PaSinkStream>(s)->getPorts();
            auto it = std::find_if(ports.begin(), ports.end(), [port](const std::shared_ptr<PaSinkPortInfo>& info) {
                return info->name == port;
            });
            if (it != std::dynamic_pointer_cast<PaSinkStream>(s)->getPorts().end()) {
                sink = s;
                break;
            }
        }
    }

    if (!sink) {
        AUDIO_ERROR("Set {} default device to card: {}, port: {} failed, not found such device.", name, card, port);
        return;
    }

    this->sink = sink->getIndex();

    AUDIO_DEBUG("Set {} default device to {}.", name, sink->getName());

    pa_context_move_sink_input_by_index(m_pContext, index, sink->getIndex(), nullptr, nullptr);
}

std::vector<std::string> PaSinkInputStream::getDeviceList() const
{
    return {};
}

std::list<std::shared_ptr<DeviceInfo>> PaSinkInputStream::getAvailablePortList() const
{
    return {};
}

void PaSinkInputStream::volumeChanged(int volume)
{
    AudioMethod::getInstance().volumeChanged(NodeType::SINK_INPUT, index, volume);
}

void PaSinkInputStream::muteChanged(bool mute)
{
    AudioMethod::getInstance().muteChanged(NodeType::SINK_INPUT, index, mute);
}

void PaSinkInputStream::portChanged(const std::string& name)
{
    AudioMethod::getInstance().portChanged(NodeType::SINK_INPUT, name);
}

void PaSinkInputStream::deviceChanged(bool autoPause)
{
//    AudioMethod::getInstance().deviceChanged(NodeType::SINK_INPUT, name);
}
}
