/*
 * Copyright 2022 The Emscripten Authors.  All rights reserved.
 * Emscripten is available under two separate licenses, the MIT license and the
 * University of Illinois/NCSA Open Source License.  Both these licenses can be
 * found in the LICENSE file.
 */

#pragma once

#include <emscripten/emscripten.h>
#include <emscripten/html5.h>

#include <memory.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

// A handle type that represents a JavaScript side object related to WebAudio.
// Used to denote the AudioContext and Audio Nodes, especially the Audio Worklet
// Node.
typedef int EMSCRIPTEN_WEBAUDIO_T;

// An outdated node type that represented an AudioWorklet node.
// If you are using this type in your application, replace it with
// EMSCRIPTEN_WEBAUDIO_T handle type instead.
typedef int EMSCRIPTEN_AUDIO_WORKLET_NODE_T __attribute__((deprecated("use EMSCRIPTEN_WEBAUDIO_T instead")));

// Default render size of 128 frames
#define AUDIO_CONTEXT_RENDER_SIZE_DEFAULT 0
// Let the hardware determine the best render size
#define AUDIO_CONTEXT_RENDER_SIZE_HARDWARE -1

typedef struct EmscriptenWebAudioCreateAttributes
{
	const char *latencyHint; // Specify one of "balanced", "interactive" or "playback"
	uint32_t sampleRate; // E.g. 44100 or 48000
	int32_t renderSizeHint; // AUDIO_CONTEXT_RENDER_SIZE_* or number of samples
} EmscriptenWebAudioCreateAttributes;

// Creates a new Web Audio AudioContext, and returns a handle to it.
EMSCRIPTEN_WEBAUDIO_T emscripten_create_audio_context(const EmscriptenWebAudioCreateAttributes *options);

typedef int AUDIO_CONTEXT_STATE;
#define AUDIO_CONTEXT_STATE_SUSPENDED 0
#define AUDIO_CONTEXT_STATE_RUNNING 1
#define AUDIO_CONTEXT_STATE_CLOSED 2
#define AUDIO_CONTEXT_STATE_INTERRUPTED	3

typedef void (*EmscriptenResumeAudioContextCallback)(EMSCRIPTEN_WEBAUDIO_T audioContext, AUDIO_CONTEXT_STATE state, void *userData1);

// Resumes the given AudioContext. The specified callback will fire when the AudioContext has completed resuming. Call this function
// inside a user event handler (mousedown, button click, etc.)
// userData1: A custom userdata pointer to pass to the callback function. This value will be passed on to the call to the given EmscriptenResumeAudioContextCallback callback function.
void emscripten_resume_audio_context_async(EMSCRIPTEN_WEBAUDIO_T audioContext, EmscriptenResumeAudioContextCallback callback, void *userData1);

// Synchronously attempts to resume the given AudioContext.
void emscripten_resume_audio_context_sync(EMSCRIPTEN_WEBAUDIO_T audioContext);

// Returns the current AudioContext state.
AUDIO_CONTEXT_STATE emscripten_audio_context_state(EMSCRIPTEN_WEBAUDIO_T audioContext);

typedef void (*EmscriptenStartWebAudioWorkletCallback)(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData2);

// Calls .suspend() on the given AudioContext and releases the JS object table
// reference to the given audio context. The specified handle is invalid
// after calling this function.
void emscripten_destroy_audio_context(EMSCRIPTEN_WEBAUDIO_T audioContext);

// Disconnects the given audio node from its audio graph, and then releases
// the JS object table reference to the given audio node. The specified handle
// is invalid after calling this function.
void emscripten_destroy_web_audio_node(EMSCRIPTEN_WEBAUDIO_T objectHandle);

// Create Wasm AudioWorklet thread. Call this function once at application startup to establish an AudioWorkletGlobalScope for your app.
// After the scope has been initialized, the given callback will fire.
// audioContext: The Web Audio context object to initialize the Wasm AudioWorklet thread on. Each AudioContext can have only one AudioWorklet
//               thread running, so do not call this function multiple times on the same AudioContext.
// stackLowestAddress: The base address for the thread's stack. Must be aligned to 16 bytes. Use e.g. memalign(16, 1024) to allocate a 1KB stack for the thread.
// stackSize: The size of the thread's stack. Must be a multiple of 16 bytes.
// callback: The callback function that will be run when thread creation either succeeds or fails.
// userData2: A custom userdata pointer to pass to the callback function. This value will be passed on to the call to the given EmscriptenStartWebAudioWorkletCallback callback function.
void emscripten_start_wasm_audio_worklet_thread_async(EMSCRIPTEN_WEBAUDIO_T audioContext, void *stackLowestAddress, uint32_t stackSize, EmscriptenStartWebAudioWorkletCallback callback, void *userData2);

