/*
 *
 * @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@
 *
 */
/*
	File:		osfilesource.h

	Contains:	simple file abstraction. This file abstraction is ONLY to be
				used for files intended for serving 
					
	
*/

#ifndef __OSFILE_H_
#define __OSFILE_H_

#include <stdio.h>
#include <time.h>

#include "OSHeaders.h"
#include "StrPtrLen.h"
#include "OSQueue.h"

#define READ_LOG 0

class FileBlockBuffer 
{

 public:
 	FileBlockBuffer(): fArrayIndex(-1),fBufferSize(0),fBufferFillSize(0),fDataBuffer(NULL),fDummy(0){}
	~FileBlockBuffer(void);
	void AllocateBuffer(UInt32 buffSize);
	void TestBuffer(void);
	void CleanBuffer() { ::memset(fDataBuffer,0, fBufferSize); }
	void SetFillSize(UInt32 fillSize) {fBufferFillSize = fillSize;}
	UInt32 GetFillSize(void) { return fBufferFillSize;}
	OSQueueElem *GetQElem() { return &fQElem; }
    SInt32				fArrayIndex;
    UInt32				fBufferSize;
    UInt32				fBufferFillSize;
    char				*fDataBuffer;
    OSQueueElem			fQElem;
	UInt32				fDummy;
};



class FileBlockPool
{
	enum {
			kDataBufferUnitSizeExp		= 15,// base 2 exponent
			kBufferUnitSize				= (1 << kDataBufferUnitSizeExp ) // 32Kbytes
    };

	public:
 		FileBlockPool(void) :  fMaxBuffers(1),  fNumCurrentBuffers(0), fBufferUnitSizeBytes(kBufferUnitSize){}
		~FileBlockPool(void);
		
		void SetMaxBuffers(UInt32 maxBuffers) { if (maxBuffers > 0) fMaxBuffers = maxBuffers; }

		void SetBuffIncValue(UInt32 bufferInc) { if (bufferInc > 0) fBufferInc = bufferInc;}
		void IncMaxBuffers(void) { fMaxBuffers += fBufferInc; }
		void DecMaxBuffers(void) { if (fMaxBuffers > fBufferInc) fMaxBuffers-= fBufferInc; }
		void DecCurBuffers(void) { if (fNumCurrentBuffers > 0) fNumCurrentBuffers--; }
		
		void SetBufferUnitSize	(UInt32 inUnitSizeInK)		{ fBufferUnitSizeBytes = inUnitSizeInK * 1024; }
		UInt32 GetBufferUnitSizeBytes() 	{ return fBufferUnitSizeBytes; }
		UInt32 GetMaxBuffers(void)	{ return fMaxBuffers; }
		UInt32 GetIncBuffers()		{ return fBufferInc; }
		UInt32 GetNumCurrentBuffers(void)	{ return fNumCurrentBuffers; }
		void DeleteBlockPool();
		FileBlockBuffer* GetBufferElement(UInt32 bufferSizeBytes);
		void MarkUsed(FileBlockBuffer* inBuffPtr);

	private:
		OSQueue fQueue;
		UInt32	fMaxBuffers;
		UInt32	fNumCurrentBuffers;	
		UInt32	fBufferInc;	
		UInt32	fBufferUnitSizeBytes;
		UInt32	fBufferDataSizeBytes;

};

class FileMap 
{

 	public:
 		FileMap(void):fFileMapArray(NULL),fDataBufferSize(0),fMapArraySize(0),fNumBuffSizeUnits(0) {}
		~FileMap(void) {fFileMapArray = NULL;}
		void 	AllocateBufferMap(UInt32 inUnitSizeInK, UInt32 inNumBuffSizeUnits, UInt32 inBufferIncCount, UInt32 inMaxBitRateBuffSizeInBlocks, UInt64 fileLen, UInt32 inBitRate);
		char*	GetBuffer(SInt32 bufIndex, Bool16 *outIsEmptyBuff);
		void 	TestBuffer(SInt32 bufIndex) {Assert (bufIndex >= 0); fFileMapArray[bufIndex]->TestBuffer();};
		void 	SetIndexBuffFillSize(SInt32 bufIndex, UInt32 fillSize) { Assert (bufIndex >= 0); fFileMapArray[bufIndex]->SetFillSize(fillSize);}
		UInt32	GetMaxBufSize(void) {return fDataBufferSize;}
		UInt32	GetBuffSize(SInt32 bufIndex) 	{ Assert (bufIndex >= 0); return fFileMapArray[bufIndex]->GetFillSize(); }
		UInt32 	GetIncBuffers(void) { return fBlockPool.GetIncBuffers(); }
		void	IncMaxBuffers() 	{fBlockPool.IncMaxBuffers(); }
		void	DecMaxBuffers() 	{fBlockPool.DecMaxBuffers(); }
		Bool16	Initialized() 		{ return fFileMapArray == NULL ? false : true; }
		void	Clean(void);
		void	DeleteMap(void);
		void	DeleteOldBuffs(void);
		SInt32	GetBuffIndex(UInt64 inPosition) {	return inPosition / this->GetMaxBufSize();  }
		SInt32	GetMaxBuffIndex() { Assert(fMapArraySize > 0); return fMapArraySize -1; }
		UInt32 	GetBuffOffset(SInt32 bufIndex) { Assert (bufIndex >= 0);  return bufIndex * this->GetMaxBufSize(); }
		FileBlockPool fBlockPool;

