/*  FrameSourceAdder.h

    flatzebra - SDL-based sound renderer
    Copyright (C) 2011 Pierre Sarrazin <http://sarrazip.com/>

    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, Fifth Floor,
    Boston, MA  02110-1301, USA.
*/

#pragma once

#include <flatzebra/roundbeetle/FrameSource.h>

#include <vector>


namespace roundbeetle {


class FrameSourceAdder : public FrameSource
{
public:

    FrameSourceAdder(float _linAttenuation);

    // Destroys any FrameSource object that may still be attached
    // to this adder.
    //
    virtual ~FrameSourceAdder();

    // child: child source that has just finished (same pointer as that passed to addChild()).
    //        Note that the FrameSource object is about to be destroyed.
    // userData: same pointer as that passed to registerChildRemovedCallback().
    // adder: the FrameSourceAdder object that has just removed the finished child from its list.
    //
    typedef void (*OnChildRemovedCallback)(FrameSource *child, void *userData, FrameSourceAdder *adder);

    // Registers a callback to be invoked (by the renderer thread) when
    // a child has finished.
    //
    // CAUTION: this callback will be invoked under the protection of the
    // renderer's mutex, which means that the callback's execution time
    // must be as short as possible.
    //
    // callback: passing NULL deregisters any previously registered callback;
    //           passing non-NULL replaces any previously registered callback
    //           with the new one.
    //
    void registerChildRemovedCallback(OnChildRemovedCallback callback, void *userData);

    // Returns the currently registered callback and the associated user data value
    // that were passed to the most recent call to registerChildRemovedCallback()
    // on this object.
    // If registerChildRemovedCallback() has not been called on this object,
    // the two returned values will be null.
    //
    void getChildRemovedCallback(OnChildRemovedCallback &cb, void *&userData) const;

    void setLinearAttenuation(float _linAttenuation);

    // child: when finished, this pointer will be passed to the callback
    // registered by registerChildRemovedCallback().
    // This method does nothing if 'child' is null.
    //
    // Does NOT rewind 'child'. If needed, this must be done by the caller.
    //
    void addChild(FrameSource *child);

    // Like addChild(), but without the protection of the renderer's mutex.
    //
    void addChildUnsafe(FrameSource *child);

    bool killChild(FrameSource *child);

    bool killChildUnsafe(FrameSource *child);

    bool isBusy() const;

    virtual size_t getFrames(Frame *frameBuffer, size_t numFramesToFill) override;

    virtual bool isFinished() const override;

    virtual bool rewind() override;

    size_t getNumOverflows() const;

private:

    void removeChild(size_t index);

    static void add(Sint16 &destSample, Sint16 childSample, float linAttenuation);
    static Sint16 bound(Sint32 sample);

private:

    void resetAddBuffer(size_t newSizeInFrames);

    FrameSourceAdder(const FrameSourceAdder &);
    FrameSourceAdder &operator = (const FrameSourceAdder &);

protected:

    std::vector<FrameSource *> children;  // FrameSource objects owned by this object
                            // Order does not matter since goal is to add the samples.
    float linAttenuation;
    std::vector<FloatFrame> addBuffer;
    size_t numOverflows;
    OnChildRemovedCallback onChildRemoved;
    void *userDataOnChildRemoved;

};


}  // namespace roundbeetle
