/* dxdiagn
 *
 * Copyright 2003 TransGaming Technologies
 *
 * David Hammerton (david@transgaming.com)
 *
 * Contributions by Rob Crittenden (rcrit@greyoak.com)
 */

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winnls.h"
#include "winreg.h"
#include "objbase.h"
#include "wine/debug.h"
#include "winerror.h"
#include "oleauto.h"
#include "wine/unicode.h"

#include "dxdiag.h"
#include "dxdiagn_private.h"

/* I'm not really sure how the IID_IDxDiagContainer interface should be treated..
 * So I'm treating it as the interface to an undocumented class..
 * SO I guess this isn't really kosher COM.
 */

WINE_DEFAULT_DEBUG_CHANNEL(dxdiagn);

ICOM_VTABLE(IDxDiagContainer) dxDiagContainerVT;

HRESULT WINAPI DxDiagContainer_QueryInterface(PDXDIAGCONTAINER iface,
                                              REFIID riid, LPVOID *obj)
{
    ICOM_THIS(IDxDiagContainerImpl, iface);
    TRACE("(%p)->(%s, %p\n", iface, debugstr_guid(riid), obj);
    if (IsEqualGUID(&IID_IUnknown, riid) ||
        IsEqualGUID(&IID_IDxDiagContainer, riid))
    {
        This->ref++;
        *obj = This;
        return S_OK;
    }
    else
    {
        FIXME("(%p)->(%s, %p): no interface\n", iface, debugstr_guid(riid), obj);
        return E_NOINTERFACE;
    }

}

ULONG WINAPI DxDiagContainer_AddRef(PDXDIAGCONTAINER iface)
{
    ICOM_THIS(IDxDiagContainerImpl, iface);
    TRACE("(%p)->()\n", iface);
    return ++This->ref;
}

ULONG WINAPI DxDiagContainer_Release(PDXDIAGCONTAINER iface)
{
    ICOM_THIS(IDxDiagContainerImpl, iface);
    TRACE("(%p)->()\n", iface);
    return --This->ref; /* don't free us, static resource */
}

/* helper to GetChildContainer */
HRESULT DXDIAGN_Container_GetOneChild(PDXDIAGCONTAINER iface,
                                      LPCWSTR pwszContainer,
                                      IDxDiagContainer **ppInstance)
{
    ICOM_THIS(IDxDiagContainerImpl, iface);
    Container_SubContainer *childContainer;
    int i;

    TRACE("(%p)->(%s, %p)\n", iface, debugstr_w(pwszContainer), ppInstance);

    i = 0;
    do
    {
        childContainer = &This->scContainers[i];
        i++;
    } while (childContainer->pwszContainerName &&
             lstrcmpW(childContainer->pwszContainerName, pwszContainer));

    if (!childContainer->pwszContainerName)
        return E_INVALIDARG;

    *ppInstance = (IDxDiagContainer *)childContainer->pContainer;

    return S_OK;
}

/* Handle multi-level requests and return the container for the lowest level
 * container. For example, return for the request 
 * "DxDiag_DirectSound.DxDiag_SoundDevices" return the container for 
 * DxDiag_SoundDevices.
 */
HRESULT WINAPI DxDiagContainer_GetChildContainer(PDXDIAGCONTAINER iface,
                                                 LPCWSTR pwszContainer,
                                                 IDxDiagContainer **ppInstance)
{
    ICOM_THIS(IDxDiagContainerImpl, iface);
    IDxDiagContainer *tmpContainer;
    LPWSTR tmpStr;
    WCHAR *s;
    INT len;
    HRESULT hr;

    TRACE("(%p)->(%s, %p)\n", iface, debugstr_w(pwszContainer), ppInstance);

    len = strlenW(pwszContainer) + 1;

    tmpContainer = (IDxDiagContainer *)This;
    tmpStr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
    lstrcpynW(tmpStr, pwszContainer, len);

    tmpContainer = (IDxDiagContainer *)This;

    s = strchrW(tmpStr, '.');
    while (s != NULL) {
        *s = '\0';
        hr = DXDIAGN_Container_GetOneChild(tmpContainer, tmpStr, &tmpContainer);
        if (!SUCCEEDED(hr))
            goto error;
        *s++;
        len = strlenW(s) + 1;
        lstrcpynW(tmpStr, s, len);
        s = strchrW(tmpStr, '.');
    }

    // Whatever is left over is the last child node and the one we want
    hr = DXDIAGN_Container_GetOneChild(tmpContainer, tmpStr, ppInstance);

    if (SUCCEEDED(hr))
        DxDiagContainer_AddRef((IDxDiagContainer *)*ppInstance);

error:
    HeapFree(GetProcessHeap(), 0, tmpStr);

    return hr;
}