    	FileBlockBuffer**	fFileMapArray;
    
 	private:

    UInt32				fDataBufferSize;
    SInt32				fMapArraySize;
    UInt32				fNumBuffSizeUnits;
	
};

class OSFileSource
{
	public:
	
		OSFileSource() :	fFile(-1), fLength(0), fPosition(0), fReadPos(0), fShouldClose(true), fIsDir(false), fCacheEnabled(false)						
		{
		
		#if READ_LOG 
			fFileLog = NULL;
			fTrackID = 0;
			fFilePath[0]=0;
		#endif
		
		}
				
		OSFileSource(const char *inPath) :	fFile(-1), fLength(0), fPosition(0), fReadPos(0), fShouldClose(true), fIsDir(false),fCacheEnabled(false)
		{
		 Set(inPath); 
		 
		#if READ_LOG 
			fFileLog = NULL;
			fTrackID = 0;
			fFilePath[0]=0;
		#endif		
		 
		 }
		
		~OSFileSource() { Close(); 	fFileMap.DeleteMap();}
		
		//Sets this object to reference this file
		void 			Set(const char *inPath);
		
		// Call this if you don't want Close or the destructor to close the fd
		void			DontCloseFD() { fShouldClose = false; }
		
		//Advise: this advises the OS that we are going to be reading soon from the
		//following position in the file
		void			Advise(UInt64 advisePos, UInt32 adviseAmt);
		
		OS_Error	Read(void* inBuffer, UInt32 inLength, UInt32* outRcvLen = NULL)
					{	return ReadFromDisk(inBuffer, inLength, outRcvLen);
					}
					
		OS_Error	Read(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen = NULL);
		OS_Error	ReadFromDisk(void* inBuffer, UInt32 inLength, UInt32* outRcvLen = NULL);
		OS_Error	ReadFromCache(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen = NULL);
		OS_Error	ReadFromPos(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen = NULL);
		void		EnableFileCache(Bool16 enabled) {OSMutexLocker locker(&fMutex); fCacheEnabled = enabled; }
		Bool16		GetCacheEnabled() { return fCacheEnabled; }
		void 		AllocateFileCache(UInt32 inUnitSizeInK = 32, UInt32 bufferSizeUnits = 0, UInt32 incBuffers = 1, UInt32 inMaxBitRateBuffSizeInBlocks = 8, UInt32 inBitRate = 32768) 
					{	fFileMap.AllocateBufferMap(inUnitSizeInK, bufferSizeUnits,incBuffers, inMaxBitRateBuffSizeInBlocks, fLength, inBitRate);
					} 
		void		IncMaxBuffers() 	{OSMutexLocker locker(&fMutex);	fFileMap.IncMaxBuffers(); }
		void		DecMaxBuffers() 	{OSMutexLocker locker(&fMutex);	fFileMap.DecMaxBuffers(); }

		OS_Error	FillBuffer(char* ioBuffer, char *buffStart, SInt32 bufIndex);
				
		void			Close();
		time_t			GetModDate()				{ return fModDate; }
		UInt64			GetLength() 				{ return fLength; }
		UInt64			GetCurOffset()				{ return fPosition; }
		void			Seek(SInt64 newPosition) 	{ fPosition = newPosition; 	}
		Bool16 IsValid() 							{ return fFile != -1; 		}
		Bool16 IsDir()								{ return fIsDir; }
		
		// For async I/O purposes
		int				GetFD()						{ return fFile; }
		void			SetTrackID(UInt32 trackID);
		// So that close won't do anything
		void ResetFD()	{ fFile=-1; }

		void SetLog(const char *inPath);
	
	private:

		int		fFile;
		UInt64	fLength;
		UInt64	fPosition;
		UInt64	fReadPos;
		Bool16	fShouldClose;
		Bool16	fIsDir;
		time_t	fModDate;
		
		
		OSMutex	fMutex;
		FileMap fFileMap;
		Bool16  fCacheEnabled;
#if READ_LOG
    	FILE*				fFileLog;
    	char				fFilePath[1024];
		UInt32  			fTrackID;
#endif

};

#endif //__OSFILE_H_
