/*
	File:		PPCLocal.c

	Contains:	PPC Local Session Management Calls

	Written by:	Sangam

	Copyright:	© 1989-1992 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		<22>	 4/14/92	BBM		<JSM>: Remove unfinished PPC code that is under the contitional
									ÒTheFututeÓ, and remove the conditional ÒCubeEÓ since that is
									reality. Remove conditionals, since all they do is confuse.
		<21>	 10/4/91	JSM		Change PsychoticFarmerOrLater conditionals to TheFuture.
		<20>	 9/29/91	DTY		Conditionalize <17> thru <19> out of CubeE.
		<19>	 6/26/91	EMT		Add support to kill PPCStart calls
		<18>	 6/10/91	EMT		Optimize code for size
		<17>	  6/4/91	EMT		Roll in StoreAndForward Revisions
		<16>	 11/6/90	EMT		Fix warning
		<15>	10/30/90	EMT		Seperate Read and Write PB's into their own queue's.
		<14>	10/18/90	EMT		Fix functions to have prototype declarations
		<13>	10/11/90	EMT		add conditional around debug code
		<13>	10/11/90	EMT		add conditional around debug code
		<12>	 9/21/90	EMT		Update constants, types, and field names as dictated by
									PPCToolBox.h
		<11>	  9/4/90	EMT		Roll in Eric's changes
		<10>	 6/28/90	S		To Improve Dynamic allocation of sessions.
		 <9>	 4/17/90	JSM		Add prototype for AvoidDuplicateCompletion, fix bug where
									AvoidDuplicateCompletion was being called with extra parameter.
		 <8>	 3/20/90	S		Bug Fix. Returning the wrong user name.
		 <7>	 2/27/90	S		ErrorCode Fix.
		 <6>	 2/20/90	S		Some ErrorCode Changes.
		 <5>	 2/13/90	S		Some Bug Fix.
		 <4>	  2/8/90	S		Minor Bug Fix.
		 <3>	 1/30/90	S		Bug Fixes.
	   <1.4>	10/12/89	CCH		Fixed header
	  <¥1.3>	10/12/89	CVC		Added Access Control
	   <1.2>	 10/2/89	CVC		Fixed a big in PPCInform call.
	   <1.1>	 9/25/89	ss		Added Support for 'more' in PPCWrite calls.
	   <1.0>	 9/18/89	CVC		Adding PPC toolbox for the first time.

	   Old Revision History:

		06/05/89 	Sangam	    New Today
		07/20/89    Sangam      1.0d2 release today!
		08/10/89	Sangam		Made changes to accomadate new IPCListPorts call
		08/10/89	Sangam		Added userEntry filed for read/write headers
		09/08/89	Sangam		Changed the errorcode for PPCReject to rejectErr
		09/11/89	Sangam		Fixed the problem of completion not being called(some times)
								in accept and reject session functions.
		09/15/89	Sangam		Changed the errorcode to rejectErr (when inform is not outstanding)
		09/19/89    Sangam      Returning the Right port name portname at PPCInform Call to build special
								version for Jay.
		09/21/89	Sangam      Changed the interface to GetPortNames call
		09/22/89 	Sangam		Removed destportName and destlocname from session data structure
		09/25/89    Sangam		Added Support for 'more' in PPCWrite calls.
		01/04/90    Sangam      Bug fix for actualLength in read/write calls.

*/

#include "PPCCommon.h"
#include <String.h>

/*----------------------------------------------------------------------------------------
	Prototypes of functions used only in this file.
----------------------------------------------------------------------------------------*/
STATIC void LocalTransfer(CommonSessionParamsPtr writeSess,
				   CommonSessionParamsPtr readSess);

/*----------------------------------------------------------------------------------------
	GetLocSess returns the a new local session table.
----------------------------------------------------------------------------------------*/

STATIC LocalIPCParamsPtr GetLocSess(PPCGlobalParamsPtr ppcglobPtr)
{
	LocalIPCParamsPtr theSess;
	
	if(theSess = ServeQueue(&ppcglobPtr->freeLocSessQueue))
	{
		memset(theSess,0,sizeof(LocalIPCParams));
		theSess->sessUse = locUse;
		EnQueue(theSess,&ppcglobPtr->LocSessQueue);
	}
	return(theSess);
}

STATIC void FreeLocSess(LocalIPCParamsPtr sessPtr)
{
	PPCGlobalParamsPtr ppcglobPtr;
	PPCPortEntryPtr portPtr;

	ppcglobPtr = (PPCGlobalParamsPtr)getGlobal();
	portPtr = sessPtr->portEntry;
	DeleteFromQueue(&ppcglobPtr->LocSessQueue,(unsigned long)sessPtr,PointerCompare);
	if(portPtr)						// <20> if this session belongs to a port.
		--portPtr->sessCount;		// <20> update the port table.
	EnQueue(sessPtr,&ppcglobPtr->freeLocSessQueue);
}

