

#ifndef x64

#include "texception.h"
#include "tiio_3gp.h"
#include "tsound.h"
#include "tconvert.h"
#include "tpropertytype.h"
//#include "trop.h"
#include "../mov/tiio_mov.h"
//#include "timageinfo.h"
#include "movsettings.h"
#include "trasterimage.h"
#include "tsystem.h"

namespace
{

int CompressionNoneId = 0;

class QuickTimeCleanUp;
class QuickTimeStuff
{
public:
	static QuickTimeStuff *instance()
	{
		if (!m_singleton)
			m_singleton = new QuickTimeStuff();
		return m_singleton;
	}
	OSErr getStatus() { return m_status; }
	~QuickTimeStuff()
	{
	}

private:
	QuickTimeStuff() : m_status(noErr)
	{
		m_status = InitializeQTML(0);
		EnterMovies();
	}

	static QuickTimeStuff *m_singleton;

	OSErr m_status;
	friend class QuickTimeCleanUp; //questa DEVE essere friend, cosi' posso controllare direttamente
								   //lo stato del singleton.
};

class QuickTimeCleanUp
{
public:
	QuickTimeCleanUp() {}
	~QuickTimeCleanUp()
	{ /*
                        Nel caso si arrivasse qui senza il singleton instanziato, e si facesse direttamente
                        'delete QuickTimeStuff::instance();' Quicktime non farebbe in tempo a terminare le
                        sue routine di inizializzazione (che sono ANCHE su altri thread) e la chiamata a
                        TerminateQTML() causerebbe un crash
                        */

		if (QuickTimeStuff::m_singleton)
			delete QuickTimeStuff::m_singleton;
	}
};

QuickTimeCleanUp cleanUp;
QuickTimeStuff *QuickTimeStuff::m_singleton = 0;

//------------------------------------------------------------------------------

enum QTLibError {
	QTNoError = 0x0000,
	QTNotInstalled,
	QTUnknownError,
	QTUnableToOpenFile,
	QTCantCreateFile,
	QTUnableToGetCompressorNames,
	QTUnableToCreateResource,
	QTUnableToUpdateMovie,
	QTBadTimeValue,
	QTUnableToDoMovieTask,
	QTUnableToSetTimeValue,
	QTUnableToSetMovieGWorld,
	QTUnableToSetMovieBox,
};

string buildQTErrorString(int ec)
{
	switch (ec) {
	case QTNotInstalled:
		return "Can't create; ensure that quicktime is correctly installed on your machine";
	case QTUnknownError:
		return "Unknown error";
	case QTUnableToOpenFile:
		return "can't open file";
	case QTCantCreateFile:
		return "can't create movie";
	case QTUnableToGetCompressorNames:
		return "unable to get compressor name";
	case QTUnableToCreateResource:
		return "can't create resource";
	case QTUnableToUpdateMovie:
		return "unable to update movie";
	case QTBadTimeValue:
		return "bad frame number";
	case QTUnableToDoMovieTask:
		return "unable to do movie task";
	case QTUnableToSetTimeValue:
		return "unable to set time value";
	case QTUnableToSetMovieGWorld:
		return "unable to set movie graphic world";
	case QTUnableToSetMovieBox:
		return "unable to set movie box";

	default: {
		return "unknown error ('" + toString(ec) + "')";
	}
	}
}

//-----------------------------------------------------------

inline LPSTR AtlW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars, UINT acp)
{
	assert(lpw != NULL);
	assert(lpa != NULL);
	// verify that no illegal character present
	// since lpa was allocated based on the size of lpw
	// don't worry about the number of chars
	lpa[0] = '\0';
	WideCharToMultiByte(acp, 0, lpw, -1, lpa, nChars, NULL, NULL);
	return lpa;
}

//-----------------------------------------------------------

