#ifndef _SOUND_HPP_
#define _SOUND_HPP_

#include <stdlib.h>
#include <stdbool.h>

namespace sound {

    // type of sound.
    // the envelope settings modify each sound.
    enum Sound {
        SoundFrequency=0,   // one frequency
        SoundWhiteNoise=1,  // all frequencies are equal
        SoundBrownianNoise=2,  // 1/f^2 noise
        SoundPinkNoise=3,   // 1/f noise
    };

    enum EnvelopeState {
        New=0,   // newly spawned
        Wait,  // ready but not yet started (waiting before starting or before repeating)
        Rise,  // increasing
        BeginOvershoot,
        EndOvershoot,
        Hold,  // staying at normal level
        Decay, // deacying
        Done,  // done
    };

    enum SoundDecayType {
        SoundDecayLinear = 0,
        SoundDecayExp    = 1
    };

    struct SoundHandle {
        // just for debugging/logging. don't rely on use
        const char *name;

        // unique ID for accessing them via lookups etc. if sound is dead, this will be never reused
        size_t uid;

        // marked for deletion by the internal thread.
        bool deleteFlag;

        // is now deleted. when a new sound should be used, this can be reused
        // and at the end deleteFlag and isDeleted should be cleared.
        bool isDeleted;

        // if true, don't deallocate once this is done.
        bool keep_when_done;

        // these are public and can be changed by the user.
        enum EnvelopeState envelope;

        /****************************************************************/
        /*             Special Parameters for specific types            */
        /****************************************************************/

        /******************** SoundFrequency ****************************/

        float freq; // base frequency for type: SoundFrequency

        /******************** White / Brownian / Pink Noise ****************/
        float leakage; // influence of previous state on current value (0..1)
        float scaling; // influence of white noise on next value

        // If true and the value goes outside (-1..1), do the step in the other
        // direction to stay inside the valid range.
        bool skipDirectionIfClamping;

        /******************** SoundFrequency: ***************************/

        /****************************************************************/
        /*             Common sound state / parameters                  */
        /****************************************************************/
        // progress of the sound which is used for the envelope but can be used for
        // other things too
        float time;

        // scalar state that can be used arbitrarily to derive the waveform from it
        float state;

        // time when the sound is done
        // if != 0.0, the sound changes with that
        // TODO: make attack gain decay stuff as envelope
        // TODO: add amplitude noise
        float _riseTime;
        float _relativeOvershootTime; // percentage of end of rise time that is used for overshoot (max amplitude at t_overshoot/2)
        float _holdTime;
        float _decayTime;
        float _waitTime;

        // waveform of type of rise. default: linear
        enum SoundDecayType riseType;

        // waveform of type of decay. default: linear
        enum SoundDecayType decayType;

        // if true, play infinitely
        bool looping;

        // base amplitude of the sound
        float amplitude;

        // amplitude when signal is at minimum (before rise, after release and in wait time)
        float minAmplitude;

        // these are al private!
        // never acces them
        enum Sound   _type; // which type of sound

        int _loopCount;

        // if true, will get removed
        bool _done;
        bool _useEnvelope;

        float *history; // if != 0, history is a pointer to 2*numHistorySamples samples.
        size_t numHistorySamples; // if != 0, save history for this sound.
        size_t lastHistorySample; // index/2 of last history sample. used
    };

    // initialize sound subsystem. return true if working, false on errors.
    bool initSound(void);

    // free sounds that are not used anymore.
    // must be called regularily by the application
    void deleteOldSounds();

    // start playing a sound.
    // args:
    // type - type of sound.
    // amplitude : 0..1
    // handle: if != NULL will point to the internal sound handle to change the
    // manipulate playing of the sound.
    // TODO: add sound from file to play too
    SoundHandle *playSound(enum Sound type, float amplitude);

    // start playing a looping frequency.
    // args:
    // freq: frequency in Hz
    SoundHandle *playFrequency(float freq, float amplitude);

    // set them.
    // args:
    // time of phases
    bool configureEnvelope(SoundHandle *handle, float rise, float hold, float decay);

    bool configureOvershoot(SoundHandle *handle, float relativeOvershootTime);

    // same as above but set to looping and set wait time between intervals
    bool configureEnvelopeWait(SoundHandle *handle, float rise, float hold, float decay, float wait);

    // without looping but with wait
    bool configureEnvelopeLooping(SoundHandle *handle, float rise, float hold, float decay, float wait);

    // getter for attributes. if 0.0, unused.
    float getRiseDuration(SoundHandle *handle);
    float getHoldDuration(SoundHandle *handle);
    float getDecayDuration(SoundHandle *handle);

    float getWaitTime(SoundHandle *handle);
    void configureWaitTime(SoundHandle *handle, float waitTime);

    // Set looping behaviour of the sound.
    // args:
    //                 / >   0, sound is looping for this number of repetitions
    // numRepetitions {  ==  0, sound is stopped when it is over
    //                 \ == -1, loop the sound forever
    //
    void setLoopCount(SoundHandle *handle, int numRepetitions);

    // stop playing a sound. the handle is invalid now.
    bool stopSound(SoundHandle *handle);

    // stop and remove all playing sounds. all handle become invalid after calling
    // this.
    void stopAllSounds(void);

    void teardownSound(void);

    // for debugging
    bool startDumpingWav(const char *filename);

    // is deletede when 'keep' not set after stopping
    void stopDumpingWav(void);

    int numActiveSounds(void);

    void deleteSound(SoundHandle *handle);

    //SoundHandle *playFreqEnvelope(float freq, float rise, float hold, float decay);

    // TODO: add fade out/fade in overlay stuff for stopping sounds smoothly
}

#endif