typedef int WEBAUDIO_PARAM_AUTOMATION_RATE;
#define WEBAUDIO_PARAM_A_RATE 0
#define WEBAUDIO_PARAM_K_RATE 1

typedef struct WebAudioParamDescriptor
{
	float defaultValue; // Default == 0.0
	float minValue; // Default = -3.4028235e38
	float maxValue; // Default = 3.4028235e38
	WEBAUDIO_PARAM_AUTOMATION_RATE automationRate; // Either WEBAUDIO_PARAM_A_RATE or WEBAUDIO_PARAM_K_RATE. Default = WEBAUDIO_PARAM_A_RATE
} WebAudioParamDescriptor;

typedef struct WebAudioWorkletProcessorCreateOptions
{
	const char *name; // The name of the AudioWorkletProcessor that is being created.

	int numAudioParams;
	const WebAudioParamDescriptor *audioParamDescriptors;
} WebAudioWorkletProcessorCreateOptions;

typedef void (*EmscriptenWorkletProcessorCreatedCallback)(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData3);

// Creates a new AudioWorkletProcessor with the given name and specified set of control parameters.
// userData3: A custom userdata pointer to pass to the callback function. This value will be passed on to the call to the given EmscriptenWorkletProcessorCreatedCallback callback function.
void emscripten_create_wasm_audio_worklet_processor_async(EMSCRIPTEN_WEBAUDIO_T audioContext, const WebAudioWorkletProcessorCreateOptions *options, EmscriptenWorkletProcessorCreatedCallback callback, void *userData3);

// Returns the number of samples processed per channel in an AudioSampleFrame, fixed at 128 in the Web Audio API 1.0 specification, and valid for the lifetime of the audio context.
// For this to differ from the default 128, the context would need to be created with a WebAudioWorkletProcessorCreateOptions renderSizeHint, part of the 1.1 Web Audio API.
int emscripten_audio_context_quantum_size(EMSCRIPTEN_WEBAUDIO_T audioContext);

// Returns the sampling rate of the given Audio Context, e.g. 48000 or 44100 or similar.
int emscripten_audio_context_sample_rate(EMSCRIPTEN_WEBAUDIO_T audioContext);

typedef struct AudioSampleFrame
{
	// Number of audio channels to process (multiplied by samplesPerChannel gives the elements in data)
	const int numberOfChannels;
	// Number of samples per channel in data
	const int samplesPerChannel;
	// An array of length numberOfChannels*samplesPerChannel elements. Samples are always arranged in a planar fashion,
	// where data[channelIndex*samplesPerChannel+i] locates the data of the i'th sample of channel channelIndex.
	float *data;
} AudioSampleFrame;

typedef struct AudioParamFrame
{
	// Specifies the length of the input array data (in float elements). This will be guaranteed to either have
	// a value of 1, for a parameter valid for the entire frame, or emscripten_audio_context_quantum_size() for a parameter that changes per sample during the frame.
	int length;
	// An array of length specified in 'length'.
	float *data;
} AudioParamFrame;

typedef bool (*EmscriptenWorkletNodeProcessCallback)(int numInputs, const AudioSampleFrame *inputs, int numOutputs, AudioSampleFrame *outputs, int numParams, const AudioParamFrame *params, void *userData4);

typedef enum {
    WEBAUDIO_CHANNEL_COUNT_MODE_MAX = 0,
    WEBAUDIO_CHANNEL_COUNT_MODE_CLAMPED_MAX = 1,
    WEBAUDIO_CHANNEL_COUNT_MODE_EXPLICIT = 2
} WEBAUDIO_CHANNEL_COUNT_MODE;

typedef enum {
    WEBAUDIO_CHANNEL_INTERPRETATION_SPEAKERS = 0,
    WEBAUDIO_CHANNEL_INTERPRETATION_DISCRETE = 1
} WEBAUDIO_CHANNEL_INTERPRETATION;

