/*
 * Copyright (c) 2010-2019 Belledonne Communications SARL.
 *
 * This file is part of Liblinphone.
 *
 * 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 <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "conference/session/call-session.h"
#include "core/core-accessor.h"
#include "private.h"
#include "mediastreamer2/dtmfgen.h"
#include "core/core.h"
#include "tester_utils.h"
#include <map>

LINPHONE_BEGIN_NAMESPACE

/*
 * The ToneManager is in charge of scheduling rings, ringbacks, and various tones that come along with the life
 * of calls.
 */
class ToneManager {
    public:
	ToneManager(Core &core);
	ToneManager(const ToneManager &other) = delete;
	~ToneManager();

	/* 
	 * The CallSession's state change notification are sufficient to trigger rings and tones.
	 * The ToneManager also needs to be informed about a transition to a future state, that's why
	 * there are to entry points:
	 * - prepareForNextState()
	 * - notifyState();
	 * They are driven by the MediaSession.
	 */
	void prepareForNextState(const std::shared_ptr<CallSession> &session, CallSession::State nextState);
	void notifyState(const std::shared_ptr<CallSession> &session, CallSession::State nextState);
	
	
	/* Below are a few accessors required by other parts of liblinphone.*/
	void playDtmf(char dtmf, int duration);
	void stopDtmf();
	LinphoneStatus playLocal(const char *audiofile);
	void startDtmfStream();
	void stopDtmfStream();
	
	/* Used to temporarily override the audio output device. */
	void setOutputDevice(const std::shared_ptr<CallSession> &session, AudioDevice *audioDevice);
	AudioDevice *getOutputDevice(const std::shared_ptr<CallSession> &session) const;
	
	/* Request the tone manager to immediately abandon any sound card usage. All running rings or tones are dropped. */
	void freeAudioResources();

	// tester
	const LinphoneCoreToneManagerStats *getStats()const;
	void resetStats();

	// Tone configuration.
	LinphoneToneDescription *getToneFromReason(LinphoneReason reason);
	LinphoneToneDescription *getToneFromId(LinphoneToneID id);
	void setTone(LinphoneReason reason, LinphoneToneID id, const char *audiofile);

    private:
        using AudioResourceType = enum {
            ToneGenerator = 0,
            LocalPlayer = 1
        };
	
        LinphoneCoreToneManagerStats mStats;
	
	void notifyIncomingCall(const std::shared_ptr<CallSession> &session);
	void notifyOutgoingCallRinging(const std::shared_ptr<CallSession> &session);
	void notifyToneIndication(LinphoneReason reason);
	void destroyRingStream();

        // start
	void startRingbackTone();
	void startRingtone();
	void startErrorTone(LinphoneReason reason);
	void startNamedTone(LinphoneToneID toneId);

	// stop
	void stopRingbackTone();
	void stopTone();
	void stopRingtone();

	void scheduleRingStreamDestruction();
	void updateRingingSessions(const std::shared_ptr<CallSession> &callSession, CallSession::State state);
	
	Core & getCore()const{
		return mCore;
	}

	// sound
	MSFilter *getAudioResource(AudioResourceType rtype, MSSndCard *card, bool create);
	LinphoneStatus playFile(const char *audiofile);
	void playTone(const MSDtmfGenCustomTone & tone);
	MSDtmfGenCustomTone generateToneFromId(LinphoneToneID toneId);
	void cleanPauseTone();
	bool inCallOrConference()const;
	bool shouldPlayWaitingTone(const std::shared_ptr<CallSession> &session);
	std::shared_ptr<CallSession> lookupRingingSession() const;
	Core & mCore;
	RingStream *mRingStream = nullptr;
	std::shared_ptr<CallSession> mSessionRinging;
	std::function< void() > mSessionRingingStopFunction;
	std::shared_ptr<CallSession> mSessionRingingBack;
	std::shared_ptr<CallSession> mSessionPaused;
	belle_sip_source_t *mRingStreamTimer = nullptr;
	bool mDtmfStreamStarted = false;
};

LINPHONE_END_NAMESPACE