HRESULT WINAPI DxDiagContainer_GetNumberOfChildContainers(
                                                 PDXDIAGCONTAINER iface,
                                                 DWORD *pdwCount)
{
    ICOM_THIS(IDxDiagContainerImpl, iface);
    Container_SubContainer *childContainer;
    int i;

    i = 0;
    do
    {
        childContainer = &This->scContainers[i];
        i++;
    } while (childContainer->pwszContainerName);

    *pdwCount = i - 1;

    TRACE("(%p)->(%ld)\n", iface, *pdwCount);

    return S_OK;
}

HRESULT WINAPI DxDiagContainer_EnumChildContainerNames(PDXDIAGCONTAINER iface,
                                                       DWORD dwIndex,
                                                       LPWSTR pwszContainer,
                                                       DWORD cchContainer)
{
    ICOM_THIS(IDxDiagContainerImpl, iface);
    Container_SubContainer *childContainer;

    childContainer = &This->scContainers[dwIndex];

    lstrcpynW(pwszContainer, childContainer->pwszContainerName, cchContainer);

    TRACE("(%p)->(%s)\n", iface, debugstr_w(childContainer->pwszContainerName));

    return S_OK;
}

/*
 * There are 4 VARIANT property types.
 * The property name contains a hungarian notation prefix.
 *
 * VARTYPE   MEMBER    PREFIX
 * 
 *  VT_UI4    ulVal      dw,i
 *  VT_I4     lVal       l
 *  VT_BOOL   boolVal    b
 *  VT_BSTR   bstrVal    sz,ull
 *
 * The DX9 sample program DxDiagReport has some helper functions that clearly
 * show which PREFIX goes with which VARTYPE.
 */

HRESULT WINAPI DxDiagContainer_GetProp(PDXDIAGCONTAINER iface,
                                       LPCWSTR pwszPropname,
                                       VARIANT *pvarProp)
{
    ICOM_THIS(IDxDiagContainerImpl, iface);
    Container_Property *property;
    int i;

    TRACE("(%p)->(%s, %p)\n", iface, debugstr_w(pwszPropname), pvarProp);

    i = 0;
    do
    {
        property = &This->pProperties[i];
        i++;
    } while (property->pwszPropName &&
             lstrcmpW(property->pwszPropName, pwszPropname));

    if (!property->pwszPropName)
        return E_INVALIDARG;

    switch (property->varType)
    {
        case DXD_DWORD:
            V_VT(pvarProp) = VT_UI4;
            V_UI4(pvarProp) = property->dw;
            break;
        case DXD_INT:
            V_VT(pvarProp) = VT_UI4;
            V_UI4(pvarProp) = property->i;
            break;
       case DXD_ULL:
            V_VT(pvarProp) = VT_BSTR;
            V_BSTR(pvarProp) = SysAllocString(property->ull);
            break;
       case DXD_LONG:
            V_VT(pvarProp) = VT_I4;
            V_I4(pvarProp) = property->l;
            break;
        case DXD_BOOL:
            V_VT(pvarProp) = VT_BOOL;
            V_BOOL(pvarProp) = property->b;
            break;
        case DXD_STR:
            V_VT(pvarProp) = VT_BSTR;
            V_BSTR(pvarProp) = SysAllocString(property->psz);
            break;
        case DXD_EMPTY:
            FIXME("empty property??");
            return E_INVALIDARG;
    }

    return S_OK;
}

HRESULT WINAPI DxDiagContainer_GetNumberOfProps(PDXDIAGCONTAINER iface,
                                                DWORD *pdwCount)
{
    ICOM_THIS(IDxDiagContainerImpl, iface);
    Container_Property *property;
    int i;

    i = 0;
    do
    {
        property = &This->pProperties[i];
        i++;
    } while (property->pwszPropName);

    *pdwCount = i - 1;

    TRACE("(%p)->(%ld)\n", iface, *pdwCount);

    return S_OK;
}

HRESULT WINAPI DxDiagContainer_EnumPropNames(PDXDIAGCONTAINER iface,
                                             DWORD dwIndex,
                                             LPWSTR pwszPropName,
                                             DWORD cchPropName)
{
    ICOM_THIS(IDxDiagContainerImpl, iface);
    Container_Property *property;

    property = &This->pProperties[dwIndex];

    lstrcpynW(pwszPropName, property->pwszPropName, cchPropName);

    TRACE("(%p)->(%s)\n", iface, debugstr_w(property->pwszPropName));

    return S_OK;
}

ICOM_VTABLE(IDxDiagContainer) dxDiagContainerVT =
{
    ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
    DxDiagContainer_QueryInterface,
    DxDiagContainer_AddRef,
    DxDiagContainer_Release,
    DxDiagContainer_GetNumberOfChildContainers,
    DxDiagContainer_EnumChildContainerNames,
    DxDiagContainer_GetChildContainer,
    DxDiagContainer_GetNumberOfProps,
    DxDiagContainer_EnumPropNames,
    DxDiagContainer_GetProp
};

