/* Implementation of the Audio Capture Filter (CLSID_AudioRecord)
 *
 * Copyright 2015 Damjan Jovanovic
 *
 * 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 <stdarg.h>

#define COBJMACROS

#include "windef.h"
#include "winbase.h"
#include "wtypes.h"
#include "wingdi.h"
#include "winuser.h"
#include "dshow.h"

#include "qcap_main.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(qcap);

typedef struct {
    IUnknown IUnknown_iface;
    IUnknown *outerUnknown;
    BaseFilter filter;
    IPersistPropertyBag IPersistPropertyBag_iface;
    BaseOutputPin *output;
} AudioRecord;

static inline AudioRecord *impl_from_IUnknown(IUnknown *iface)
{
    return CONTAINING_RECORD(iface, AudioRecord, IUnknown_iface);
}

static inline AudioRecord *impl_from_BaseFilter(BaseFilter *filter)
{
    return CONTAINING_RECORD(filter, AudioRecord, filter);
}

static inline AudioRecord *impl_from_IBaseFilter(IBaseFilter *iface)
{
    BaseFilter *filter = CONTAINING_RECORD(iface, BaseFilter, IBaseFilter_iface);
    return impl_from_BaseFilter(filter);
}

static inline AudioRecord *impl_from_IPersistPropertyBag(IPersistPropertyBag *iface)
{
    return CONTAINING_RECORD(iface, AudioRecord, IPersistPropertyBag_iface);
}

static HRESULT WINAPI Unknown_QueryInterface(IUnknown *iface, REFIID riid, LPVOID *ppv)
{
    AudioRecord *This = impl_from_IUnknown(iface);
    if (IsEqualIID(riid, &IID_IUnknown)) {
        TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv);
        *ppv = &This->filter.IBaseFilter_iface;
    } else if (IsEqualIID(riid, &IID_IPersist)) {
        TRACE("(%p)->(IID_IPersist, %p)\n", This, ppv);
        *ppv = &This->filter.IBaseFilter_iface;
    } else if (IsEqualIID(riid, &IID_IMediaFilter)) {
        TRACE("(%p)->(IID_IMediaFilter, %p)\n", This, ppv);
        *ppv = &This->filter.IBaseFilter_iface;
    } else if (IsEqualIID(riid, &IID_IBaseFilter)) {
        TRACE("(%p)->(IID_IBaseFilter, %p)\n", This, ppv);
        *ppv = &This->filter.IBaseFilter_iface;
    } else if (IsEqualIID(riid, &IID_IPersistPropertyBag)) {
        TRACE("(%p)->(IID_IPersistPropertyBag, %p)\n", This, ppv);
        *ppv = &This->IPersistPropertyBag_iface;
    } else {
        FIXME("(%p): no interface for %s\n", This, debugstr_guid(riid));
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI Unknown_AddRef(IUnknown *iface)
{
    AudioRecord *This = impl_from_IUnknown(iface);
    return BaseFilterImpl_AddRef(&This->filter.IBaseFilter_iface);
}

static ULONG WINAPI Unknown_Release(IUnknown *iface)
{
    AudioRecord *This = impl_from_IUnknown(iface);
    ULONG ref = BaseFilterImpl_Release(&This->filter.IBaseFilter_iface);
    TRACE("(%p/%p)->() ref=%d\n", iface, This, ref);
    if (!ref) {
        CoTaskMemFree(This);
    }
    return ref;
}

static const IUnknownVtbl UnknownVtbl = {
    Unknown_QueryInterface,
    Unknown_AddRef,
    Unknown_Release
};

static HRESULT WINAPI AudioRecord_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv)
{
    AudioRecord *This = impl_from_IBaseFilter(iface);
    return IUnknown_QueryInterface(This->outerUnknown, riid, ppv);
}

static ULONG WINAPI AudioRecord_AddRef(IBaseFilter *iface)
{
    AudioRecord *This = impl_from_IBaseFilter(iface);
    return IUnknown_AddRef(This->outerUnknown);
}

static ULONG WINAPI AudioRecord_Release(IBaseFilter *iface)
{
    AudioRecord *This = impl_from_IBaseFilter(iface);
    return IUnknown_Release(This->outerUnknown);
}

static HRESULT WINAPI AudioRecord_Stop(IBaseFilter *iface)
{
    AudioRecord *This = impl_from_IBaseFilter(iface);
    FIXME("(%p): stub\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI AudioRecord_Pause(IBaseFilter *iface)
{
    AudioRecord *This = impl_from_IBaseFilter(iface);
    FIXME("(%p): stub\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI AudioRecord_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
{
    AudioRecord *This = impl_from_IBaseFilter(iface);
    FIXME("(%p, %x%08x): stub\n", This, (ULONG)(tStart >> 32), (ULONG)tStart);
    return E_NOTIMPL;
}

static HRESULT WINAPI AudioRecord_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
{
    AudioRecord *This = impl_from_IBaseFilter(iface);
    FIXME("(%p)->(%s, %p): stub\n", This, debugstr_w(Id), ppPin);
    return E_NOTIMPL;
}

static const IBaseFilterVtbl AudioRecordVtbl = {
    AudioRecord_QueryInterface,
    AudioRecord_AddRef,
    AudioRecord_Release,
    BaseFilterImpl_GetClassID,
    AudioRecord_Stop,
    AudioRecord_Pause,
    AudioRecord_Run,
    BaseFilterImpl_GetState,
    BaseFilterImpl_SetSyncSource,
    BaseFilterImpl_GetSyncSource,
    BaseFilterImpl_EnumPins,
    AudioRecord_FindPin,
    BaseFilterImpl_QueryFilterInfo,
    BaseFilterImpl_JoinFilterGraph,
    BaseFilterImpl_QueryVendorInfo
};

static IPin* WINAPI AudioRecord_GetPin(BaseFilter *iface, int pos)
{
    AudioRecord *This = impl_from_BaseFilter(iface);
    FIXME("(%p, %d): stub\n", This, pos);
    return NULL;
}

static LONG WINAPI AudioRecord_GetPinCount(BaseFilter *iface)
{
    AudioRecord *This = impl_from_BaseFilter(iface);
    FIXME("(%p): stub\n", This);
    return 0;
}

static const BaseFilterFuncTable AudioRecordFuncs = {
    AudioRecord_GetPin,
    AudioRecord_GetPinCount
};

static HRESULT WINAPI PPB_QueryInterface(IPersistPropertyBag *iface, REFIID riid, LPVOID *ppv)
{
    AudioRecord *This = impl_from_IPersistPropertyBag(iface);
    return IUnknown_QueryInterface(This->outerUnknown, riid, ppv);
}

static ULONG WINAPI PPB_AddRef(IPersistPropertyBag *iface)
{
    AudioRecord *This = impl_from_IPersistPropertyBag(iface);
    return IUnknown_AddRef(This->outerUnknown);
}

static ULONG WINAPI PPB_Release(IPersistPropertyBag *iface)
{
    AudioRecord *This = impl_from_IPersistPropertyBag(iface);
    return IUnknown_Release(This->outerUnknown);
}

static HRESULT WINAPI PPB_GetClassID(IPersistPropertyBag *iface, CLSID *pClassID)
{
    AudioRecord *This = impl_from_IPersistPropertyBag(iface);
    TRACE("(%p/%p)->(%p)\n", iface, This, pClassID);
    return IBaseFilter_GetClassID(&This->filter.IBaseFilter_iface, pClassID);
}

static HRESULT WINAPI PPB_InitNew(IPersistPropertyBag *iface)
{
    AudioRecord *This = impl_from_IPersistPropertyBag(iface);
    FIXME("(%p/%p)->(): stub\n", iface, This);
    return E_NOTIMPL;
}

static HRESULT WINAPI PPB_Load(IPersistPropertyBag *iface, IPropertyBag *pPropBag,
        IErrorLog *pErrorLog)
{
    AudioRecord *This = impl_from_IPersistPropertyBag(iface);
    HRESULT hr;
    VARIANT var;
    static const WCHAR WaveInIDW[] = {'W','a','v','e','I','n','I','D',0};

    TRACE("(%p/%p)->(%p, %p)\n", iface, This, pPropBag, pErrorLog);

    V_VT(&var) = VT_I4;
    hr = IPropertyBag_Read(pPropBag, WaveInIDW, &var, pErrorLog);
    if (SUCCEEDED(hr))
    {
        FIXME("FIXME: implement opening waveIn device %d\n", V_I4(&var));
    }

    return hr;
}

static HRESULT WINAPI PPB_Save(IPersistPropertyBag *iface, IPropertyBag *pPropBag,
        BOOL fClearDirty, BOOL fSaveAllProperties)
{
    AudioRecord *This = impl_from_IPersistPropertyBag(iface);
    FIXME("(%p/%p)->(%p, %u, %u): stub\n", iface, This, pPropBag, fClearDirty, fSaveAllProperties);
    return E_NOTIMPL;
}

static const IPersistPropertyBagVtbl PersistPropertyBagVtbl =
{
    PPB_QueryInterface,
    PPB_AddRef,
    PPB_Release,
    PPB_GetClassID,
    PPB_InitNew,
    PPB_Load,
    PPB_Save
};

IUnknown* WINAPI QCAP_createAudioCaptureFilter(IUnknown *outer, HRESULT *phr)
{
    HRESULT hr;
    AudioRecord *This = NULL;

    FIXME("(%p, %p): the entire CLSID_AudioRecord implementation is just stubs\n", outer, phr);

    This = CoTaskMemAlloc(sizeof(*This));
    if (This == NULL) {
        hr = E_OUTOFMEMORY;
        goto end;
    }
    memset(This, 0, sizeof(*This));
    This->IUnknown_iface.lpVtbl = &UnknownVtbl;
    This->IPersistPropertyBag_iface.lpVtbl = &PersistPropertyBagVtbl;
    if (outer)
        This->outerUnknown = outer;
    else
        This->outerUnknown = &This->IUnknown_iface;

    hr = BaseFilter_Init(&This->filter, &AudioRecordVtbl, &CLSID_AudioRecord,
            (DWORD_PTR)(__FILE__ ": AudioRecord.csFilter"), &AudioRecordFuncs);

end:
    *phr = hr;
    if (SUCCEEDED(hr)) {
        return (IUnknown*)&This->filter.IBaseFilter_iface;
    } else {
        if (This)
            IBaseFilter_Release(&This->filter.IBaseFilter_iface);
        return NULL;
    }
}