/*++

Copyright (c) 1996,1997  Microsoft Corporation

Module Name:

    FRAMEBUF.CPP

Abstract:

	Manages Memory for Send/Receive Frames.
	BUGBUG: when you have time do an intelligent implementation,
	for now this is just a frame cache.

Author:

	Aaron Ogus (aarono)

Environment:

	Win32/COM

Revision History:

	Date   Author  Description
   ======  ======  ============================================================
  12/10/96 aarono  Original
   6/6/98  aarono  More debug checks, shrink pool.

--*/

#include <windows.h>
#include "newdpf.h"
#include <dplay.h>
#include <dplaysp.h>
#include <dplaypr.h>
#include "mydebug.h"
#include "bufmgr.h"
#include "macros.h"

#define MAX_FRAMES_ON_LIST 16
#define MIN_FRAMES_ON_LIST 8

typedef struct _frame 
{
	BILINK Bilink;
	UINT len;
	UCHAR data[0];
} FRAME, *PFRAME;	

BILINK FrameList;
UINT   nFramesOnList=0;
UINT   TotalFrameMemory=0;
CRITICAL_SECTION FrameLock;

#ifdef DEBUG
void DebugChkFrameList()
{
	BILINK *pBilink;
	PFRAME  pFrameWalker;

	DWORD	count=0;
	DWORD	totalsize=0;
	DWORD   fBreak=FALSE;

	Lock(&FrameLock);

	pBilink=FrameList.next;
	while(pBilink != &FrameList){
		pFrameWalker=CONTAINING_RECORD(pBilink,FRAME,Bilink);
		pBilink=pBilink->next;
		count++;
		totalsize+=pFrameWalker->len;
	}

	if(totalsize != TotalFrameMemory){
		DPF(0, "Total Frame Memory says %d but I count %d\n",TotalFrameMemory, totalsize);
		fBreak=TRUE;
	}

	if(count != nFramesOnList){
		DPF(0, "nFramesOnList %d but I count %d\n",nFramesOnList, count);
		fBreak=TRUE;
	}
	if(fBreak){
		DEBUG_BREAK();
	}
	
	Unlock(&FrameLock);
}
#else
#define DebugChkFrameList()
#endif

VOID InitFrameBuffers(VOID)
{
	InitBilink(&FrameList);
	InitializeCriticalSection(&FrameLock);
	nFramesOnList=0;
	TotalFrameMemory=0;
}

VOID FiniFrameBuffers(VOID)
{
	BILINK *pBilink;
	PFRAME  pFrame;
	
	Lock(&FrameLock);

	while(!EMPTY_BILINK(&FrameList)){
		pBilink=FrameList.next;
		pFrame=CONTAINING_RECORD(pBilink,FRAME,Bilink);
		Delete(pBilink);
		My_GlobalFree(pFrame);
	}

	nFramesOnList=0;
	TotalFrameMemory=0;
	
	Unlock(&FrameLock);
	DeleteCriticalSection(&FrameLock);
}

VOID ReleaseFrameBufferMemory(PUCHAR pFrameData)
{
	PFRAME  pFrame;
	BILINK  FramesToFree;
	BILINK *pBilink;
	
	Lock(&FrameLock);

	DebugChkFrameList();

	InitBilink(&FramesToFree);

	pFrame=CONTAINING_RECORD(pFrameData,FRAME,data);

	InsertAfter(&pFrame->Bilink, &FrameList);
	nFramesOnList++;
	TotalFrameMemory+=pFrame->len;

	if(nFramesOnList > MAX_FRAMES_ON_LIST){
		while(nFramesOnList > MIN_FRAMES_ON_LIST){
			pBilink=FrameList.next;
			pFrame=CONTAINING_RECORD(pBilink,FRAME,Bilink);
			nFramesOnList--;
			TotalFrameMemory-=pFrame->len;
			Delete(pBilink);
			DebugChkFrameList();
			InsertAfter(pBilink, &FramesToFree);
		}
	}
	
	DebugChkFrameList();
	ASSERT(nFramesOnList);

	Unlock(&FrameLock);

	// Drop lock before freeing, to make more effecient.

	while(!EMPTY_BILINK(&FramesToFree)){
		pBilink=FramesToFree.next;
		pFrame=CONTAINING_RECORD(pBilink,FRAME,Bilink);
		Delete(pBilink);
		My_GlobalFree(pFrame);
	}

	DebugChkFrameList();

}

PBUFFER GetFrameBuffer(UINT FrameLen)
{
	PBUFFER pBuffer;
	PFRAME  pFrame;
	MEMDESC memdesc;

	BILINK  *pBilinkWalker;
	PFRAME  pFrameBest=NULL, pFrameWalker;
	UINT    difference=FrameLen;

	DPF(9,"==>GetFrameBuffer Len %d\n",FrameLen);

	Lock(&FrameLock);
	
	if(!EMPTY_BILINK(&FrameList)){
		ASSERT(nFramesOnList);

		pBilinkWalker=FrameList.next;
		
		while(pBilinkWalker != &FrameList){
			pFrameWalker=CONTAINING_RECORD(pBilinkWalker, FRAME, Bilink);
			if(pFrameWalker->len >= FrameLen){
				if(FrameLen-pFrameWalker->len < difference){
					difference=FrameLen-pFrameWalker->len;
					pFrameBest=pFrameWalker;
					if(!difference){
						break;
					}
				}
			}
			pBilinkWalker=pBilinkWalker->next;
		}

		if(!pFrameBest){
			goto alloc_new_frame;
		} else {
			pFrame=pFrameBest;
		}

		DebugChkFrameList();

		Delete(&pFrame->Bilink);
		nFramesOnList--;
		TotalFrameMemory-=pFrame->len;

		DebugChkFrameList();

		Unlock(&FrameLock);
		
	} else {
	
alloc_new_frame:	
		Unlock(&FrameLock);
		pFrame=(PFRAME)My_GlobalAlloc(GMEM_FIXED,FrameLen+sizeof(FRAME));
		if(!pFrame){
			return NULL;
		}
		pFrame->len=FrameLen;
	}

	memdesc.pData=&pFrame->data[0];
	memdesc.len=pFrame->len;
	
	pBuffer=BuildBufferChain((&memdesc),1);

	if(!pBuffer){
		ReleaseFrameBufferMemory(pFrame->data);
		DPF(9,"<==GetFrameBuffer FAILED returning %x\n",pBuffer);
	} else {
		pBuffer->dwFlags |= BFLAG_FRAME;
		DPF(9,"<==GetFrameBuffer %x, len %d\n",pBuffer, pFrame->len);
	}	

	DebugChkFrameList();
	
	return pBuffer;
}

// Release the buffer, put the memory back on the frame buffer list.
VOID FreeFrameBuffer(PBUFFER pBuffer)
{
	ASSERT(pBuffer->dwFlags & BFLAG_FRAME);
	ReleaseFrameBufferMemory(pBuffer->pData);
	FreeBuffer(pBuffer);
}
