#ifndef AUDIOPIPELINE_AUDIO_H__INCLUDED
#define AUDIOPIPELINE_AUDIO_H__INCLUDED

#include <stdarg.h>
#include <stdint.h>

/**
 * Audio buffer that is passed from one pipeline element to the next, contents
 * in data are undefined, they could be compressed data or interleaved sample
 * data.
 */
typedef struct _AudioBuffer {
    uint32_t buf_size;
    uint8_t *data;
    // FIXME: Data type
} AudioBuffer;

/**
 * Type of pipeline element
 */
typedef enum _AudioPipelineElementType {
    AudioElementSource,  /** Audio source, has to be the first item in a pipeline */
    AudioElementDemuxer, /** Format demuxer, yields sample data from a stream */
    AudioElementDecoder, /** Audio decoder, decodes sample data from a compressed input buffer */
    AudioElementEncoder, /** Audio encoder, takes sample data and compresses them */
    AudioElementFilter,  /** Audio filter, takes sample data, transforms the samples and yields sample data */
    AudioElementMuxer,   /** Takes compressed buffers and muxes them to an output format which can be saved*/
    AudioElementSink     /** Audio sink, takes a buffer and outputs it somewhere, probably a file or soundcard */
} AudioPipelineElementType;

/**
 * Status of the audio pipeline
 */
typedef enum _AudioPipelineStatus {
    PipelineStopped,   /** Pipeline in stopped state, no processing is happening yet */
    PipelineRunning,   /** Pipeline in running state, samples will be produced, transformed and consumed*/
    PipelinePaused,    /** Pipeline in paused mode, everything is initialized but no processing is happening */
    PipelineBuffering, /** Pipeline is pre-loading data */
    PipelineError,     /** Pipeline is in error state, something happened that prevents other states */
    PipelineFinished   /** Pipeline has finished processing data */
} AudioPipelineStatus;

/** A audio pipeline element, data contained in this is opaque to the user */
typedef struct _AudioPipelineElement AudioPipelineElement;

/** 
 * The audio pipeline, created by calling `audio_pipeline_assemble` and can be used by calling
 * the function pointers provided.
 */
typedef struct _AudioPipeline {
    
    /** Start processing audio */
    AudioPipelineStatus (*start)(struct _AudioPipeline *pipeline);
    
    /** Stop processing audio and reset internal state */
    AudioPipelineStatus (*reset)(struct _AudioPipeline *pipeline);
    
    /** Stop processing audio, does not reset internal state, you can call start on the pipeline
        to continue processing */
    AudioPipelineStatus (*stop)(struct _AudioPipeline *pipeline);

    /** Status callback will be called if the state of the pipeline changes */
    void (*statusCallback)(struct _AudioPipeline *self, AudioPipelineStatus status);

    /** The source element of the pipeline, some sources may allow changing parameters */
    struct _AudioPipelineElement *source;
    
    /** The sink element of the pipeline */
    struct _AudioPipelineElement *sink;
    
    /** Pipeline status (same as is provided via status callback) */
    AudioPipelineStatus status;
} AudioPipeline;

/**
 * Assemble a pipeline from components, end the List with a sink and a NULL element!
 *
 * @param source The source element
 * @param ... other elements to add to the pipeline, end with NULL element!
 *
 * @returns Initialized `AudioPipeline` or `NULL` on Error.
 */
AudioPipeline *audio_pipeline_assemble(AudioPipelineElement *source, ...);

/**
 * Destroy the pipeline given, this will release all associated memory, do not use pipeline afterwards!
 *
 * @param pipeline The pipeline to deallocate
 */
void audio_pipeline_destroy(AudioPipeline *pipeline);

/**
 * Safely allocate an audio buffer, returns NULL on out of memory conditions.
 * 
 * @param size Size in bytes of the buffer data, iIf set to 0, the data pointer will not be allocated and stays at `NULL`
 *
 * @returns Initialized `AudioBuffer`
 */
AudioBuffer *alloc_audio_buffer(uint32_t size);

/**
 * Free an audio buffer and all it's contents if any has been allocated.
 *
 * @param buffer The audio buffer to deallocate
 */
void free_audio_buffer(AudioBuffer *buffer);

#endif /* AUDIOPIPELINE_AUDIO_H__INCLUDED */