// =================================================================================================
// Copyright Adobe
// Copyright 2005 Adobe
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it. 
// =================================================================================================

#if XMP_WinBuild
	#pragma warning ( disable : 4996 )	// '...' was declared deprecated
#endif

#include "public/include/XMP_Environment.h"	// ! XMP_Environment.h must be the first included header.

#include "public/include/XMP_Const.h"
#include "public/include/XMP_IO.hpp"

#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "source/XMPFiles_IO.hpp"
#include "source/XIO.hpp"
#include "source/IOUtils.hpp"

#include "XMPFiles/source/FileHandlers/MPEG2_Handler.hpp"
#include "../FormatSupport/PackageFormat_Support.hpp"
using namespace std;

// =================================================================================================
/// \file MPEG2_Handler.cpp
/// \brief File format handler for MPEG2.
///
/// BLECH! YUCK! GAG! MPEG-2 is done using a sidecar and recognition only by file extension! BARF!!!!!
///
// =================================================================================================

// =================================================================================================
// FindFileExtension
// =================

static inline XMP_StringPtr FindFileExtension ( XMP_StringPtr filePath )
{

	XMP_StringPtr pathEnd = filePath + strlen(filePath);
	XMP_StringPtr extPtr;

	for ( extPtr = pathEnd-1; extPtr > filePath; --extPtr ) {
		if ( (*extPtr == '.') || (*extPtr == '/') ) break;
		#if XMP_WinBuild
			if ( (*extPtr == '\\') || (*extPtr == ':') ) break;
		#endif
	}

	if ( (extPtr < filePath) || (*extPtr != '.') ) return pathEnd;
	return extPtr;

}	// FindFileExtension

// =================================================================================================
// MPEG2_MetaHandlerCTor
// =====================

XMPFileHandler * MPEG2_MetaHandlerCTor ( XMPFiles * parent )
{
	return new MPEG2_MetaHandler ( parent );

}	// MPEG2_MetaHandlerCTor

// =================================================================================================
// MPEG2_CheckFormat
// =================

// The MPEG-2 handler uses just the file extension, not the file content. Worse yet, it also uses a
// sidecar file for the XMP. This works better if the handler owns the file, we open the sidecar
// instead of the actual MPEG-2 file.

bool MPEG2_CheckFormat ( XMP_FileFormat format,
						XMP_StringPtr  filePath,
						XMP_IO*    fileRef,
						XMPFiles *     parent )
{
	IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(fileRef);

	XMP_Assert ( (format == kXMP_MPEGFile) || (format == kXMP_MPEG2File) );
	XMP_Assert ( fileRef == 0 );

	return ( (parent->format == kXMP_MPEGFile) || (parent->format == kXMP_MPEG2File) );	// ! Just use the first call's format hint.

}	// MPEG2_CheckFormat

// =================================================================================================
// MPEG2_MetaHandler::MPEG2_MetaHandler
// ====================================

MPEG2_MetaHandler::MPEG2_MetaHandler ( XMPFiles * _parent )
{
	this->parent = _parent;
	this->handlerFlags = kMPEG2_HandlerFlags;
	this->stdCharForm  = kXMP_Char8Bit;

	XMP_StringPtr filePath = this->parent->GetFilePath().c_str();
	XMP_StringPtr extPtr = FindFileExtension ( filePath );
	this->sidecarPath.assign ( filePath, (extPtr - filePath) );
	this->sidecarPath += ".xmp";
}	// MPEG2_MetaHandler::MPEG2_MetaHandler

// =================================================================================================
// MPEG2_MetaHandler::~MPEG2_MetaHandler
// =====================================

MPEG2_MetaHandler::~MPEG2_MetaHandler()
{
	// Nothing to do.

}	// MPEG2_MetaHandler::~MPEG2_MetaHandler

// =================================================================================================
// MPEG2_MetaHandler::GetFileModDate
// =================================

bool MPEG2_MetaHandler::GetFileModDate ( XMP_DateTime * modDate )
{
	if ( ! Host_IO::Exists ( this->sidecarPath.c_str() ) ) return false;
	return Host_IO::GetModifyDate ( this->sidecarPath.c_str(), modDate );

}	// MPEG2_MetaHandler::GetFileModDate