inline char *filePath2unichar(const TFilePath &path)
{
	int _convert = 0;
	std::wstring ws = path.getWideString();

	LPCWSTR lpw = ws.c_str();
	char *name = NULL;

	if (lpw) {
		_convert = (lstrlenW(lpw) + 1) * 2;
		LPSTR pStr = new char[_convert];
		name = AtlW2AHelper(pStr, lpw, _convert, 0);
	}

	char *outName = new char[1024];

	if (QTMLGetCanonicalPathName(name, outName, 1024) == noErr) {
		delete[] name;
		return outName;
	}
	delete[] outName;
	return name;
}

//-----------------------------------------------------------

TFilePath getLegalName(const TFilePath &name)
{
	if (QuickTimeStuff::instance()->getStatus() != noErr)
		return name;

	TFilePath legalName;
	char outDir[1024];

	TFilePath dirName(name.getParentDir());

	char dirNameFp[1024] = "";

	OSErr err = QTMLGetCanonicalPathName(dirNameFp, outDir, 1024);

	if (err == noErr)
		legalName = TFilePath(outDir) + name.withoutParentDir();
	else
		legalName = name;

	return legalName;
}

//--------------------------`----------------------------------------------------

string long2fourchar(TINT32 fcc)
{
	string s;
	s += (char((fcc & 0xff000000) >> 24));
	s += (char((fcc & 0x00ff0000) >> 16));
	s += (char((fcc & 0x0000ff00) >> 8));
	s += (char((fcc & 0x000000ff) >> 0));

	return s;
}

const std::string CodecNamesId = "PU_CodecName";
const std::string CodecQualityId = "PU_CodecQuality";

} //namespace

//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//  TImageWriterMov
//------------------------------------------------------------------------------

class TImageWriter3gp : public TImageWriter
{

public:
	TImageWriter3gp(const TFilePath &, int frameIndex, TLevelWriter3gp *);
	~TImageWriter3gp() { m_lwm->release(); }
	bool is64bitOutputSupported() { return false; }

private:
	//not implemented
	TImageWriter3gp(const TImageWriter3gp &);
	TImageWriter3gp &operator=(const TImageWriter3gp &src);

public:
	void save(const TImageP &);
	int m_frameIndex;

private:
	TLevelWriter3gp *m_lwm;
};

//-----------------------------------------------------------
//  TImageReaderv
//-----------------------------------------------------------
class TImageReader3gp : public TImageReader
{

public:
	TImageReader3gp(const TFilePath &, int frameIndex, TLevelReader3gp *);
	~TImageReader3gp() { m_lrm->release(); }

private:
	//not implemented
	TImageReader3gp(const TImageReader3gp &);
	TImageReader3gp &operator=(const TImageReader3gp &src);

public:
	TImageP load();
	void load(const TRasterP &rasP, const TPoint &pos = TPoint(0, 0), int shrinkX = 1, int shrinkY = 1);
	int m_frameIndex;

	TDimension getSize() const { return m_lrm->getSize(); }
	TRect getBBox() const { return m_lrm->getBBox(); }

private:
	TLevelReader3gp *m_lrm;
};

//-----------------------------------------------------------
//-----------------------------------------------------------
//        TImageWriter3gp
//-----------------------------------------------------------

TImageWriter3gp::TImageWriter3gp(const TFilePath &path, int frameIndex, TLevelWriter3gp *lwm)
	: TImageWriter(path), m_lwm(lwm), m_frameIndex(frameIndex)
{
	m_lwm->addRef();
}

//----------
namespace
{
void copy(TRasterP rin, PixelXRGB *bufout, int lx, int ly)
{
	rin->lock();
	TRaster32P rin32 = rin;
	assert(rin32);

	PixelXRGB *rowout = &(bufout[(ly - 1) * lx]);
	for (int y = 0; y < rin32->getLy(); y++) {
		PixelXRGB *pixout = rowout;
		TPixelRGBM32 *pixin = rin32->pixels(y);
		TPixelRGBM32 *pixinEnd = pixin + rin32->getLx();
		while (pixin < pixinEnd) {
			pixout->x = pixin->m;
			pixout->r = pixin->r;
			pixout->g = pixin->g;
			pixout->b = pixin->b;
			pixout++;
			pixin++;
		}
		rowout -= lx;
	}
	rin->unlock();
}
};

