/*
 *
 * @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: QTAtom_dref.cpp,v 1.8 2001/10/23 03:35:36 lecroy Exp $
//
// QTAtom_dref:
//   The 'dref' QTAtom class.


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

#ifndef __Win32__
#include <sys/types.h>
#include <netinet/in.h>
#endif

#include "QTFile.h"

#include "QTAtom.h"
#include "QTAtom_dref.h"
#include "OSMemory.h"


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



// -------------------------------------
// Constants
//
const int		drefPos_VersionFlags		=  0;
const int		drefPos_NumRefs				=  4;
const int		drefPos_RefTable			=  8;

const int		drefRefPos_Size				=  0;
const int		drefRefPos_Type				=  4;
const int		drefRefPos_VersionFlags		=  8;
const int		drefRefPos_Data				= 12;



// -------------------------------------
// Mac alias constants and typedefs
//
#if __MACOS__ 
#pragma options align=mac68k
#endif

enum {
		kEndMark	=-1,			/* -1 end of variable info */
		kAbsPath	= 2,			/* 2  absolute path name */
		kMaxMark	= 10			/* End Marker */
};

typedef struct
{
	short 			what;			/* what kind of information */
	short 			len;			/* length of variable data */
	char 			data[1];		/* actual data */
} varInfo;

typedef struct
{
	char			private1[130];
	short 			nlvlFrom; 		/* # of levels from fromFile/toFile until a common */
	short 			nlvlTo;			/* ancestor directory is found */
	char			private2[16];
	varInfo			vdata[1];		/* variable length info */
} AliasRecordPriv;

#ifdef __MACOS__ 
#pragma options align=reset
#endif



// -------------------------------------
// Constructors and destructors
//
QTAtom_dref::QTAtom_dref(QTFile * File, QTFile::AtomTOCEntry * TOCEntry, Bool16 Debug, Bool16 DeepDebug)
	: QTAtom(File, TOCEntry, Debug, DeepDebug),
	  fNumRefs(0), fRefs(NULL)
{
}

QTAtom_dref::~QTAtom_dref(void)
{
	//+ 6.16.1999 rt fix memory leak
	for( UInt32 curRef = 0; curRef < fNumRefs; curRef++ )
	{
		delete [] fRefs[curRef].Data;
		delete fRefs[curRef].FCB;
	}
	//-end
	
	//
	// Free our variables.
	delete[] fRefs;
}



// -------------------------------------
// Initialization functions
//
Bool16 QTAtom_dref::Initialize(void)
{
	// Temporary vars
	UInt32		tempInt32;

	// General vars
	UInt64		refPos;


	//
	// Parse this atom's fields.
	ReadInt32(drefPos_VersionFlags, &tempInt32);
	fVersion = (UInt8)((tempInt32 >> 24) & 0x000000ff);
	fFlags = tempInt32 & 0x00ffffff;

	ReadInt32(drefPos_NumRefs, &fNumRefs);

	//
	// Read in all of the refs.
	if( fNumRefs > 0 ) {
		//
		// Allocate our ref table.
		fRefs = NEW DataRefEntry[fNumRefs];
		if( fRefs == NULL )
			return false;

		//
		// Read them all in..
		refPos = drefPos_RefTable;
		for( UInt32 CurRef = 0; CurRef < fNumRefs; CurRef++ ) {
			//
			// Set up the entry.
			fRefs[CurRef].Flags = 0x0;
			fRefs[CurRef].ReferenceType = FOUR_CHARS_TO_INT('N', 'U', 'L', 'L'); // NULL
			fRefs[CurRef].DataLength = 0;
			fRefs[CurRef].Data = NULL;

			fRefs[CurRef].IsEntryInitialized = false;
			fRefs[CurRef].IsFileOpen = false;
			fRefs[CurRef].FCB = NULL;


			//
			// Get the flags and type.
			ReadInt32(refPos + drefRefPos_VersionFlags, &tempInt32);
			fRefs[CurRef].Flags = tempInt32 & 0x00ffffff;

			ReadInt32(refPos + drefRefPos_Type, &tempInt32);
			fRefs[CurRef].ReferenceType = tempInt32;
			
			//
			// We're done if this is a self-referencing atom.
			if( fRefs[CurRef].Flags & flagSelfRef ) {
				fRefs[CurRef].IsEntryInitialized = true;
				continue;
			}
			

			//
			// Get all of the data.
			ReadInt32(refPos + drefRefPos_Size, &tempInt32);
			fRefs[CurRef].DataLength = tempInt32 - 12 /* skip the header */;
			
			fRefs[CurRef].Data = NEW char[fRefs[CurRef].DataLength];
			if( fRefs[CurRef].Data == NULL ) {
				//
				// De-initialize this entry.
				fRefs[CurRef].DataLength = 0;
				fRefs[CurRef].IsEntryInitialized = false;
			} else {
				//
				// Read the entry data.
				ReadBytes(refPos + drefRefPos_Data, fRefs[CurRef].Data, fRefs[CurRef].DataLength);
				
				//
				// Configure the rest of the entry.
				fRefs[CurRef].FCB = NEW QTFile_FileControlBlock();
				if( fRefs[CurRef].FCB == NULL )
					fRefs[CurRef].IsEntryInitialized = false;
				else
					fRefs[CurRef].IsEntryInitialized = true;
				fRefs[CurRef].IsFileOpen = false;
			}

			//
			// Skip over this mini-atom.
			refPos += fRefs[CurRef].DataLength + 12 /* account for the header */;
		}
	}

	//
	// This atom has been successfully read in.
	return true;
}