typedef struct EmscriptenAudioWorkletNodeCreateOptions
{
	// How many audio nodes does this node take inputs from? Default=1
	int numberOfInputs;
	// How many audio nodes does this node output to? Default=1
	int numberOfOutputs;
	// For each output, specifies the number of audio channels (1=mono/2=stereo/etc.) for that output. Default=an array of ones for each output channel.
	int *outputChannelCounts;
	// Number of channels used when up-mixing and down-mixing connections to any inputs to the node. Default=2
	unsigned long channelCount;
	// How channels will be counted when up-mixing and down-mixing connections to any inputs to the node? Default=max
	WEBAUDIO_CHANNEL_COUNT_MODE channelCountMode;
	// How individual channels will be treated when up-mixing and down-mixing connections to any inputs to the node? Default=speakers
	WEBAUDIO_CHANNEL_INTERPRETATION channelInterpretation;

} EmscriptenAudioWorkletNodeCreateOptions;

// Instantiates the given AudioWorkletProcessor as an AudioWorkletNode, which continuously calls the specified processCallback() function on the browser's audio thread to perform audio processing.
// userData4: A custom userdata pointer to pass to the callback function. This value will be passed on to the call to the given EmscriptenWorkletNodeProcessCallback callback function.
// Returns a handle to the created audio worklet node object.
EMSCRIPTEN_WEBAUDIO_T emscripten_create_wasm_audio_worklet_node(EMSCRIPTEN_WEBAUDIO_T audioContext, const char *name, const EmscriptenAudioWorkletNodeCreateOptions *options, EmscriptenWorkletNodeProcessCallback processCallback, void *userData4);

// Connects a node's output to a target, e.g., connect the worklet node to the context.
// For outputIndex and inputIndex, see the AudioNode.connect() documentation (setting 0 as the default values)
void emscripten_audio_node_connect(EMSCRIPTEN_WEBAUDIO_T source, EMSCRIPTEN_WEBAUDIO_T destination, int outputIndex, int inputIndex);

// Returns true if the current thread is executing a Wasm AudioWorklet, false otherwise.
// Note that calling this function can be relatively slow as it incurs a Wasm->JS transition,
// so avoid calling it in hot paths.
bool emscripten_current_thread_is_audio_worklet(void);

#define EMSCRIPTEN_AUDIO_MAIN_THREAD 0

/* emscripten_audio_worklet_function_*: Post a pointer to a C/C++ function to be executed on the Audio Worklet 
   thread of the given Web Audio context. Notes:
 - If running inside an Audio Worklet thread, specify ID EMSCRIPTEN_AUDIO_MAIN_THREAD (== 0) to pass a message
   from the audio worklet to the main thread.
 - When specifying non-zero ID, the Audio Context denoted by the ID must have been created by the calling thread.
 - Passing messages between audio thread and main thread with this family of functions is relatively slow and has
   a really high latency cost compared to direct coordination using atomics and synchronization primitives like
   mutexes. Additionally these functions will generate garbage on the JS heap. Therefore avoid using these
   functions where performance is critical. */
void emscripten_audio_worklet_post_function_v(EMSCRIPTEN_WEBAUDIO_T id, void (*funcPtr)(void));
void emscripten_audio_worklet_post_function_vi(EMSCRIPTEN_WEBAUDIO_T id, void (*funcPtr)(int), int arg0);
void emscripten_audio_worklet_post_function_vii(EMSCRIPTEN_WEBAUDIO_T id, void (*funcPtr)(int, int), int arg0, int arg1);
void emscripten_audio_worklet_post_function_viii(EMSCRIPTEN_WEBAUDIO_T id, void (*funcPtr)(int, int, int), int arg0, int arg1, int arg2);
void emscripten_audio_worklet_post_function_vd(EMSCRIPTEN_WEBAUDIO_T id, void (*funcPtr)(double), double arg0);
void emscripten_audio_worklet_post_function_vdd(EMSCRIPTEN_WEBAUDIO_T id, void (*funcPtr)(double, double), double arg0, double arg1);
void emscripten_audio_worklet_post_function_vddd(EMSCRIPTEN_WEBAUDIO_T id, void (*funcPtr)(double, double, double), double arg0, double arg1, double arg2);
void emscripten_audio_worklet_post_function_sig(EMSCRIPTEN_WEBAUDIO_T id, void *funcPtr, const char *sig, ...);

#ifdef __cplusplus
} // ~extern "C"
#endif