//-----------------------------------------------------------
void TImageWriter3gp::save(const TImageP &img)
{
	m_lwm->save(img, m_frameIndex);
}

//-----------------------------------------------------------

void TLevelWriter3gp::save(const TImageP &img, int frameIndex)
{
	if (m_cancelled)
		return;
	string msg;
	QMutexLocker sl(&m_mutex);

	TRasterImageP image(img);
	TRasterP ras = image->getRaster();
	int lx = ras->getLx();
	int ly = ras->getLy();

	ras->lock();
	void *buffer = image->getRaster()->getRawData();
	int pixSize = image->getRaster()->getPixelSize();
	if (pixSize != 4) {
		msg = "Unsupported pixel type";
		goto error;
	}

	if (!m_properties)
		m_properties = new Tiio::MovWriterProperties();

	Tiio::MovWriterProperties *prop = (Tiio::MovWriterProperties *)(m_properties);

	QTAtomContainer atoms;
	QTNewAtomContainer(&atoms);
	fromPropertiesToAtoms(*prop, atoms);
	ComponentInstance ci = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
	if (SCSetSettingsFromAtomContainer(ci, atoms))
		assert(false);

	/*
CodecType compression = prop->getCurrentCodec();
CodecQ quality = prop->getCurrentQuality();
*/

	if (!m_initDone) {
		Rect frame;
		QDErr err;

		m_videoTrack = NewMovieTrack(m_movie, FixRatio((short)lx, 1), FixRatio((short)ly, 1), kNoVolume);

		if ((err = GetMoviesError() != noErr)) {
			msg = "can't create video track";
			goto error;
		}

		m_dataRef = nil;
		m_hMovieData = NewHandle(0);

		// Construct the Handle data reference
		err = PtrToHand(&m_hMovieData, &m_dataRef, sizeof(Handle));

		if ((err = GetMoviesError() != noErr)) {
			msg = "can't create Data Ref";
			goto error;
		}

		m_videoMedia = NewTrackMedia(m_videoTrack, VideoMediaType, (TINT32)m_frameRate, m_dataRef, HandleDataHandlerSubType);

		OpenADefaultComponent(MovieExportType, '3gpp', &m_myExporter);
		if (TSystem::doHaveMainLoop())
			err = (short)MovieExportDoUserDialog(m_myExporter, m_movie, 0, 0, 0, &m_cancelled);
		if (m_cancelled) {
			msg = "user abort of 3gp creation";
			goto error;
		}
		if ((err = GetMoviesError() != noErr)) {
			msg = "can't create video media";
			goto error;
		}

		if ((err = BeginMediaEdits(m_videoMedia)) != noErr) {
			msg = "can't begin edit video media";
			goto error;
		}

		frame.left = 0;
		frame.top = 0;
		frame.right = lx;
		frame.bottom = ly;

		if ((err = NewGWorld(&(m_gworld), pixSize * 8, &frame, 0, 0, 0)) != noErr) {
			msg = "can't create movie buffer";
			goto error;
		}

		LockPixels(m_gworld->portPixMap);
		/*if ((err = GetMaxCompressionSize(m_gworld->portPixMap, &frame, 0, 
                                quality,  compression,anyCodec, 
																 &max_compressed_size))!=noErr)
    throw TImageException(getFilePath(), "can't get max compression size");
*/

		if ((err = MemError()) != noErr) {
			msg = "can't allocate compressed data for movie";
			goto error;
		}

		if ((err = MemError()) != noErr) {
			msg = "can't allocate img handle";
			goto error;
		}

		m_pixmap = GetGWorldPixMap(m_gworld);
		if (!LockPixels(m_pixmap)) {
			msg = "can't lock pixels";
			goto error;
		}

		buf = (PixelXRGB *)(*(m_pixmap))->baseAddr;
		buf_lx = lx;
		buf_ly = ly;

		m_initDone = true;
	}

	Rect frame;
	ImageDescriptionHandle img_descr = 0;
	Handle compressedData = 0;

	QDErr err;

	frame.left = 0;
	frame.top = 0;
	frame.right = lx;
	frame.bottom = ly;

	copy(ras, buf, buf_lx, buf_ly);

	if ((err = (QDErr)SCCompressImage(
			 ci,
			 m_gworld->portPixMap,
			 &frame,
			 &img_descr,
			 &compressedData)) != noErr) {
		msg = "can't compress image";
		goto error;
	}

	/*
if ((err = CompressImage(m_gworld->portPixMap, 
	                       &frame, 
												 quality, compression,
												 img_descr, compressed_data_ptr))!=noErr)
  throw TImageException(getFilePath(), "can't compress image");
*/
	if ((err = AddMediaSample(m_videoMedia, compressedData, 0,
							  (*img_descr)->dataSize, 1,
							  (SampleDescriptionHandle)img_descr,
							  1, 0, 0)) != noErr) {
		msg = "can't add image to movie media";
		goto error;
	}

	DisposeHandle((Handle)img_descr);
	DisposeHandle(compressedData);
	ras->unlock();
	return;

error:
	ras->unlock();
	throw TImageException(getFilePath(), msg);
}