void StartLocalSession(PPCStartPBPtr		startPB,
					   PPCPortEntryPtr		srcportPtr,
					   PPCGlobalParamsPtr	ppcglobPtr)
{
	PPCPortEntryPtr         destportPtr;
	CommonSessionParamsPtr  srcSess, destSess;
	PPCInformPBPtr      informPB;
	PBWriteResPtr           res;	// <20>
	unsigned short          i;		// <20>
	unsigned char           *temp;	// <20>

	res =(PBWriteResPtr)(startPB->Reserved);

	if (!(destportPtr = CheckPortName(startPB->portName, ppcglobPtr)))
	{
		CompleteWithResult(startPB,destPortErr);
		return;
	}

	if (!(informPB = ServeQueue(&destportPtr->informPBQueue)))
	{
		CompleteWithResult(startPB,noInformErr);
		return;
	}

	if (!(srcSess =(CommonSessionParamsPtr)GetLocSess(ppcglobPtr)))
	{
		SetPortInformQue(informPB, destportPtr); // Add the PB back
		CompleteWithResult(startPB,sessTableErr);
		return;
	}
	if (!(destSess = (CommonSessionParamsPtr)GetLocSess(ppcglobPtr)))
	{
		FreeLocSess((LocalIPCParamsPtr)srcSess);  /* Free the Source Session table */
		SetPortInformQue(informPB, destportPtr); // Add the PB back
CompleteStartPBWithResult:
		CompleteWithResult(startPB,sessTableErr);
		return;
	}
	
	SetSessionTable(srcSess, srcportPtr, ppcglobPtr);
	SetSessionTable(destSess, destportPtr, ppcglobPtr); 
	
	srcSess->conRef = destSess->sessRefNum;
	destSess->conRef = srcSess->sessRefNum;

	
	informPB->sessRefNum = destSess->sessRefNum;
	informPB->serviceType = ppcServiceRealTime;
	informPB->userData = startPB->userData;
	informPB->requestType = ppcLocalOrigin;
	if (informPB->portName)
		CopyPortName(&(srcportPtr->portName),informPB->portName);
	
	if (informPB->locationName)
	{
		temp = (unsigned char *)(informPB->locationName);
		for (i=0; i< sizeof(LocationNameRec); i++)
			*temp++ = '\0';
	}

	if (informPB->userName)

	   *informPB->userName = '\0'; 	// No User Name for local Session
	if (informPB->autoAccept)
	{
		srcSess->sessState = DataXferState;
	    destSess->sessState =  DataXferState;
		startPB->sessRefNum = srcSess->sessRefNum;
		srcSess->PBPtr = NULL;
		CompleteWithResult(informPB,noErr);
		CompleteWithResult(startPB,noErr);
		return;
	}
	else
	{
		srcSess->sessState = AwaitAcceptResp;
	    destSess->sessState =  AwaitAcceptReq;
		srcSess->PBPtr = startPB; 	// Remember the Start PB for this session.
		CompleteWithResult(informPB,noErr);
	}	
} // StartLocalSession

void AcceptLocalSession(PPCAcceptPBPtr acceptPB,
						CommonSessionParamsPtr destSess,
						PPCGlobalParamsPtr ppcglobPtr)
{
	CommonSessionParamsPtr srcSess;
	PPCStartPBPtr      startPB;
	
	if (!(srcSess = SessRefNumtoPtr(destSess->conRef, ppcglobPtr)))
	{
		FreeLocSess((LocalIPCParamsPtr)destSess);
		CompleteWithResult(acceptPB,noSessionErr);
		return;
	}

	srcSess->sessState =  DataXferState;
	destSess->sessState = DataXferState;
	startPB = srcSess->PBPtr;
	startPB->sessRefNum = srcSess->sessRefNum;
	srcSess->PBPtr = NULL;
	destSess->PBPtr = NULL;
	CompleteWithResult(startPB,noErr);
	CompleteWithResult(acceptPB,noErr);
} // AcceptLocalSession

void RejectLocalSession(PPCRejectPBPtr     rejectPB,
						CommonSessionParamsPtr destSess,
						PPCGlobalParamsPtr     ppcglobPtr)
{
	CommonSessionParamsPtr srcSess;
	PPCStartPBPtr      startPB;
	
	if (!(srcSess = SessRefNumtoPtr(destSess->conRef, ppcglobPtr)))
	{
		FreeLocSess((LocalIPCParamsPtr)destSess);
		CompleteWithResult(rejectPB,noSessionErr);
		return;
	}
	startPB = srcSess->PBPtr;
	FreeLocSess((LocalIPCParamsPtr)destSess);
	FreeLocSess((LocalIPCParamsPtr)srcSess);
	startPB->rejectInfo =  rejectPB->rejectInfo;
	CompleteWithResult(startPB,userRejectErr);
	CompleteWithResult(rejectPB, noErr);
}


