/*
 * Copyright (C) 2025 Georg Zotti
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
 */

#ifndef STELSPEECHMGR_HPP
#define STELSPEECHMGR_HPP

#include "StelModule.hpp"
#include "StelObject.hpp"
#include <QObject>
#include <QSettings>
#include <QMap>
#include <QString>
#include <QLoggingCategory>
#if defined(ENABLE_SPEECH)
#include <QAudioOutput>
#include <QTextToSpeech>
#endif

class QMediaPlayer;
class QVoice;

//! @class StelSpeechMgr
//! Provides artificial voice output for a narration on sky objects or GUI elements.
//!
//! Sometimes, in the dark you may want voice output over reading small text on the screen.
//! Calling a "narrate" action (default: Shift+R) for the selected object will provide some information.
//!
//! Parallel to the InfoString settings which decide what information items to show,
//! we use a similar structure to decide what data elements are being narrated.
//!
//! @note: This functionality requires Qt6.4 and higher or else just emits the narration to logfile.
//! @note You can finetune the amount of speech-related messages in the logfile by configuring the logging category stel.Speech.
//! For this, e.g. set environment variable QT_LOGGING_RULES="*.debug=false;stel.Speech.debug=false;".
//! By default, for this new module, all messages are displayed. Later we will limit this to the Info category.


Q_DECLARE_LOGGING_CATEGORY(Speech)

class StelSpeechMgr : public StelModule
{
	Q_OBJECT

	Q_PROPERTY(double rate                READ getRate     WRITE setRate     NOTIFY rateChanged)
	Q_PROPERTY(double pitch               READ getPitch    WRITE setPitch    NOTIFY pitchChanged)
	Q_PROPERTY(double volume              READ getVolume   WRITE setVolume   NOTIFY volumeChanged)
#if defined(ENABLE_SPEECH)
	Q_PROPERTY(QTextToSpeech::State state READ getState                      NOTIFY stateChanged)
#endif


public:
	StelSpeechMgr();
	~StelSpeechMgr() override;
	void init() override;
	//! Get the module ID, returns "StelSpeechMgr". BUT FOR WHAT PURPOSE?
	//virtual QString getModuleID() const {return "StelSpeechMgr";}


	//! Retrieve the currently active flags which information bits to narrate
	const StelObject::InfoStringGroup& getNarrationTextFilters() const;

	//! Set the currently active flags which information bits to narrate
	void setNarrationTextFilters(const StelObject::InfoStringGroup &flags);

signals:
	void rateChanged(double);
	void pitchChanged(double);
	void volumeChanged(double);
#if defined(ENABLE_SPEECH)
	void stateChanged(QTextToSpeech::State state);
	void languageChanged();
	void speechReady();
#endif

public slots:
	//! Emit an audible synthesized narration
	void say(const QString &narration) const;
	//! Stops replay of narration.
	void stop() const;

	//! global info, always false if Qt<6.6 and if no engine was initialized
	bool enabled() const;

	//! set replay rate (-1...+1)
	void setRate(double newRate);
	double getRate() const {return m_rate;}

	//! set pitch (-1...+1)
	void setPitch(double newPitch);
	double getPitch() const {return m_pitch;}

	//! set volume (0...+1)
	void setVolume(double newVolume);
	double getVolume() const {return m_volume;}

#if defined(ENABLE_SPEECH)
	//! return the state of the Speech engine, or QTextToSpeech::State::Error
	QTextToSpeech::State getState() const;

public:
	//! set the engine to be used. Some OS like Windows have more than one.
	//! @arg engine must be one of those returned by QTextToSpeech::availableEngines.
	//! emits engineChanged().
	void setEngine(const QString &engine);
	//! return engine name or an empty string if no engine was configured.
	QString getEngine() const;

	//! set the voice to be used.
	//! @arg name must be one of those returned by QTextToSpeech::findVoices(),
	//! or else any valid voice will be set.
	void setVoice(QString &name);
	//! return the name of the current voice
	QString getVoiceName() const {return m_speech->voice().name();}

	//! return the name of available voices
	QStringList getAvailableVoiceNames() const;
#endif

private:
	void initNarrationFlagsFromConfig(QSettings *conf);

#if defined(ENABLE_SPEECH)
	//! Set necessary things after engine change. This is triggered by setEngine()
	void onEngineReady();
	//! Set necessary things after application language change:
	//! m_voices is reconfigured.
	//! This is called by onEngineChanged() and triggered by subsequent StelApp::languageChanged()
	void onAppLanguageChanged();

#ifdef ENABLE_SPEECH
private slots:
	//! Debugging aid: Report any state changes
	void reportStateChanged(QTextToSpeech::State state) const;
	void reportError(QTextToSpeech::ErrorReason reason, const QString &errorString) const;
#endif

private:
	//! Replicates some functionality of Qt6.6's findVoices, but just finds the voive of a particular name.
	QVoice findVoice(QString &name) const;

	QTextToSpeech *m_speech = nullptr;
	QList<QVoice> m_voices;
#endif

	double m_rate;   // -1...1
	double m_pitch;  // -1...1
	double m_volume; // 0...1
	StelObject::InfoStringGroup flags; //! Selection of info bits to be narrated
};

#endif // STELSPEECHMGR_HPP