//-----------------------------------------------------------
//        TLevelWriterMov
//-----------------------------------------------------------

TLevelWriter3gp::TLevelWriter3gp(const TFilePath &path, TPropertyGroup *winfo)
	: TLevelWriter(path, winfo), m_initDone(false), m_IOError(QTNoError), m_pixmap(0), m_gworld(0), m_videoMedia(0), m_videoTrack(0), m_movie(0), m_cancelled(false), m_soundDataRef(0), m_hSoundMovieData(0)

{
	m_frameRate = 12.;

	if (QuickTimeStuff::instance()->getStatus() != noErr) {
		m_IOError = QTNotInstalled;
		throw TImageException(m_path, buildQTErrorString(m_IOError));
	}

	QDErr err;

	m_movie = NewMovie(0);
	if ((err = GetMoviesError() != noErr))
		throw TImageException(getFilePath(), "can't create movie");
}

//-----------------------------------------------------------

#ifdef _DEBUG
#define FailIf(cond, handler)                                 \
	if (cond) {                                               \
		DebugStr((ConstStr255Param) #cond " goto " #handler); \
		goto handler;                                         \
	} else                                                    \
	0
#else
#define FailIf(cond, handler) \
	if (cond) {               \
		goto handler;         \
	} else                    \
	0
#endif

#ifdef _DEBUG
#define FailWithAction(cond, action, handler)                 \
	if (cond) {                                               \
		DebugStr((ConstStr255Param) #cond " goto " #handler); \
		{                                                     \
			action;                                           \
		}                                                     \
		goto handler;                                         \
	} else                                                    \
	0
#else
#define FailWithAction(cond, action, handler) \
	if (cond) {                               \
		{                                     \
			action;                           \
		}                                     \
		goto handler;                         \
	} else                                    \
	0
#endif

void TLevelWriter3gp::saveSoundTrack(TSoundTrack *st)
{
	Track theTrack;
	OSErr myErr = noErr;
	SoundDescriptionV1Handle mySampleDesc;
	Media myMedia;
	Handle myDestHandle;
	SoundComponentData sourceInfo;
	SoundComponentData destInfo;
	SoundConverter converter;
	CompressionInfo compressionInfo;
	int err;

	if (m_cancelled)
		return;

	if (!st)
		throw TException("null reference to soundtrack");

	if (st->getBitPerSample() != 16) {
		throw TImageException(m_path, "Only 16 bits per sample is supported");
	}

	theTrack = NewMovieTrack(m_movie, 0, 0, kFullVolume);
	myErr = GetMoviesError();

	FailIf(myErr != noErr, CompressErr);

	myDestHandle = NewHandle(0);

	FailWithAction(myDestHandle == NULL, myErr = MemError(), NoDest);

	*myDestHandle = (char *)st->getRawData();

	//////////
	//
	// create a media for the track passed in
	//
	//////////

	// set new track to be a sound track

	m_soundDataRef = nil;
	m_hSoundMovieData = NewHandle(0);

	// Construct the Handle data reference
	err = PtrToHand(&m_hSoundMovieData, &m_soundDataRef, sizeof(Handle));

	if ((err = GetMoviesError() != noErr))
		throw TImageException(getFilePath(), "can't create Data Ref");

	myMedia = NewTrackMedia(theTrack, SoundMediaType, st->getSampleRate(), m_soundDataRef, HandleDataHandlerSubType); //track->rate >> 16
	myErr = GetMoviesError();
	FailIf(myErr != noErr, Exit);

	// start a media editing session
	myErr = BeginMediaEdits(myMedia);
	FailIf(myErr != noErr, Exit);

	sourceInfo.flags = 0x0;
	sourceInfo.format = kSoundNotCompressed;
	sourceInfo.numChannels = st->getChannelCount();
	sourceInfo.sampleSize = st->getBitPerSample();
	sourceInfo.sampleRate = st->getSampleRate();
	sourceInfo.sampleCount = st->getSampleCount();
	sourceInfo.buffer = (unsigned char *)st->getRawData();
	sourceInfo.reserved = 0x0;

	destInfo.flags = kNoSampleRateConversion | kNoSampleSizeConversion |
					 kNoSampleFormatConversion | kNoChannelConversion |
					 kNoDecompression | kNoVolumeConversion |
					 kNoRealtimeProcessing;

	destInfo.format = k16BitNativeEndianFormat;

	destInfo.numChannels = st->getChannelCount();
	destInfo.sampleSize = st->getBitPerSample();
	destInfo.sampleRate = st->getSampleRate();
	destInfo.sampleCount = st->getSampleCount();
	destInfo.buffer = (unsigned char *)st->getRawData();
	destInfo.reserved = 0x0;

	SoundConverterOpen(&sourceInfo, &destInfo, &converter);

	myErr = SoundConverterGetInfo(converter, siCompressionFactor, &compressionInfo);

	myErr = SoundConverterGetInfo(converter, siCompressionFactor, &compressionInfo);
	myErr = GetCompressionInfo(fixedCompression, sourceInfo.format, sourceInfo.numChannels, sourceInfo.sampleSize, &compressionInfo);
	FailIf(myErr != noErr, ConverterErr);

	compressionInfo.bytesPerFrame = compressionInfo.bytesPerPacket * destInfo.numChannels;

	//////////
	//
	// create a sound sample description
	//
	//////////

	// use the SoundDescription format 1 because it adds fields for data size information
	// and is required by AddSoundDescriptionExtension if an extension is required for the compression format

	mySampleDesc = (SoundDescriptionV1Handle)NewHandleClear(sizeof(SoundDescriptionV1));
	FailWithAction(myErr != noErr, myErr = MemError(), Exit);

	(**mySampleDesc).desc.descSize = sizeof(SoundDescriptionV1);
	(**mySampleDesc).desc.dataFormat = destInfo.format;
	(**mySampleDesc).desc.resvd1 = 0;
	(**mySampleDesc).desc.resvd2 = 0;
	(**mySampleDesc).desc.dataRefIndex = 1;
	(**mySampleDesc).desc.version = 1;
	(**mySampleDesc).desc.revlevel = 0;
	(**mySampleDesc).desc.vendor = 0;
	(**mySampleDesc).desc.numChannels = destInfo.numChannels;
	(**mySampleDesc).desc.sampleSize = destInfo.sampleSize;
	(**mySampleDesc).desc.compressionID = 0;
	(**mySampleDesc).desc.packetSize = 0;
	(**mySampleDesc).desc.sampleRate = st->getSampleRate() << 16;
	(**mySampleDesc).samplesPerPacket = compressionInfo.samplesPerPacket;
	(**mySampleDesc).bytesPerPacket = compressionInfo.bytesPerPacket;
	(**mySampleDesc).bytesPerFrame = compressionInfo.bytesPerFrame;
	(**mySampleDesc).bytesPerSample = compressionInfo.bytesPerSample;

	//////////
	//
	// add samples to the media
	//
	//////////

	myErr = AddMediaSample(myMedia, myDestHandle,
						   0,
						   destInfo.sampleCount * compressionInfo.bytesPerFrame,
						   1,
						   (SampleDescriptionHandle)mySampleDesc,
						   destInfo.sampleCount * compressionInfo.samplesPerPacket,
						   0,
						   NULL);
	FailIf(myErr != noErr, MediaErr);

	myErr = EndMediaEdits(myMedia);
	FailIf(myErr != noErr, MediaErr);

	//////////
	//
	// insert the media into the track
	//
	//////////

	myErr = InsertMediaIntoTrack(theTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
	FailIf(myErr != noErr, MediaErr);

	goto Done;

ConverterErr:
NoDest:
CompressErr:
Exit:

Done:

MediaErr:
	if (mySampleDesc != NULL)
		DisposeHandle((Handle)mySampleDesc);

	if (converter)
		SoundConverterClose(converter);

	if (myErr != noErr)
		throw TImageException(m_path, "error saving audio track");
}

//-----------------------------------------------------------

TLevelWriter3gp::~TLevelWriter3gp()
{
	if (m_pixmap)
		UnlockPixels(m_pixmap);
	if (m_gworld)
		DisposeGWorld(m_gworld);
	QDErr err;

	if (m_videoMedia)
		if ((err = EndMediaEdits(m_videoMedia)) != noErr) {
		} // throw TImageException(getFilePath(), "can't end edit media");

	if (m_videoTrack)
		if ((err = InsertMediaIntoTrack(m_videoTrack, 0, 0,
										GetMediaDuration(m_videoMedia), fixed1))) {
		} // throw TImageException(getFilePath(), "can't insert media into track");

	short resId = movieInDataForkResID;
	if (m_movie) {
		FSSpec fspec;
		long myFlags = 0L;
		OSErr myErr = noErr;
		UCHAR myCancelled = FALSE;
		char *pStr = filePath2unichar(m_path);

		NativePathNameToFSSpec(pStr, &fspec, kFullNativePath);

		myFlags = createMovieFileDeleteCurFile; // |
												//movieFileSpecValid | movieToFileOnlyExport;

		myErr = ConvertMovieToFile(
			m_movie,				// the movie to convert
			NULL,					// all tracks in the movie
			&fspec,					// the output file
			'3gpp',					// the output file type
			FOUR_CHAR_CODE('TVOD'), // the output file creator
			smSystemScript,			// the script
			&resId,					// no resource ID to be returned
			myFlags,				// export flags
			m_myExporter);			// no specific exp
	}

	DisposeHandle(m_hMovieData);
	DisposeHandle(m_dataRef);
	if (m_hSoundMovieData)
		DisposeHandle(m_hMovieData);
	if (m_hSoundMovieData)
		DisposeHandle(m_hSoundMovieData);

	if (m_refNum)
		CloseMovieFile(m_refNum);
	DisposeMovie(m_movie);
}

//-----------------------------------------------------------

TImageWriterP TLevelWriter3gp::getFrameWriter(TFrameId fid)
{
	if (m_cancelled)
		return 0;

	if (m_IOError)
		throw TImageException(m_path, buildQTErrorString(m_IOError));
	if (fid.getLetter() != 0)
		return TImageWriterP(0);
	int index = fid.getNumber() - 1;

	TImageWriter3gp *iwm = new TImageWriter3gp(m_path, index, this);
	return TImageWriterP(iwm);
}

//-----------------------------------------------------------

TLevelReader3gp::TLevelReader3gp(const TFilePath &path)
	: TLevelReader(path), m_IOError(QTNoError), m_track(0), m_movie(0), m_depth(0)
//                ,m_timeScale(0)
{
	FSSpec fspec;
	QDErr err;
	Boolean dataRefWasChanged;
	if (QuickTimeStuff::instance()->getStatus() != noErr) {
		m_IOError = QTNotInstalled;
		return;
	}

	char *pStr = filePath2unichar(m_path);

	if ((err = NativePathNameToFSSpec(pStr, &fspec, kFullNativePath)) != noErr) {
		delete[] pStr;
		pStr = 0;
		throw TImageException(path, "can't open file");
	}
	delete[] pStr;
	pStr = 0;
	if ((err = OpenMovieFile(&fspec, &m_refNum, fsRdPerm))) {
		m_IOError = QTUnableToOpenFile;
		return;
	}

	m_resId = 0;
	Str255 name;
	err = NewMovieFromFile(&m_movie, m_refNum, &m_resId,
						   name, fsRdPerm, &dataRefWasChanged);

	int numTracks = GetMovieTrackCount(m_movie);
	assert(numTracks == 1 || numTracks == 2);

	m_track = GetMovieIndTrackType(m_movie, 1, VideoMediaType, movieTrackMediaType);

	//m_track=GetMovieTrack(m_movie,numTracks);

	ImageDescriptionHandle imageH;
	imageH = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
	TINT32 index = 1;
	Media theMedia = GetTrackMedia(m_track);

	GetMediaSampleDescription(theMedia, index, (SampleDescriptionHandle)imageH);
	ImageDescriptionPtr imagePtr = *imageH;
	m_lx = imagePtr->width;
	m_ly = imagePtr->height;
	m_depth = imagePtr->depth;

	DisposeHandle((Handle)imageH);

	//m_info->m_frameRate = GetMediaTimeScale(theMedia);
}

//------------------------------------------------

//------------------------------------------------

//------------------------------------------------
// TImageReaderMov
//------------------------------------------------

TImageReader3gp::TImageReader3gp(const TFilePath &path, int frameIndex, TLevelReader3gp *lrm)
	: TImageReader(path), m_lrm(lrm), m_frameIndex(frameIndex)
{
	m_lrm->addRef();
}

//------------------------------------------------

TLevelReader3gp::~TLevelReader3gp()
{
	StopMovie(m_movie);

	if (m_refNum)
		CloseMovieFile(m_refNum);
	if (m_movie)
		DisposeMovie(m_movie);
}

//------------------------------------------------

TLevelP TLevelReader3gp::loadInfo()
{
	TLevelP level;
	if (m_IOError != QTNoError)
		throw TImageException(m_path, buildQTErrorString(m_IOError));

	OSType mediaType = VisualMediaCharacteristic;
	TimeValue nextTime, currentTime;
	currentTime = 0;
	nextTime = 0;
	//per il primo
	int f = 1;
	// io vorrei fare '|', ma sul manuale c'e' scritto '+'
	GetMovieNextInterestingTime(m_movie, nextTimeMediaSample + nextTimeEdgeOK, 1, &mediaType, currentTime, 0, &nextTime, 0);
	if (nextTime != -1) {
		TFrameId frame(f);
		level->setFrame(frame, TImageP());
		currentTimes.push_back(nextTime);
		f++;
	}
	currentTime = nextTime;
	while (nextTime != -1) {
		GetMovieNextInterestingTime(m_movie, nextTimeMediaSample, 1, &mediaType, currentTime, 0, &nextTime, 0);
		if (nextTime != -1) {
			TFrameId frame(f);
			level->setFrame(frame, TImageP());
			currentTimes.push_back(nextTime);
			f++;
		}
		currentTime = nextTime;
	}
	return level;
}

//------------------------------------------------

TImageP TImageReader3gp::load()
{
	TRasterPT<TPixelRGBM32> ret(m_lrm->getSize());

	m_lrm->load(ret, m_frameIndex, TPointI(), 1, 1);

	return TRasterImageP(ret);
}

//------------------------------------------------

void TImageReader3gp::load(const TRasterP &rasP, const TPoint &pos, int shrinkX, int shrinkY)
{
	if ((shrinkX != 1) || (shrinkY != 1) || (pos != TPoint(0, 0))) {
		TImageReader::load(rasP, pos, shrinkX, shrinkY);
	} else
		m_lrm->load(rasP, m_frameIndex, pos, shrinkX, shrinkY);
}
//------------------------------------------------
inline void setMatteAndYMirror(const TRaster32P &ras)
{
	ras->lock();
	TPixel32 *upRow = ras->pixels();
	TPixel32 *dwRow = ras->pixels(ras->getLy() - 1);
	int hLy = (int)(ras->getLy() / 2. + 0.5); //piccola pessimizzazione...
	int wrap = ras->getWrap();
	int lx = ras->getLx();
	TPixel32 *upPix = 0;
	TPixel32 *lastPix = ras->pixels(hLy);
	while (upPix < lastPix) {
		upPix = upRow;
		TPixel32 *dwPix = dwRow;
		TPixel32 *endPix = upPix + lx;
		while (upPix < endPix) {
			TPixel32 tmpPix(upPix->r, upPix->g, upPix->b, 0xff);
			*upPix = *dwPix;
			upPix->m = 0xff;
			*dwPix = tmpPix;
			++upPix;
			++dwPix;
		}
		upRow += wrap;
		dwRow -= wrap;
	}
	ras->unlock();
}

//------------------------------------------------

void TLevelReader3gp::load(const TRasterP &rasP, int frameIndex, const TPoint &pos, int shrinkX, int shrinkY)
{
	{
		QMutexLocker sl(&m_mutex);
		if (m_IOError != QTNoError)
			throw TImageException(m_path, buildQTErrorString(m_IOError));

		TRaster32P ras = rasP;

		Rect rect;
		rect.right = pos.x + ras->getLx();
		rect.left = pos.x;
		rect.bottom = pos.y + ras->getLy();
		rect.top = pos.y;

		GWorldPtr offscreenGWorld;
		OSErr err;
		ras->lock();
		err = QTNewGWorldFromPtr(
			&offscreenGWorld, k32BGRAPixelFormat,
			&rect, 0, 0, 0, ras->getRawData(), ras->getWrap() * 4);

		if (err != noErr) {
			m_IOError = QTUnableToCreateResource;
			DisposeGWorld(offscreenGWorld);
			goto error;
		}

		SetMovieBox(m_movie, &rect);
		err = GetMoviesError();
		if (err != noErr) {
			m_IOError = QTUnableToSetMovieBox;
			DisposeGWorld(offscreenGWorld);
			goto error;
		}

		SetMovieGWorld(m_movie, offscreenGWorld, GetGWorldDevice(offscreenGWorld));
		err = GetMoviesError();
		if (err != noErr) {
			m_IOError = QTUnableToSetMovieGWorld;
			DisposeGWorld(offscreenGWorld);
			goto error;
		}

		TimeValue currentTime = currentTimes[frameIndex];

		SetMovieTimeValue(m_movie, currentTime);

		err = GetMoviesError();
		if (err != noErr) {
			m_IOError = QTUnableToSetTimeValue;
			DisposeGWorld(offscreenGWorld);
			goto error;
		}

		err = UpdateMovie(m_movie);
		if (err != noErr) {
			m_IOError = QTUnableToUpdateMovie;
			DisposeGWorld(offscreenGWorld);
			goto error;
		}

		MoviesTask(m_movie, 0);
		err = GetMoviesError();
		if (err != noErr) {
			m_IOError = QTUnableToDoMovieTask;
			DisposeGWorld(offscreenGWorld);
			goto error;
		}

		DisposeGWorld(offscreenGWorld);
		ras->unlock();
	}

	if (m_depth != 32) {
		setMatteAndYMirror(rasP);
	} else {
		rasP->yMirror();
	}

	return;

error:
	rasP->unlock();
	throw TImageException(m_path, buildQTErrorString(m_IOError));
}

//------------------------------------------------

TImageReaderP TLevelReader3gp::getFrameReader(TFrameId fid)
{
	if (m_IOError != QTNoError)
		throw TImageException(m_path, buildQTErrorString(m_IOError));

	if (fid.getLetter() != 0)
		return TImageReaderP(0);
	int index = fid.getNumber() - 1;

	TImageReader3gp *irm = new TImageReader3gp(m_path, index, this);
	return TImageReaderP(irm);
}

#endif