// =================================================================================================
// MPEG2_MetaHandler::FillAssociatedResources
// =================================
void MPEG2_MetaHandler::FillAssociatedResources ( std::vector<std::string> * resourceList )
{
	resourceList->push_back(this->parent->GetFilePath());
	PackageFormat_Support::AddResourceIfExists(resourceList, this->sidecarPath);
}	// MPEG2_MetaHandler::FillAssociatedResources

// =================================================================================================
// MPEG2_MetaHandler::IsMetadataWritable
// =================================
bool MPEG2_MetaHandler::IsMetadataWritable ( )
{
	return Host_IO::Writable( this->sidecarPath.c_str(), true );
}	// MPEG2_MetaHandler::IsMetadataWritable

// =================================================================================================
// MPEG2_MetaHandler::CacheFileData
// ================================

void MPEG2_MetaHandler::CacheFileData()
{
	bool readOnly = (! (this->parent->openFlags & kXMPFiles_OpenForUpdate));

	if ( this->parent->UsesClientIO() ) {
		XMP_Throw ( "MPEG2 cannot be used with client-managed I/O", kXMPErr_InternalFailure );
	}

	this->containsXMP = false;
	this->processedXMP = true;	// Whatever we do here is all that we do for XMPFiles::OpenFile.

	// Try to open the sidecar XMP file. Tolerate an open failure, there might not be any XMP.
	// Note that MPEG2_CheckFormat can't save the sidecar path because the handler doesn't exist then.

	if ( ! Host_IO::Exists ( this->sidecarPath.c_str() ) ) return;	// OK to not have XMP.

	XMPFiles_IO * localFile = XMPFiles_IO::New_XMPFiles_IO ( this->sidecarPath.c_str(), readOnly );
	if ( localFile == 0 ) XMP_Throw ( "Failure opening MPEG-2 XMP file", kXMPErr_ExternalFailure );
	this->parent->ioRef = localFile;

	// Extract the sidecar's contents and parse.

	this->packetInfo.offset = 0;	// We take the whole sidecar file.
	this->packetInfo.length = (XMP_Int32) localFile->Length();

	if ( this->packetInfo.length > 0 ) {

		this->xmpPacket.assign ( this->packetInfo.length, ' ' );
		localFile->ReadAll ( (void*)this->xmpPacket.c_str(), this->packetInfo.length );

		this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
		this->containsXMP = true;

	}

	if ( readOnly ) {
		localFile->Close();
		delete localFile;
		this->parent->ioRef = 0;
	}

}	// MPEG2_MetaHandler::CacheFileData

// =================================================================================================
// MPEG2_MetaHandler::UpdateFile
// =============================

void MPEG2_MetaHandler::UpdateFile ( bool doSafeUpdate )
{
	if ( ! this->needsUpdate ) return;

	XMP_Assert ( this->parent->UsesLocalIO() );

	if ( this->parent->ioRef == 0 ) {
		XMP_Assert ( ! Host_IO::Exists ( this->sidecarPath.c_str() ) );
		Host_IO::Create ( this->sidecarPath.c_str() );
		this->parent->ioRef = XMPFiles_IO::New_XMPFiles_IO ( this->sidecarPath.c_str(), Host_IO::openReadWrite );
		if ( this->parent->ioRef == 0 ) XMP_Throw ( "Failure opening MPEG-2 XMP file", kXMPErr_ExternalFailure );
	}

	XMP_IO* fileRef = this->parent->ioRef;
	XMP_Assert ( fileRef != 0 );
	XIO::ReplaceTextFile ( fileRef, this->xmpPacket, doSafeUpdate );

	XMPFiles_IO* localFile = (XMPFiles_IO*)fileRef;
	localFile->Close();
	delete localFile;
	this->parent->ioRef = 0;

	this->needsUpdate = false;

}	// MPEG2_MetaHandler::UpdateFile

// =================================================================================================
// MPEG2_MetaHandler::WriteTempFile
// ================================

void MPEG2_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
{
	IgnoreParam(tempRef);

	XMP_Throw ( "MPEG2_MetaHandler::WriteTempFile: Should never be called", kXMPErr_Unavailable );

}	// MPEG2_MetaHandler::WriteTempFile
