/*
 * Copyright (c) 2015 Andrew Eikum for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "wine/list.h"

#include "mmsystem.h"
#include "xaudio2.h"
#include "xaudio2fx.h"
#include "xapo.h"
#include "devpkey.h"
#include "mmdeviceapi.h"
#include "audioclient.h"

#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alext.h>

typedef struct _XA2Buffer {
    XAUDIO2_BUFFER xa2buffer;
    DWORD offs_bytes;
    UINT32 latest_al_buf, looped, loop_end_bytes, play_end_bytes, cur_end_bytes;
} XA2Buffer;

typedef struct _IXAudio2Impl IXAudio2Impl;

typedef struct _XA2SourceImpl {
    IXAudio2SourceVoice IXAudio2SourceVoice_iface;

#if XAUDIO2_VER == 0
    IXAudio20SourceVoice IXAudio20SourceVoice_iface;
#elif XAUDIO2_VER <= 3
    IXAudio23SourceVoice IXAudio23SourceVoice_iface;
#elif XAUDIO2_VER <= 7
    IXAudio27SourceVoice IXAudio27SourceVoice_iface;
#endif

    IXAudio2Impl *xa2;

    BOOL in_use;

    CRITICAL_SECTION lock;

    WAVEFORMATEX *fmt;
    ALenum al_fmt;
    UINT32 submit_blocksize;

    IXAudio2VoiceCallback *cb;

    DWORD nsends;
    XAUDIO2_SEND_DESCRIPTOR *sends;

    BOOL running;

    UINT64 played_frames;

    XA2Buffer buffers[XAUDIO2_MAX_QUEUED_BUFFERS];
    UINT32 first_buf, cur_buf, nbufs, in_al_bytes;

    UINT32 scratch_bytes, convert_bytes;
    BYTE *scratch_buf, *convert_buf;

    ALuint al_src;
    /* most cases will only need about 4 AL buffers, but some corner cases
     * could require up to MAX_QUEUED_BUFFERS */
    ALuint al_bufs[XAUDIO2_MAX_QUEUED_BUFFERS];
    DWORD first_al_buf, al_bufs_used;

    struct list entry;
} XA2SourceImpl;

typedef struct _XA2SubmixImpl {
    IXAudio2SubmixVoice IXAudio2SubmixVoice_iface;

#if XAUDIO2_VER == 0
    IXAudio20SubmixVoice IXAudio20SubmixVoice_iface;
#elif XAUDIO2_VER <= 3
    IXAudio23SubmixVoice IXAudio23SubmixVoice_iface;
#elif XAUDIO2_VER <= 7
    IXAudio27SubmixVoice IXAudio27SubmixVoice_iface;
#endif

    BOOL in_use;

    XAUDIO2_VOICE_DETAILS details;

    CRITICAL_SECTION lock;

    struct list entry;
} XA2SubmixImpl;

struct _IXAudio2Impl {
    IXAudio2 IXAudio2_iface;
    IXAudio2MasteringVoice IXAudio2MasteringVoice_iface;

#if XAUDIO2_VER == 0
    IXAudio20 IXAudio20_iface;
#elif XAUDIO2_VER <= 2
    IXAudio22 IXAudio22_iface;
#elif XAUDIO2_VER <= 7
    IXAudio27 IXAudio27_iface;
#endif

#if XAUDIO2_VER == 0
    IXAudio20MasteringVoice IXAudio20MasteringVoice_iface;
#elif XAUDIO2_VER <= 3
    IXAudio23MasteringVoice IXAudio23MasteringVoice_iface;
#elif XAUDIO2_VER <= 7
    IXAudio27MasteringVoice IXAudio27MasteringVoice_iface;
#endif

    LONG ref;

    CRITICAL_SECTION lock;

    HANDLE engine, mmevt;
    BOOL stop_engine;

    struct list source_voices;
    struct list submix_voices;

    IMMDeviceEnumerator *devenum;

    WCHAR **devids;
    UINT32 ndevs;

    UINT32 last_query_glitches;

    IAudioClient *aclient;
    IAudioRenderClient *render;

    UINT32 period_frames;

    WAVEFORMATEXTENSIBLE fmt;

    ALCdevice *al_device;
    ALCcontext *al_ctx;

    UINT32 ncbs;
    IXAudio2EngineCallback **cbs;

    BOOL running;
};

#if XAUDIO2_VER == 0
extern const IXAudio20SourceVoiceVtbl XAudio20SourceVoice_Vtbl DECLSPEC_HIDDEN;
extern const IXAudio20SubmixVoiceVtbl XAudio20SubmixVoice_Vtbl DECLSPEC_HIDDEN;
extern const IXAudio20MasteringVoiceVtbl XAudio20MasteringVoice_Vtbl DECLSPEC_HIDDEN;
#elif XAUDIO2_VER <= 3
extern const IXAudio23SourceVoiceVtbl XAudio23SourceVoice_Vtbl DECLSPEC_HIDDEN;
extern const IXAudio23SubmixVoiceVtbl XAudio23SubmixVoice_Vtbl DECLSPEC_HIDDEN;
extern const IXAudio23MasteringVoiceVtbl XAudio23MasteringVoice_Vtbl DECLSPEC_HIDDEN;
#elif XAUDIO2_VER <= 7
extern const IXAudio27SourceVoiceVtbl XAudio27SourceVoice_Vtbl DECLSPEC_HIDDEN;
extern const IXAudio27SubmixVoiceVtbl XAudio27SubmixVoice_Vtbl DECLSPEC_HIDDEN;
extern const IXAudio27MasteringVoiceVtbl XAudio27MasteringVoice_Vtbl DECLSPEC_HIDDEN;
#endif

#if XAUDIO2_VER == 0
extern const IXAudio20Vtbl XAudio20_Vtbl DECLSPEC_HIDDEN;
#elif XAUDIO2_VER <= 2
extern const IXAudio22Vtbl XAudio22_Vtbl DECLSPEC_HIDDEN;
#elif XAUDIO2_VER <= 7
extern const IXAudio27Vtbl XAudio27_Vtbl DECLSPEC_HIDDEN;
#endif

extern IClassFactory *make_xapo_factory(REFCLSID clsid) DECLSPEC_HIDDEN;
extern HRESULT xaudio2_initialize(IXAudio2Impl *This, UINT32 flags, XAUDIO2_PROCESSOR proc) DECLSPEC_HIDDEN;