/*		DirectMusic
 *
 * Copyright 2002-2003 TransGaming Technologies Inc. 
 *
 */

#include "config.h"
#include <assert.h>
#include <string.h>

#include "wine/debug.h"
#include "winbase.h"
#include "winerror.h"
#include "windef.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "mmsystem.h"
#include "ole2.h"
#include "strmif.h"
#include "control.h"
#include "uuids.h"
#include "errors.h"
#include "dsound.h"
#include "dmusici.h"
#include "dmusicf.h"
#include "dmime_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(dmusic);

typedef struct TrackItem {
    struct TrackItem* next;
    IDirectMusicTrack* pTrack;
    DWORD dwGroup;
} TrackItem;

static TrackItem* AllocTrackItem(void)
{
	return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TrackItem));
}

static void FreeTrackItem(TrackItem* item)
{
	if (item->pTrack) IDirectMusicTrack_Release(item->pTrack);
	HeapFree(GetProcessHeap(), 0, item);
}

static void FreeTrackItems(TrackItem* item)
{
	while (item) {
		TrackItem* next = item->next;
		FreeTrackItem(item);
		item = next;
	}
}

/*****************************************************************************
 * IDirectMusicSegment implementation structure
 */
typedef struct IDirectMusicSegmentImpl IDirectMusicSegmentImpl;
struct IDirectMusicSegmentImpl
{
    /* IUnknown fields */
    ICOM_VFIELD(IDirectMusicSegment8);
    ICOM_VTABLE(IDirectMusicObject) *lpOVtbl;
    ICOM_VTABLE(IPersistStream) *lpPSVtbl;
    DWORD ref;
    /* IDirectMusicSegment fields */
    DMUS_OBJECTDESC desc;
    DMUS_IO_SEGMENT_HEADER hdr;
    CRITICAL_SECTION track_cs;
    TrackItem* tracks;
};

static HRESULT WINAPI IDirectMusicSegmentImpl_ParseStream(
	IDirectMusicSegmentImpl* This,
	IStream* pStream,
	LPDMUS_OBJECTDESC pDesc);

static HRESULT WINAPI IDirectMusicSegmentImpl_QueryInterface(
	IDirectMusicSegment8* iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS(IDirectMusicSegmentImpl,iface);

	TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
	if (IsEqualGUID(riid, &IID_IUnknown) ||
	    IsEqualGUID(riid, &IID_IDirectMusicSegment) ||
	    IsEqualGUID(riid, &IID_IDirectMusicSegment8)) {
	    *ppobj = (LPVOID)This;
	    This->ref++;
	    return S_OK;
	}
	if (IsEqualGUID(riid, &IID_IDirectMusicObject)) {
	    *ppobj = (LPVOID)&This->lpOVtbl;
	    This->ref++;
	    return S_OK;
	}
	if (IsEqualGUID(riid, &IID_IPersist) ||
	    IsEqualGUID(riid, &IID_IPersistStream)) {
	    *ppobj = (LPVOID)&This->lpPSVtbl;
	    This->ref++;
	    return S_OK;
	}
	FIXME("(%p): no interface for %s\n", This, debugstr_guid(riid));
	return E_NOINTERFACE;
}

static ULONG WINAPI IDirectMusicSegmentImpl_AddRef(IDirectMusicSegment8* iface) {
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	return ++(This->ref);
}

