/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2001 Apple Computer, Inc.  All Rights Reserved. The
 * contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License.  Please
 * obtain a copy of the License at http://www.apple.com/publicsource and
 * read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.  Please
 * see the License for the specific language governing rights and
 * limitations under the License.
 *
 *
 * @APPLE_LICENSE_HEADER_END@
 *
 */

// $Id: QTFile_FileControlBlock.cpp,v 1.14 2001/11/15 17:34:44 murata Exp $
//
// QTFile:
//   The central point of control for a file in the QuickTime File Format.


// -------------------------------------
// Includes
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "OSMutex.h"
#include "OSMemory.h"

#include "QTFile.h"

#include "QTAtom.h"
#include "QTAtom_mvhd.h"
#include "QTAtom_tkhd.h"

#include "QTTrack.h"
#include "QTHintTrack.h"

#include <fcntl.h>



// -------------------------------------
// Macros
//
//#define DEBUG_PRINT(s) if(fDebug) printf s
//#define DEEP_DEBUG_PRINT(s) if(fDeepDebug) printf s


// -------------------------------------
// Class state cookie
//

QTFile_FileControlBlock::QTFile_FileControlBlock(void)
	: fDataBufferPool(NULL),
	  fDataBufferSize(0), fDataBufferPosStart(0), fDataBufferPosEnd(0),
	  fCurrentDataBuffer(NULL), fPreviousDataBuffer(NULL),
	  fCurrentDataBufferLength(0), fPreviousDataBufferLength(0),
	  fNumBlocksPerBuff(1),fNumBuffs(1),
	  fCacheEnabled(false)
	  
{
}

QTFile_FileControlBlock::~QTFile_FileControlBlock(void)
{
	if( fDataBufferPool != NULL )
		delete[] fDataBufferPool;
#if DSS_SERVER_INTERNAL
	(void)QTSS_CloseFileObject(fDataFD);
#endif

}


void QTFile_FileControlBlock::Set(const char * DataPath)
{
#if DSS_SERVER_INTERNAL
	(void)QTSS_OpenFileObject((char*)DataPath, qtssOpenFileReadAhead, &fDataFD);
#else
    fDataFD.Set(DataPath);
#endif

}

Bool16 QTFile_FileControlBlock::ReadInternal(FILE_SOURCE *dataFD, UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32 *inReadLenPtr)
{
    	UInt32 readLen = 0;
 		if (NULL != inReadLenPtr)
   			*inReadLenPtr = 0;
   			
#if DSS_SERVER_INTERNAL
   		QTSS_Error	theErr = QTSS_Seek(*dataFD, inPosition);
		if (theErr == QTSS_NoErr)
			theErr = QTSS_Read(*dataFD, inBuffer, inLength, &readLen);
		if (theErr != QTSS_NoErr)
			return false;
#else
 		if( dataFD->Read(inPosition, inBuffer, inLength, &readLen) != OS_NoErr )
		    return false;
#endif
		if (NULL != inReadLenPtr)
			*inReadLenPtr = readLen;
		
		if(inReadLenPtr == NULL && readLen != inLength) //external reads expect false if it fails to read all the requested data.
		    return false;
		    
		return true;
}


