// voiceplayer.cxx -- voice/sound sample player
//
// Written by Jean-Yves Lefort, started September 2005.
//
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2005 Jean-Yves Lefort <jylefort@FreeBSD.org>

#ifdef _MSC_VER
#  pragma warning( disable: 4355 )
#endif

#include <config.h>

#include "voiceplayer.hxx"

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <cmath>

#include <string>
#include <sstream>

#include <simgear/debug/logstream.hxx>
#include <simgear/misc/ResourceManager.hxx>
#include <simgear/sound/sample_group.hxx>
#include <simgear/sound/soundmgr.hxx>
#include <simgear/structure/exception.hxx>

using std::string;
using std::map;
using std::vector;

///////////////////////////////////////////////////////////////////////////////
// constants //////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// helpers ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#define ADD_VOICE(Var,Sample,Twice) \
    { make_voice(&Var);append(Var,Sample);\
      if (Twice) append(Var,Sample); }

#define test_bits(_bits, _test) (((_bits) & (_test)) != 0)

/////////////////////////////////////////////////////////////////////////
// FGVoicePlayer::Voice::SampleElement ///////////////////////////
/////////////////////////////////////////////////////////////////////////

FGVoicePlayer::Voice::SampleElement::SampleElement (SGSharedPtr<SGSoundSample> sample, float volume)
: _sample(sample), _volume(volume)
{
  silence = false;
}

void FGVoicePlayer::Voice::SampleElement::play (float volume)
{
  if (_sample && (volume > 0.05)) { set_volume(volume); _sample->play_once(); }
}

void FGVoicePlayer::Voice::SampleElement::stop ()
{
  if (_sample) _sample->stop();
}

bool FGVoicePlayer::Voice::SampleElement::is_playing ()
{
  return _sample ? _sample->is_playing() : false;
}

void FGVoicePlayer::Voice::SampleElement::set_volume (float volume)
{
  if (_sample) _sample->set_volume(volume * _volume);
}

///////////////////////////////////////////////////////////////////////////////
// FGVoicePlayer //////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

void
FGVoicePlayer::Speaker::bind (SGPropertyNode *node)
{
    // uses xmlsound property names
    tie(node, "volume", &volume);
    tie(node, "pitch", &pitch);
}

void
FGVoicePlayer::Speaker::update_configuration ()
{
    map< string, SGSharedPtr<SGSoundSample> >::iterator iter;
    for (iter = player->samples.begin(); iter != player->samples.end(); iter++)
    {
        SGSoundSample *sample = (*iter).second;

        sample->set_pitch(pitch);
    }

    if (player->voice)
        player->voice->volume_changed();
}

FGVoicePlayer::Voice::~Voice ()
{
    for (iter = elements.begin(); iter != elements.end(); iter++)
        delete *iter;       // we owned the element
    elements.clear();
}

void
FGVoicePlayer::Voice::play ()
{
    iter = elements.begin();
    element = *iter;

    element->play(get_volume());
}

void
FGVoicePlayer::Voice::stop (bool now)
{
    if (element)
    {
        if (now || element->silence)
        {
            element->stop();
            element = NULL;
        }
        else
            iter = elements.end() - 1; // stop after the current element finishes
    }
}

void
FGVoicePlayer::Voice::set_volume (float _volume)
{
    volume = _volume;
    volume_changed();
}

void
FGVoicePlayer::Voice::volume_changed ()
{
    if (element)
        element->set_volume(get_volume());
}

void
FGVoicePlayer::Voice::update ()
{
    if (element)
    {
        if (! element->is_playing())
        {
            if (++iter == elements.end())
                element = NULL;
            else
            {
                element = *iter;
                element->play(get_volume());
            }
        }
    }
}

FGVoicePlayer::FGVoicePlayer (PropertiesHandler* properties_handler, string _dev_name)
: volume(1.0), voice(NULL), next_voice(NULL), paused(false),
dev_name(_dev_name), dir_prefix(""),
speaker(this,properties_handler)
{
  _sgr = NULL;
}

FGVoicePlayer::~FGVoicePlayer ()
{
    vector<Voice *>::iterator iter1;
    for (iter1 = _voices.begin(); iter1 != _voices.end(); iter1++)
        delete *iter1;
    _voices.clear();
    samples.clear();
}

void
FGVoicePlayer::bind (SGPropertyNode *node, const char* default_dir_prefix)
{
    _defaultDirPrefix = default_dir_prefix;
    dir_prefix = node->getStringValue("voice/file-prefix", default_dir_prefix);
    speaker.bind(node);
}

void
FGVoicePlayer::init ()
{
    auto smgr = globals->get_subsystem<SGSoundMgr>();
    _sgr = smgr->find("avionics", true);
    _sgr->tie_to_listener();
    speaker.update_configuration();
}

void
FGVoicePlayer::pause()
{
    if (paused)
        return;

    paused = true;
    if (voice)
    {
        voice->stop(true);
    }
}

void
FGVoicePlayer::resume()
{
    if (!paused)
        return;
    paused = false;
    if (voice)
    {
        voice->play();
    }
}

SGSoundSample *
FGVoicePlayer::get_sample (const char *name)
{
    string refname;
    refname = dev_name + "/" + dir_prefix + name;

    SGSoundSample *sample = _sgr->find(refname);
    if (! sample)
    {
        auto resMan = simgear::ResourceManager::instance();
        string filename = dir_prefix + string(name) + ".wav";
        auto p = resMan->findPath(filename);
        if (p.isNull()) {
            // fall back to the default prefix
            filename = _defaultDirPrefix + string(name) + ".wav";
        }

        sample = new SGSoundSample(filename.c_str(), SGPath());

        _sgr->add(sample, refname);
        samples[refname] = sample;
    }

    return sample;
}

void
FGVoicePlayer::play (Voice *_voice, unsigned int flags)
{
    if (!_voice)
        return;
    if (test_bits(flags, PLAY_NOW) || ! voice ||
            (voice->element && voice->element->silence))
    {
        if (voice)
            voice->stop(true);

        voice = _voice;
        looped = test_bits(flags, PLAY_LOOPED);

        next_voice = NULL;
        next_looped = false;

        if (!paused)
            voice->play();
    }
    else
    {
        next_voice = _voice;
        next_looped = test_bits(flags, PLAY_LOOPED);
    }
}

void
FGVoicePlayer::stop (unsigned int flags)
{
    if (voice)
    {
        voice->stop(test_bits(flags, STOP_NOW));
        if (voice->element)
            looped = false;
        else
            voice = NULL;
        next_voice = NULL;
    }
}

void
FGVoicePlayer::set_volume (float _volume)
{
    volume = _volume;
    if (voice)
        voice->volume_changed();
}

void
FGVoicePlayer::update ()
{
    if (voice)
    {
        voice->update();

        if (next_voice)
        {
            if (! voice->element || voice->element->silence)
            {
                voice = next_voice;
                looped = next_looped;

                next_voice = NULL;
                next_looped = false;

                voice->play();
            }
        }
        else
        {
            if (! voice->element)
            {
                if (looped)
                    voice->play();
                else
                    voice = NULL;
            }
        }
    }
}

void
FGVoicePlayer::append (Voice *voice, const char *sample_name)
{
  voice->append(new Voice::SampleElement(get_sample(sample_name)));
}