void WriteLocalData(PPCWritePBPtr      	writePB,
					CommonSessionParamsPtr srcSess,
					PPCGlobalParamsPtr     ppcglobPtr)
{
	CommonSessionParamsPtr destSess;
	PBWriteResPtr          res;

	res = (PBWriteResPtr)(writePB->Reserved);
	res->curBufPos = writePB->bufferPtr;
	res->length = writePB->bufferLength;

	if (destSess = SessRefNumtoPtr(srcSess->conRef, ppcglobPtr))
	{
		AddWritePB(srcSess,writePB);
		if(!srcSess->writePB)		// if not currently writing then
		{
			srcSess->writePB = GetWritePB(srcSess);
			if(destSess->readPB)
				LocalTransfer(srcSess, destSess);
			
		}
	}
	else
		CompleteWithResult(writePB,noSessionErr);
} // WriteLocalData

void ReadLocalData(PPCReadPBPtr readPB,
				   CommonSessionParamsPtr destSess,
				   PPCGlobalParamsPtr ppcglobPtr)
{
	CommonSessionParamsPtr srcSess;
	PBWriteResPtr          res;

	res = (PBWriteResPtr)(readPB->Reserved);

	if (srcSess = SessRefNumtoPtr(destSess->conRef, ppcglobPtr))
	{
		AddReadPB(destSess,readPB);
		if(!destSess->readPB)
		{
			destSess->readPB = GetReadPB(destSess);
			if(srcSess->writePB)
				LocalTransfer(srcSess, destSess);
		}
	}
	else
		CompleteWithResult(readPB,noSessionErr);
} // ReadLocalData

void EndLocalSession(PPCEndPBPtr         endPB,
					 CommonSessionParamsPtr srcSess,
					 PPCGlobalParamsPtr     ppcglobPtr)
{
	CommonSessionParamsPtr     destSess;
	PPCPortEntryPtr            portPtr;

	
	portPtr = srcSess->portEntry;
	destSess = DeleteSessByRefNum(srcSess->conRef,ppcglobPtr);
	if (srcSess->sessState == AwaitAcceptResp)
	{
		if (destSess)
		{
			FreeLocSess((LocalIPCParamsPtr)destSess);
		}
		CompleteWithResult(srcSess->PBPtr, portClosedErr);
		FreeLocSess((LocalIPCParamsPtr)srcSess);
	}
	else if (srcSess->sessState == AwaitAcceptReq)
	{
		if (destSess)
		{
			CompleteWithResult(destSess->PBPtr,destPortErr);
			FreeLocSess((LocalIPCParamsPtr)destSess);
		}
		FreeLocSess((LocalIPCParamsPtr)srcSess);
	}
	else if (srcSess->sessState == DataXferState)
	{
		if (destSess)
		{
			KillReadAndWrite(destSess);
			FreeLocSess((LocalIPCParamsPtr)destSess);
		}
		KillReadAndWrite(srcSess);
		FreeLocSess((LocalIPCParamsPtr)srcSess);
	}
#ifdef DEBUG
	else
		DebugStr("\pSrcSession in unknown state, doesn't get freed");
#endif
	if (endPB)
		CompleteWithResult(endPB,noErr);
	else								// <20> Must be a PPCClose operation.
		CompleteClosePort(portPtr);		// <20>
} // EndLocalSession

void ListLocalPorts(IPCListPortsPBPtr listPB,PPCGlobalParamsPtr ppcglobPtr)
{
    GetPortNames(ppcglobPtr,listPB->portName,
				 true, // local
				 true, // guetstUnChecked
				 listPB->startIndex, 
				 (PortInfoPtr)listPB->bufferPtr,
				 listPB->requestCount, &(listPB->actualCount));
	CompleteWithResult(listPB,noErr);
} //ListLocalPorts 


STATIC void LocalTransfer(CommonSessionParamsPtr writeSess,
						  CommonSessionParamsPtr readSess)
{
	PPCWritePBPtr      writePB;
	PPCReadPBPtr       readPB;
	unsigned long xferLength;
	PBWriteResPtr res;
	unsigned char *dataPtr;
	
	while((writePB = writeSess->writePB) && (readPB = readSess->readPB))
	{
		res = (PBWriteResPtr)(writePB->Reserved);	
		if  (res->length > readPB->bufferLength -readPB->actualLength)
			xferLength = readPB->bufferLength - readPB->actualLength;
		else
			xferLength = res->length;	// <20>
		/* Copy the Data */
		dataPtr = (unsigned char *)(readPB->bufferPtr + readPB->actualLength);
		BlockMove((char *)res->curBufPos, dataPtr,xferLength);
		writePB->actualLength += xferLength;
		res->curBufPos+=xferLength;
		res->length-=xferLength;	

		readPB->blockCreator = writePB->blockCreator;
		readPB->blockType = writePB->blockType;
		readPB->userData = writePB->userData;
		readPB->actualLength = readPB->actualLength + xferLength;

		if (res->length)				// <20> If we have more to write for this request.
		{
			readPB->more = true;
		}
		else
		{
			readPB->more = writePB->more;
			CompleteWithResult(writePB,noErr);
			writeSess->writePB = GetWritePB(writeSess);
		}
		
		// if no more to read, or the read buffer is full then
		if (!readPB->more || (readPB->actualLength == readPB->bufferLength))	
		{
			CompleteWithResult(readPB,noErr);
			readSess->readPB = GetReadPB(readSess);
		}
	}
} // LocalTransfer