static ULONG WINAPI IDirectMusicSegmentImpl_Release(IDirectMusicSegment8* iface) {
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	TRACE("(%p), ref was %ld\n",This,This->ref);
	if (!--(This->ref)) {
		FreeTrackItems(This->tracks);
		DeleteCriticalSection(&This->track_cs);
		HeapFree(GetProcessHeap(),0,This);
		return 0;
	}
	return This->ref;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_GetLength(
	IDirectMusicSegment8* iface,
	MUSIC_TIME* pmtLength)
{
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	TRACE("(%p)->(%p)\n", This, pmtLength);
	*pmtLength = This->hdr.mtLength;
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_GetDefaultResolution(
	IDirectMusicSegment8* iface,
	DWORD* pdwResolution)
{
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	TRACE("(%p)->(%p)\n", This, pdwResolution);
	*pdwResolution = This->hdr.dwResolution;
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_SetDefaultResolution(
	IDirectMusicSegment8* iface,
	DWORD dwResolution)
{
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	TRACE("(%p)->(%ld)\n", This, dwResolution);
	This->hdr.dwResolution = dwResolution;
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_GetTrack(
	IDirectMusicSegment8* iface,
	REFGUID rguidType,
	DWORD dwGroupBits,
	DWORD dwIndex,
	IDirectMusicTrack** ppTrack)
{
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	BOOL nullguid;
	TrackItem* track;
	HRESULT hr;

	TRACE("(%p)->(%s,0x%lx,%ld,%p)\n", This,
	      debugstr_guid(rguidType), dwGroupBits, dwIndex, ppTrack);
	nullguid = IsEqualGUID(rguidType, &GUID_NULL);
	EnterCriticalSection(&This->track_cs);
	for (track = This->tracks; track; track = track->next) {
		if (!(track->dwGroup & dwGroupBits)) continue;
		if (!nullguid) {
			IPersistStream *pStream;
			CLSID clsid;
			hr = IDirectMusicTrack_QueryInterface(track->pTrack, &IID_IPersistStream, (void**)&pStream);
			if (FAILED(hr)) continue;
			hr = IPersistStream_GetClassID(pStream, &clsid);
			IPersistStream_Release(pStream);
			if (FAILED(hr)) continue;
			if (!IsEqualGUID(rguidType, &clsid)) continue;
		}
		if (!dwIndex--) {
			/* found it */
			IDirectMusicTrack_AddRef(track->pTrack);
			*ppTrack = track->pTrack;
			break;
		}
	}
	LeaveCriticalSection(&This->track_cs);
	if (!track) return DMUS_E_NOT_FOUND;
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_GetTrackGroup(
	IDirectMusicSegment8* iface,
	IDirectMusicTrack* pTrack,
	DWORD* pdwGroupBits)
{
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	TrackItem* track;
	TRACE("(%p)->(%p,%p)\n", This, pTrack, pdwGroupBits);
	EnterCriticalSection(&This->track_cs);
	for (track = This->tracks; track && track->pTrack != pTrack; track = track->next);
	if (track) *pdwGroupBits = track->dwGroup;
	LeaveCriticalSection(&This->track_cs);
	if (!track) return DMUS_E_NOT_FOUND;
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_InsertTrack(
	IDirectMusicSegment8* iface,
	IDirectMusicTrack* pTrack,
	DWORD dwGroupBits)
{
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	TrackItem* track;
	TRACE("(%p)->(%p,0x%lx)\n", This, pTrack, dwGroupBits);
	EnterCriticalSection(&This->track_cs);
	track = This->tracks;
	if (track) {
		while (track->pTrack != pTrack && track->next)
			track = track->next;
		if (track->pTrack == pTrack) {
			LeaveCriticalSection(&This->track_cs);
			ERR("track already inserted\n");
			return E_FAIL;
		}
		track->next = AllocTrackItem();
		track = track->next;
	} else {
		This->tracks = AllocTrackItem();
		track = This->tracks;
	}
	IDirectMusicTrack_AddRef(pTrack);
	track->pTrack = pTrack;
	track->dwGroup = dwGroupBits;
	LeaveCriticalSection(&This->track_cs);
	IDirectMusicTrack_Init(pTrack, (IDirectMusicSegment*)This);
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_RemoveTrack(
	IDirectMusicSegment8* iface,
	IDirectMusicTrack* pTrack)
{
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	TrackItem* last;
	TrackItem* track;
	TRACE("(%p)->(%p)\n", This, pTrack);
	EnterCriticalSection(&This->track_cs);
	last = NULL;
	track = This->tracks;
	while (track && track->pTrack != pTrack) {
		last = track;
		track = track->next;
	}
	if (track) {
		if (last) last->next = track->next;
		else This->tracks = track->next;
	}
	LeaveCriticalSection(&This->track_cs);
	if (!track) return S_FALSE;
	IDirectMusicTrack_Init(pTrack, NULL); /* just in case */
	FreeTrackItem(track);
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_GetParam(
	IDirectMusicSegment8* iface,
	REFGUID rguidType,
	DWORD dwGroupBits,
	DWORD dwIndex,
	MUSIC_TIME mtTime,
	MUSIC_TIME* pmtNext,
	void* pParam)
{
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	IDirectMusicTrack* pTrack;
	HRESULT hr;

	TRACE("(%p)->(%s,0x%08lx,%ld,%ld,%p,%p)\n", This,
	      debugstr_guid(rguidType), dwGroupBits, dwIndex,
	      mtTime, pmtNext, pParam);

	if (dwIndex == DMUS_SEG_ANYTRACK) {
		DWORD c = 0;
		while (TRUE) {
			hr = IDirectMusicSegment8_GetTrack(iface, &GUID_NULL,
							   dwGroupBits, c, &pTrack);
			if (FAILED(hr)) break;
			c++;
			hr = IDirectMusicTrack_GetParam(pTrack, rguidType,
							mtTime, pmtNext, pParam);
			IDirectMusicTrack_Release(pTrack);
			if (SUCCEEDED(hr)) return hr;
		}
		return DMUS_E_TRACK_NOT_FOUND;
	}

	hr = IDirectMusicSegment8_GetTrack(iface, &GUID_NULL, dwGroupBits, dwIndex, &pTrack);
	if (FAILED(hr)) return DMUS_E_TRACK_NOT_FOUND;

	hr = IDirectMusicTrack_GetParam(pTrack, rguidType, mtTime, pmtNext, pParam);
	IDirectMusicTrack_Release(pTrack);

	return hr;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_Download(
	IDirectMusicSegment8* iface,
	IUnknown* pAudioPath)
{
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	FIXME("(%p)->(%p): stub\n", This, pAudioPath);
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_Unload(
	IDirectMusicSegment8* iface,
	IUnknown* pAudioPath)
{
	ICOM_THIS(IDirectMusicSegmentImpl,iface);
	FIXME("(%p)->(%p): stub\n", This, pAudioPath);
	return S_OK;
}

static ICOM_VTABLE(IDirectMusicSegment8) dmsvt = 
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	IDirectMusicSegmentImpl_QueryInterface,
	IDirectMusicSegmentImpl_AddRef,
	IDirectMusicSegmentImpl_Release,
	IDirectMusicSegmentImpl_GetLength,
	(void*)0xdeaf4006, /* SetLength */
	(void*)0xdeaf4007, /* GetRepeats */
	(void*)0xdeaf4008, /* SetRepeats */
	IDirectMusicSegmentImpl_GetDefaultResolution,
	IDirectMusicSegmentImpl_SetDefaultResolution,
	IDirectMusicSegmentImpl_GetTrack,
	IDirectMusicSegmentImpl_GetTrackGroup,
	IDirectMusicSegmentImpl_InsertTrack,
	IDirectMusicSegmentImpl_RemoveTrack,
	(void*)0xdeaf400f, /* InitPlay */
	(void*)0xdeaf4010, /* GetGraph */
	(void*)0xdeaf4011, /* SetGraph */
	(void*)0xdeaf4012, /* AddNotificationType */
	(void*)0xdeaf4013, /* RemoveNotificationType */
	IDirectMusicSegmentImpl_GetParam,
	(void*)0xdeaf4015, /* SetParam */
	(void*)0xdeaf4016, /* Clone */
	(void*)0xdeaf4017, /* SetStartPoint */
	(void*)0xdeaf4018, /* GetStartPoint */
	(void*)0xdeaf4019, /* SetLoopPoints */
	(void*)0xdeaf401a, /* GetLoopPoints */
	(void*)0xdeaf401b, /* SetPChannelsUsed */
	(void*)0xdeaf401c, /* SetTrackConfig */
	(void*)0xdeaf401d, /* GetAudioPathConfig */
	(void*)0xdeaf401e, /* Compose */
	IDirectMusicSegmentImpl_Download,
	IDirectMusicSegmentImpl_Unload
};

static HRESULT WINAPI IDirectMusicSegmentObject_QueryInterface(
	IDirectMusicObject* iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpOVtbl,iface);
	return IDirectMusicSegment8_QueryInterface((IDirectMusicSegment8*)This, riid, ppobj);
}

static ULONG WINAPI IDirectMusicSegmentObject_AddRef(IDirectMusicObject* iface) {
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpOVtbl,iface);
	return IDirectMusicSegment8_AddRef((IDirectMusicSegment8*)This);
}

static ULONG WINAPI IDirectMusicSegmentObject_Release(IDirectMusicObject* iface) {
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpOVtbl,iface);
	return IDirectMusicSegment8_Release((IDirectMusicSegment8*)This);
}

static HRESULT WINAPI IDirectMusicSegmentObject_GetDescriptor(
	IDirectMusicObject* iface,
	LPDMUS_OBJECTDESC pDesc)
{
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpOVtbl,iface);
	TRACE("(%p)->(%p)\n", This, pDesc);
	*pDesc = This->desc;
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentObject_SetDescriptor(
	IDirectMusicObject* iface,
	LPDMUS_OBJECTDESC pDesc)
{
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpOVtbl,iface);
	DWORD dwValid = pDesc->dwValidData;
	DWORD dwUsed = 0;
	TRACE("(%p)->(%p)\n", This, pDesc);
	if (dwValid & DMUS_OBJ_OBJECT) {
		memcpy(&This->desc.guidObject, &pDesc->guidObject, sizeof(This->desc.guidObject));
		dwUsed |= DMUS_OBJ_OBJECT;
	}
	if (dwValid & DMUS_OBJ_CLASS) {
		memcpy(&This->desc.guidClass, &pDesc->guidClass, sizeof(This->desc.guidClass));
		dwUsed |= DMUS_OBJ_CLASS;
	}
	if (dwValid & DMUS_OBJ_NAME) {
		memcpy(This->desc.wszName, pDesc->wszName, sizeof(This->desc.wszName));
		dwUsed |= DMUS_OBJ_NAME;
	}
	if (dwValid & DMUS_OBJ_CATEGORY) {
		memcpy(This->desc.wszCategory, pDesc->wszCategory, sizeof(This->desc.wszCategory));
		dwUsed |= DMUS_OBJ_CATEGORY;
	}
	if (dwValid & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) {
		memcpy(This->desc.wszFileName, pDesc->wszFileName, sizeof(This->desc.wszFileName));
		dwUsed |= DMUS_OBJ_FILENAME;
		if (dwValid & DMUS_OBJ_FULLPATH)
			dwUsed |= DMUS_OBJ_FULLPATH;
	}
	if (dwValid & DMUS_OBJ_VERSION) {
		memcpy(&This->desc.vVersion, &pDesc->vVersion, sizeof(This->desc.vVersion));
		dwUsed |= DMUS_OBJ_VERSION;
	}
	if (dwValid & DMUS_OBJ_DATE) {
		memcpy(&This->desc.ftDate, &pDesc->ftDate, sizeof(This->desc.ftDate));
		dwUsed |= DMUS_OBJ_DATE;
	}
	This->desc.dwValidData |= dwUsed;
	dwValid &= ~dwUsed;
	if (dwValid) {
		FIXME("unhandled data: %08lx\n", dwValid);
		pDesc->dwValidData = dwValid;
		return S_FALSE;
	}
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentObject_ParseDescriptor(
	IDirectMusicObject* iface,
	IStream* pStream,
	LPDMUS_OBJECTDESC pDesc)
{
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpOVtbl,iface);
	TRACE("(%p)->(%p)\n", This, pDesc);
	if (!pDesc) return E_POINTER;
	return IDirectMusicSegmentImpl_ParseStream(This, pStream, pDesc);
}

static ICOM_VTABLE(IDirectMusicObject) dmovt =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	IDirectMusicSegmentObject_QueryInterface,
	IDirectMusicSegmentObject_AddRef,
	IDirectMusicSegmentObject_Release,
	IDirectMusicSegmentObject_GetDescriptor,
	IDirectMusicSegmentObject_SetDescriptor,
	IDirectMusicSegmentObject_ParseDescriptor
};

static HRESULT WINAPI IDirectMusicSegmentStream_QueryInterface(
	IPersistStream* iface,REFIID riid,LPVOID *ppobj
) {
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpPSVtbl,iface);
	return IDirectMusicSegment8_QueryInterface((IDirectMusicSegment8*)This, riid, ppobj);
}

static ULONG WINAPI IDirectMusicSegmentStream_AddRef(IPersistStream* iface) {
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpPSVtbl,iface);
	return IDirectMusicSegment8_AddRef((IDirectMusicSegment8*)This);
}

static ULONG WINAPI IDirectMusicSegmentStream_Release(IPersistStream* iface) {
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpPSVtbl,iface);
	return IDirectMusicSegment8_Release((IDirectMusicSegment8*)This);
}

static HRESULT WINAPI IDirectMusicSegmentStream_GetClassID(
	IPersistStream* iface,
	CLSID* pClassID)
{
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpPSVtbl,iface);
	TRACE("(%p)->(%p)\n", This, pClassID);
	memcpy(pClassID, &CLSID_DirectMusicSegment, sizeof(CLSID));
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentStream_IsDirty(
	IPersistStream* iface)
{
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpPSVtbl,iface);
	TRACE("(%p)->()\n", This);
	return S_FALSE; /* who cares */
}