Bool16 QTFile_FileControlBlock::Read(FILE_SOURCE *dflt, UInt64 inPosition, void* inBuffer, UInt32 inLength)
{
    // Temporary vars
    UInt32 rcSize;

    // General vars
    FILE_SOURCE		*dataFD;

	// success or failure
	Bool16 result = false;

    // Get the file descriptor.  If the FCB is NULL, or the descriptor in
    // the FCB is -1, then we need to use the class' descriptor.
    if (this->IsValid())
		dataFD = &fDataFD;
    else
		dataFD = dflt;

	if	(
			( !fCacheEnabled) ||	// file control block caching disabled
			( inLength > fDataBufferSize ) ||	// too big for this cache
			( inPosition < fDataBufferPosStart)	// backing up
		)
	{
		//if ( !fCacheEnabled) printf("QTFile_FileControlBlock::Read  cache not enabled\n");
		//if ( inLength > fDataBufferSize) printf("QTFile_FileControlBlock::Read  read too big for cache len=%lu max%lu\n",inLength,fDataBufferSize);
		//if ( inPosition < fDataBufferPosStart) printf("QTFile_FileControlBlock::Read  backing up skipping cache missed by =%lu bytes\n", fDataBufferPosStart - inPosition);
		result = this->ReadInternal(dataFD, inPosition, inBuffer, inLength);
		goto done;
	}
	

    // Is the requested block of data in our data buffer?  If not, read in the
    // section of the file where this piece of data is.
    if( (inPosition < fDataBufferPosStart) || ((inPosition + inLength) > fDataBufferPosEnd) ) 
    {
		// If this is a forward-moving, contiguous read, then we can keep the
		// current buffer around.
		
		if (	(fCurrentDataBufferLength != 0)
			&&	((fDataBufferPosEnd - fCurrentDataBufferLength) <= inPosition)
			&& ((fDataBufferPosEnd + fDataBufferSize) >= (inPosition + inLength))
		   ) 
		{
		    // Temporary vars
		    char		*TempDataBuffer;
				
			//printf("QTFile_FileControlBlock::Read forward read inPosition=%"_64BITARG_"u fPreviousDataBuffer=%"_64BITARG_"u start=%"_64BITARG_"u\n",inPosition,fDataBufferPosStart,fDataBufferPosEnd);
				
		    // First, demote the current buffer.
		    fDataBufferPosStart += fPreviousDataBufferLength;
		    TempDataBuffer = fPreviousDataBuffer;
				
		    fPreviousDataBuffer = fCurrentDataBuffer;
		    fPreviousDataBufferLength = fCurrentDataBufferLength;
				
		    fCurrentDataBuffer = TempDataBuffer;
		    fCurrentDataBufferLength = 0;
				
		    //
		    // Then, fill the now-current buffer with data.
			if (!this->ReadInternal(dataFD, fDataBufferPosEnd, fCurrentDataBuffer, fDataBufferSize, &rcSize) )
				goto done;
		
			//Assert(rcSize == fDataBufferSize);
		    fCurrentDataBufferLength = (UInt32)rcSize;
		    fDataBufferPosEnd += fCurrentDataBufferLength;
		} 
		else 
		{	
			//printf("QTFile_FileControlBlock::Read not a contiguous forward read inPosition=%"_64BITARG_"u fPreviousDataBuffer=%"_64BITARG_"u missed=%"_64BITARG_"d\n ",inPosition,fDataBufferPosStart,(fDataBufferPosStart > inPosition) ? fDataBufferPosStart-inPosition: inPosition - fDataBufferPosStart);
		    
		    // We need to play with our current and previous data buffers in
		    // order to skip around while reading.
		    fCurrentDataBuffer = fDataBufferPool;
		    fCurrentDataBufferLength = 0;

		    fPreviousDataBuffer = (char *)fDataBufferPool + fDataBufferSize;
		    fPreviousDataBufferLength = 0;

		    fDataBufferPosStart = inPosition;
		    
			if (! this->ReadInternal(dataFD, fDataBufferPosStart, fCurrentDataBuffer, fDataBufferSize, &rcSize) )
				goto done;

			//Assert(rcSize == fDataBufferSize);
		    fCurrentDataBufferLength = (UInt32)rcSize;
		    fDataBufferPosEnd = fDataBufferPosStart + fCurrentDataBufferLength;
		}

    }
	
    //
    // Copy the data out of our buffer(s).
    {
		// General vars
		UInt32		ReadLength = inLength;
		UInt32		ReadOffset = inPosition - fDataBufferPosStart;
		
		//
		// Figure out if doing a continuous copy would cause us to cross a
		// buffer boundary.
		if	(	(inPosition < (fDataBufferPosStart + fPreviousDataBufferLength))
			&&	((ReadOffset + ReadLength) > fPreviousDataBufferLength ) 
			) 
		{
		    // Temporary vars
		    char	*pBuffer = (char *)inBuffer;
								
		    //
		    // Read the first part of the block.
		    ReadLength = fDataBufferSize - ReadOffset;
		    if (ReadLength <= (fPreviousDataBufferLength - ReadOffset) )
		   		::memcpy(pBuffer, fPreviousDataBuffer + ReadOffset, ReadLength);
		    else
		    	goto done;
		    	
		    pBuffer += ReadLength;
				
		    //
		    // Read the last part of the block.
		    ReadLength = inLength - ReadLength;
		    if (ReadLength <= fCurrentDataBufferLength )
		    {	::memcpy(pBuffer, fCurrentDataBuffer, ReadLength);
		    	result = true;
		    }
		    //
		    // Or maybe this is a continuous copy out of the old buffer.
		} 
		else if ( inPosition < (fDataBufferPosEnd - fCurrentDataBufferLength) ) 
		{
		    if (ReadLength <= (fPreviousDataBufferLength - ReadOffset) )
		    {	::memcpy(inBuffer, fPreviousDataBuffer + ReadOffset, ReadLength);
		    	result = true;
			}		    	
		} 
		else 
		{
		    ReadOffset -= fPreviousDataBufferLength;
		    if (ReadLength <= (fCurrentDataBufferLength - ReadOffset) )
		    {	::memcpy(inBuffer, fCurrentDataBuffer + ReadOffset, ReadLength);
		    	result = true;
			}		    	
			
		}
    }

done:
    // We're done.
    return result;
}


