/*
 * Copyright 2024 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 by the Free Software
 * Foundation, either version 3 of the License, or (at your option) 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 <https://www.gnu.org/licenses/>.
 */

#ifndef SPEECH_SYNTHESIZER_H
#define SPEECH_SYNTHESIZER_H

#include <kylin-ai/aiengine.h>

#include <atomic>
#include <memory>
#include <string>

#include "audioconfiginfo.h"
#include "concurrentqueue/blockingconcurrentqueue.h"
#include "taskmanager/taskrunner.h"
#include "utils/audioplayer.h"

struct SpeechSynthesisResult {
    int reason;
    std::string audioFormat;
    int audioRate;
    int audioChannel;
    std::string base64AudioData;
    int errorCode;
    std::string errorMessage;
};

class SpeechSynthesizer : public std::enable_shared_from_this<SpeechSynthesizer> {
public:
    struct TtsParam {
        OutputAudioInfo outputAudioInfo;
        std::string text;
    };

    using ISpeechEngine = ai_engine::core_ai::speech::AbstractSpeechEngine;
    using ISpeechEnginePtr = std::shared_ptr<ISpeechEngine>;

    using SynthesisResultCallback = std::function<void(SpeechSynthesisResult)>;

public:
    SpeechSynthesizer();

    ~SpeechSynthesizer();

    bool init(int& errorCode, const ISpeechEnginePtr& speechEngine);

    void destroy();

    void setSynthesisResultCallback(const SynthesisResultCallback& callback);

    void setAudioConfig(const AudioConfigInfo& audioConfigInfo);

    void synthesizeTextOnce(const std::string& text);
    void stopSpeaking();
    bool engineInUse() const { return engineInUse_; }

private:
    std::shared_ptr<SpeechSynthesizer> getSharedObject() { return shared_from_this(); }

    void setSynthesisResultCallbackWithError(const ai_engine::EngineError& error) const;
    void writePcmToFile(const std::vector<uint8_t>& audioData);

    void handleSynthesisResult(const ai_engine::core_ai::speech::SynthesisResult& result,
                               const std::shared_ptr<AudioPlayer>& audioPlayer, const TtsParam& ttsParam);

    void onceTtsAsync();

    void onceTtsTask();

private:
    OutputAudioInfo outputAudioInfo_;
    ISpeechEnginePtr speechEngine_{nullptr};
    SynthesisResultCallback synthesisResultCallback_{nullptr};
    bool isFirstWrite_ = true;
    std::atomic_bool engineInUse_{false};
    std::atomic<task::TaskId> taskId_{task::INVALID_TASK_ID};
    moodycamel::BlockingConcurrentQueue<TtsParam> ttsParamQueue_;
};

#endif