static HRESULT WINAPI IDirectMusicSegmentStream_Load(
	IPersistStream* iface,
	IStream* pStream)
{
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpPSVtbl,iface);
	TRACE("(%p)->(%p)\n", This, pStream);
	return IDirectMusicSegmentImpl_ParseStream(This, pStream, NULL);
}

static HRESULT WINAPI IDirectMusicSegmentStream_Save(
	IPersistStream* iface,
	IStream* pStream,
	BOOL fClearDirty)
{
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpPSVtbl,iface);
	FIXME("(%p)->(%p,%d): stub\n", This, pStream, fClearDirty);
	return E_NOTIMPL;
}

static HRESULT WINAPI IDirectMusicSegmentStream_GetSizeMax(
	IPersistStream* iface,
	ULARGE_INTEGER* pcbSize)
{
	ICOM_THIS_MULTI(IDirectMusicSegmentImpl,lpPSVtbl,iface);
	FIXME("(%p)->(%p): stub\n", This, pcbSize);
	pcbSize->QuadPart = 0;
	return E_NOTIMPL;
}

static ICOM_VTABLE(IPersistStream) dmpsvt =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	IDirectMusicSegmentStream_QueryInterface,
	IDirectMusicSegmentStream_AddRef,
	IDirectMusicSegmentStream_Release,
	IDirectMusicSegmentStream_GetClassID,
	IDirectMusicSegmentStream_IsDirty,
	IDirectMusicSegmentStream_Load,
	IDirectMusicSegmentStream_Save,
	IDirectMusicSegmentStream_GetSizeMax
};