void QTFile_FileControlBlock::AdjustDataBufferBitRate(UInt32 inUnitSizeInK, UInt32 inFileBitRate, UInt32 inNumBuffSizeUnits, UInt32 inMaxBitRateBuffSizeInBlocks)
{
	if (!fCacheEnabled)
		return;
		
	// General vars
	UInt32	newDataBufferSizeInUnits = inNumBuffSizeUnits;
	UInt32	newDataBufferSize = 0;
	UInt32	newUnitSizeBytes = 0;
	
	if (inUnitSizeInK < 1)
		inUnitSizeInK = 32;
	
	newUnitSizeBytes = inUnitSizeInK * 1024;

	if (inMaxBitRateBuffSizeInBlocks < 1)
		inMaxBitRateBuffSizeInBlocks = kMaxDefaultBlocks;
		
	if (inFileBitRate == 0) // set the maximum
		inFileBitRate = inMaxBitRateBuffSizeInBlocks * newUnitSizeBytes;
		
	if (inNumBuffSizeUnits < 1) // calculate if not set to a given number
	{
		newDataBufferSizeInUnits = (inFileBitRate + newUnitSizeBytes) / newUnitSizeBytes; // 32k bytes of buffer for every 32k bits in rate
		if( newDataBufferSizeInUnits > inMaxBitRateBuffSizeInBlocks ) // Limit the buffer size value to a reasonable maximum. should be pref
			newDataBufferSizeInUnits = inMaxBitRateBuffSizeInBlocks;
	}
	
	newDataBufferSize = newDataBufferSizeInUnits * kBlockByteSize;
	
	//printf("QTFile_FileControlBlock::AdjustDataBuffer private buffers NewDataBufferSizeInUnits =%lu NewDataBufferSize = %lu\n",newDataBufferSizeInUnits,newDataBufferSize);

	// Free the old buffer.
	delete[] fDataBufferPool;
	fDataBufferSize 	= newDataBufferSize;
	fDataBufferPool 	= NEW char[2 * newDataBufferSize]; // 2 contiguous buffers
	fCurrentDataBuffer 	= fDataBufferPool;
	fPreviousDataBuffer = (char *)fDataBufferPool + newDataBufferSize;
	
	fDataBufferPosStart = 0;
	fDataBufferPosEnd 	= 0;
	fCurrentDataBufferLength = 0;
	fPreviousDataBuffer = 0;
	
}