// -------------------------------------
// Read functions.
//
Bool16 QTAtom_dref::Read(UInt32 RefID, UInt64 Offset, char * const Buffer, UInt32 Length, QTFile_FileControlBlock * FCB)
{
	// General vars
	DataRefEntry		*Entry;
	
	
	//
	// Validate that this ref exists.
	if( (RefID == 0) || (RefID > fNumRefs) )
		return false;

//	printf("QTAtom_dref::Read Offset = %qd, Length = %ld \n", Offset, Length);
	//
	// If this data reference is in the movie file itself, then we can forward
	// the request directly to QTFile.
	if( IsRefInThisFile(RefID) )
		return fFile->Read(Offset, Buffer, Length, FCB);
	

	//
	// The ref is not in this file; see if we have already opened an FCB for
	// the file and use that, otherwise we need to process the ref.
	Entry = &fRefs[RefID - 1];
	
	//
	// Abort if the entry was not initialized.
	if( !Entry->IsEntryInitialized )
		return false;
	
	//
	// Open the file (after parsing the data reference) if it is not open
	// already.
	if( !Entry->IsFileOpen ) {
		// General vars
		char		*AliasPath;
		
		
		//
		// We only support parsing alias types.
		if( Entry->ReferenceType != FOUR_CHARS_TO_INT('a', 'l', 'i', 's') ) //alis
			return false;
		
		//
		// Parse the alias.
		if( (AliasPath = ResolveAlias(Entry->Data, Entry->DataLength)) == NULL )
			return false;
		
		//
		// Create a path which does *not* contain the current movie's name.
		char * p;
		char * MoviePath = fFile->GetMoviePath();
		char * NewPath = NEW char[strlen(MoviePath) + strlen(AliasPath) + 1];
		//char * NewPath = (char *)calloc(1, strlen(MoviePath) + strlen(AliasPath));
		if( (p = strrchr(MoviePath, QT_PATH_SEPARATOR)) == NULL ) {
			memcpy(NewPath, AliasPath, strlen(AliasPath) + 1);
		} else {
			int MoviePathClippedLength = (p - MoviePath) + 1;
			memcpy(NewPath, MoviePath, MoviePathClippedLength);
			memcpy(NewPath + MoviePathClippedLength, AliasPath, strlen(AliasPath) + 1);
		}

			
		//
		// Do the open.
		Entry->FCB->Set(NewPath);
		if(!Entry->FCB->IsValid()) {
			delete [] NewPath;
			//free(NewPath);
			return false;
		}
		
		Entry->IsFileOpen = true;
		delete [] NewPath;
		//free(NewPath);
	}
	
	//
	// Do the read.
	return fFile->Read(Offset, Buffer, Length, Entry->FCB);
}



// -------------------------------------
// Debugging functions
//
void QTAtom_dref::DumpAtom(void)
{
	DEBUG_PRINT(("QTAtom_dref::DumpAtom - Dumping atom.\n"));
	for( UInt32 CurRef = 1; CurRef <= fNumRefs; CurRef++ )
		DEBUG_PRINT(("QTAtom_dref::DumpAtom - ..Ref #%lu is in file: %s\n", CurRef, IsRefInThisFile(CurRef) ? "yes" : "no"));
}



// -------------------------------------
// Protected member functions.
//
char * QTAtom_dref::ResolveAlias(char * const AliasData, UInt32 /* AliasDataLength */)
{
	// General vars
	AliasRecordPriv		*Alias = (AliasRecordPriv *)AliasData;
	varInfo				*AliasVarInfo;
	
	char				*path, *pathStart;
	int					pathLength;
	
	
	//
	// Verify that we have a relative path.
	if( (Alias->nlvlTo == -1) || (Alias->nlvlFrom == -1) )
		return NULL;
	
	//
	// Search for the absolute pathname in this alias.
	AliasVarInfo = Alias->vdata;
	for( int loopCount = kMaxMark - 1; loopCount >= 0; loopCount--) {
		//
		// Break out of the loop if this is a match/the end of the alias.
		if( ((short)ntohs(AliasVarInfo->what) == kAbsPath) || ((short)ntohs(AliasVarInfo->what) == kEndMark) )
			break;
		
		//
		// Otherwise we need to move to the next data unit.
		AliasVarInfo = (varInfo *)((char *)AliasVarInfo + ((ntohs(AliasVarInfo->len) + 1) & ~1) + 4 /* header size */);
	}


	//
	// Now that we have the path, we need to strip off the absolute portions
	// of it so that we can get at it from our current (relative) root.
	AliasVarInfo->data[ntohs(AliasVarInfo->len)] = '\0';
	
	pathStart = path = AliasVarInfo->data;
	path += ntohs(AliasVarInfo->len);
	int i = ntohs(Alias->nlvlTo);
	pathLength = -1;
	while( i && (path > pathStart) ) {
		if( *path-- == ':' )
			i--;
		pathLength++;
	}
	
	if( i == 1 )
		pathLength += 2;	// fell out of loop; we're relative to root
	else
		path += 2;			// points past separator character
	
	
	//
	// Return the alias.
	return path;
}