static HRESULT WINAPI IDirectMusicSegmentImpl_LoadTrack(
	HMMIO hmmio,
	LPMMCKINFO pck,
	LPCGUID guidClass,
	IDirectMusicTrack** ppTrack)
{
	IPersistStream* pObjStream = NULL;
	IStream* pStream = NULL;
	LARGE_INTEGER ofs;
	HRESULT hr;

	/* create object of class */
	hr = CoCreateInstance(guidClass, NULL, CLSCTX_INPROC,
			      &IID_IPersistStream, (void**)&pObjStream);
	if (FAILED(hr)) return hr;

	hr = DMIME_CloneStream(hmmio, &pStream);
	if (FAILED(hr)) goto fail;

	/* now, I'm not sure where we're supposed to allow the object
	 * to start reading, but until we find otherwise, I'll do the
	 * easiest thing for the track - rewind to the header of this
	 * data chunk, and let the track object descend right into it
	 * (that way it will also know the size of the chunk). */
	if (pck->ckid == FOURCC_RIFF || pck->ckid == FOURCC_LIST)
		ofs.QuadPart = -12;
	else
		ofs.QuadPart = -8;
	IStream_Seek(pStream, ofs, STREAM_SEEK_CUR, NULL);

	/* let track object load data */
	hr = IPersistStream_Load(pObjStream, pStream);
	IStream_Release(pStream);
	if (FAILED(hr)) goto fail;

	/* get track interface */
	hr = IPersistStream_QueryInterface(pObjStream, &IID_IDirectMusicTrack, (void**)ppTrack);
fail:
	IPersistStream_Release(pObjStream);
	return hr;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_ParseTrackForm(
	IDirectMusicSegmentImpl* This,
	HMMIO hmmio,
	LPMMCKINFO pck,
	LPDMUS_OBJECTDESC pDesc,
	IDirectMusicTrack** ppTrack)
{
	MMCKINFO ck;
	MMRESULT mmres;
	DMUS_IO_TRACK_HEADER hdr;
	DMUS_IO_TRACK_EXTRAS_HEADER xhdr;
	LPDMUS_OBJECTDESC desc;
	DMUS_OBJECTDESC new_desc;
	IDirectMusicTrack* pTrack = NULL;

	memset(&new_desc, 0, sizeof(new_desc));
	new_desc.dwSize = sizeof(new_desc);
	desc = pDesc ? pDesc : &new_desc;

	mmres = mmioDescend(hmmio, &ck, pck, 0);
	if (mmres) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	/* first chunk should be track header */
	if (ck.ckid != DMUS_FOURCC_TRACK_CHUNK) {
		ERR("Track chunk not found\n");
		return E_FAIL;
	}
	STRUCT_READ_START(hdr)
	STRUCT_READ(hdr, guidClassID)
	STRUCT_READ(hdr, dwPosition)
	STRUCT_READ(hdr, dwGroup)
	STRUCT_READ(hdr, ckid)
	STRUCT_READ(hdr, fccType)
	STRUCT_READ_END(hdr)
	mmioAscend(hmmio, &ck, 0);
	TRACE("Track class: %s\n", debugstr_guid(&hdr.guidClassID));
	memcpy(&desc->guidClass, &hdr.guidClassID, sizeof(desc->guidClass));
	desc->dwValidData |= DMUS_OBJ_CLASS;
	memset(&xhdr, 0, sizeof(xhdr));
	/* read remaining chunks */
	while (!(mmres = mmioDescend(hmmio, &ck, pck, 0))) {
		HRESULT hr = DMIME_CommonChunk(hmmio, &ck, pDesc);
		if (FAILED(hr)) return hr;
		if (hr == S_OK || pDesc) {
			mmioAscend(hmmio, &ck, 0);
			continue;
		}
		if (ck.ckid == DMUS_FOURCC_TRACK_EXTRAS_CHUNK) {
			STRUCT_READ_START(xhdr)
			STRUCT_READ(xhdr, dwFlags)
			STRUCT_READ(xhdr, dwPriority)
			STRUCT_READ_END(hdr)
		}
		else if (ck.ckid == FOURCC_LIST) {
			if (!hdr.ckid && ck.fccType == hdr.fccType) {
				/* track data in list form */
				hr = IDirectMusicSegmentImpl_LoadTrack(hmmio, &ck, &hdr.guidClassID, &pTrack);
				if (FAILED(hr)) return hr;
			}
			else {
				FIXME("unknown list %08lx\n", ck.ckid);
				break;
			}
		}
		else if (hdr.ckid && ck.ckid == hdr.ckid) {
			/* track data in chunk form */
			hr = IDirectMusicSegmentImpl_LoadTrack(hmmio, &ck, &hdr.guidClassID, &pTrack);
			if (FAILED(hr)) return hr;
		}
		else {
			FIXME("unknown chunk %08lx\n", ck.ckid);
			break;
		}
		mmioAscend(hmmio, &ck, 0);
	}
	if (mmres != MMIOERR_CHUNKNOTFOUND) {
		ERR("mmio error %d\n", mmres);
		if (pTrack) IDirectMusicTrack_Release(pTrack);
		return E_FAIL;
	}
	if (pTrack) {
		/* successfully created a track */
		if (This) {
			/* insert into segment */
			IDirectMusicSegmentImpl_InsertTrack((IDirectMusicSegment8*)This,
							    pTrack, hdr.dwGroup);
		}
		if (ppTrack) *ppTrack = pTrack;
		else IDirectMusicTrack_Release(pTrack);
	}
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_ParseTrackList(
	IDirectMusicSegmentImpl* This,
	HMMIO hmmio,
	LPMMCKINFO pck)
{
	MMCKINFO ck;
	MMRESULT mmres;

	while (!(mmres = mmioDescend(hmmio, &ck, pck, 0))) {
		HRESULT hr;
		if (ck.ckid != FOURCC_RIFF || ck.fccType != DMUS_FOURCC_TRACK_FORM) {
			ERR("Track form not found\n");
			return E_FAIL;
		}
		hr = IDirectMusicSegmentImpl_ParseTrackForm(This, hmmio, &ck, NULL, NULL);
		if (FAILED(hr)) return hr;
		mmioAscend(hmmio, &ck, 0);
	}
	if (mmres != MMIOERR_CHUNKNOTFOUND) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_ParseForm(
	IDirectMusicSegmentImpl* This,
	HMMIO hmmio,
	LPMMCKINFO pck,
	LPDMUS_OBJECTDESC pDesc)
{
	MMCKINFO ck;
	MMRESULT mmres;
	LPDMUS_OBJECTDESC desc;

	desc = pDesc ? pDesc : &This->desc;

	mmres = mmioDescend(hmmio, &ck, pck, 0);
	if (mmres) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	/* first chunk should be segment header */
	if (ck.ckid != DMUS_FOURCC_SEGMENT_CHUNK) {
		ERR("Segment chunk not found\n");
		return E_FAIL;
	}
	if (!pDesc) {
		STRUCT_READ_START(This->hdr)
		STRUCT_READ(This->hdr, dwRepeats)
		STRUCT_READ(This->hdr, mtLength)
		STRUCT_READ(This->hdr, mtPlayStart)
		STRUCT_READ(This->hdr, mtLoopStart)
		STRUCT_READ(This->hdr, mtLoopEnd)
		STRUCT_READ(This->hdr, dwResolution)
		STRUCT_READ(This->hdr, rtLength)
		STRUCT_READ(This->hdr, dwFlags)
		STRUCT_READ(This->hdr, dwReserved)
		STRUCT_READ_END(This->hdr)
	}
	mmioAscend(hmmio, &ck, 0);
	/* read remaining chunks */
	while (!(mmres = mmioDescend(hmmio, &ck, pck, 0))) {
		HRESULT hr = DMIME_CommonChunk(hmmio, &ck, desc);
		if (FAILED(hr)) return hr;
		if (hr == S_OK || pDesc) {
			mmioAscend(hmmio, &ck, 0);
			continue;
		}
		if (ck.ckid == FOURCC_LIST) {
			switch (ck.fccType) {
			case DMUS_FOURCC_TRACK_LIST:
				/* parse embedded track list */
				TRACE("Track list found\n");
				hr = IDirectMusicSegmentImpl_ParseTrackList(This, hmmio, &ck);
				if (FAILED(hr)) return hr;
				break;
			default:
				FIXME("unknown list %08lx\n", ck.ckid);
				break;
			}
		}
		else {
			FIXME("unknown chunk %08lx\n", ck.ckid);
			break;
		}
		mmioAscend(hmmio, &ck, 0);
	}
	if (mmres != MMIOERR_CHUNKNOTFOUND) {
		ERR("mmio error %d\n", mmres);
		return E_FAIL;
	}
	return S_OK;
}

static HRESULT WINAPI IDirectMusicSegmentImpl_ParseStream(
	IDirectMusicSegmentImpl* This,
	IStream* pStream,
	LPDMUS_OBJECTDESC pDesc)
{
	HMMIO hmmio;
	MMCKINFO ck0;
	MMRESULT mmres;
	HRESULT hr = E_FAIL;

	hmmio = DMIME_mmioOpen(pStream);
	if (!hmmio) return E_FAIL;
	/* descend into RIFF chunk */
	mmres = mmioDescend(hmmio, &ck0, NULL, 0);
	if (mmres) {
		ERR("mmio error %d\n", mmres);
		DMIME_mmioClose(hmmio);
		return E_FAIL;
	}
	if (ck0.ckid == FOURCC_RIFF) {
		if (ck0.fccType == DMUS_FOURCC_SEGMENT_FORM) {
			TRACE("Segment form found\n");
			hr = IDirectMusicSegmentImpl_ParseForm(This, hmmio, &ck0, pDesc);
		} else {
			ERR("Segment form not found\n");
			hr = E_FAIL;
		}
	} else {
		ERR("RIFF chunk not found\n");
		hr = E_FAIL;
	}
	mmioAscend(hmmio, &ck0, 0);
	DMIME_mmioClose(hmmio);
	return hr;
}


/***********************************************************************
 *		DirectMusicSegmentCreate 
 */
HRESULT DirectMusicSegmentCreate(REFGUID lpGUID, IUnknown **ppObj, IUnknown *pUnkOuter)
{
	IDirectMusicSegmentImpl** ippDM=(IDirectMusicSegmentImpl**)ppObj;

	if (lpGUID)
		TRACE("(%p,%p,%p)\n",lpGUID,ippDM,pUnkOuter);
	else
		TRACE("DirectMusicSegmentCreate (%p)\n", ippDM);

	if (ippDM == NULL)
		return E_INVALIDARG;

	/* Allocate memory */
	*ippDM = (IDirectMusicSegmentImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectMusicSegmentImpl));
	if (*ippDM == NULL)
		return E_OUTOFMEMORY;

	ICOM_VTBL(*ippDM)	= &dmsvt;
	(*ippDM)->lpOVtbl	= &dmovt;
	(*ippDM)->lpPSVtbl	= &dmpsvt;
	(*ippDM)->ref		= 1;
	InitializeCriticalSection(&(*ippDM)->track_cs);

	return DS_OK;
}
