/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005 
 *					All rights reserved
 *
 *  This file is part of GPAC / ISO Media File Format sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#include <gpac/intern/m4_isomedia_dev.h>


void AVC_RewriteESDescriptor(AVCSampleEntryAtom *avc)
{
	if (avc->esd) OD_DeleteDescriptor((Descriptor **)&avc->esd);
	avc->esd = OD_NewESDescriptor(2);
	avc->esd->decoderConfig->streamType = M4ST_VISUAL;
	/*AVC OTI is 0x21, AVC parameter set stream OTI (not supported in gpac) is 0x22*/
	avc->esd->decoderConfig->objectTypeIndication = 0x21;
	if (avc->bitrate) {
		avc->esd->decoderConfig->bufferSizeDB = avc->bitrate->bufferSizeDB;
		avc->esd->decoderConfig->avgBitrate = avc->bitrate->avgBitrate;
		avc->esd->decoderConfig->maxBitrate = avc->bitrate->maxBitrate;
	}
	if (avc->descr) {
		u32 i; 
		for (i=0; i<ChainGetCount(avc->descr->descriptors); i++) {
			Descriptor *clone;
			Descriptor *desc = ChainGetEntry(avc->descr->descriptors, i);
			clone = NULL;
			OD_DuplicateDescriptor(desc, &clone);
			if (OD_AddDescToDesc((Descriptor *)avc->esd, clone) != M4OK) OD_DeleteDescriptor(&clone);
		}
	}
	if (avc->avc_config) {
		AVC_WriteDSI(avc->avc_config->config, &avc->esd->decoderConfig->decoderSpecificInfo->data, &avc->esd->decoderConfig->decoderSpecificInfo->dataLength);
	}
}

M4Err AVC_UpdateESD(AVCSampleEntryAtom *avc, ESDescriptor *esd)
{
	if (!avc->bitrate) avc->bitrate = (MPEG4BitRateAtom*)CreateAtom(MPEG4BitRateAtomType);
	if (avc->descr) DelAtom((Atom *) avc->descr);
	avc->descr = NULL;
	avc->bitrate->avgBitrate = esd->decoderConfig->avgBitrate;
	avc->bitrate->maxBitrate = esd->decoderConfig->maxBitrate;
	avc->bitrate->bufferSizeDB = esd->decoderConfig->bufferSizeDB;

	if (ChainGetCount(esd->IPIDataSet)
		|| ChainGetCount(esd->IPMPDescriptorPointers)
		|| esd->langDesc
		|| ChainGetCount(esd->extensionDescriptors)
		|| esd->ipiPtr || esd->qos || esd->RegDescriptor) {

		avc->descr = (MPEG4ExtensionDescriptorsAtom *)CreateAtom(MPEG4ExtensionDescriptorsAtomType);
		if (esd->RegDescriptor) { ChainAddEntry(avc->descr->descriptors, esd->RegDescriptor); esd->RegDescriptor = NULL; }
		if (esd->qos) { ChainAddEntry(avc->descr->descriptors, esd->qos); esd->qos = NULL; }
		if (esd->ipiPtr) { ChainAddEntry(avc->descr->descriptors, esd->ipiPtr); esd->ipiPtr= NULL; }

		while (ChainGetCount(esd->IPIDataSet)) {
			Descriptor *desc = ChainGetEntry(esd->IPIDataSet, 0);
			ChainDeleteEntry(esd->IPIDataSet, 0);
			ChainAddEntry(avc->descr->descriptors, desc);
		}
		while (ChainGetCount(esd->IPMPDescriptorPointers)) {
			Descriptor *desc = ChainGetEntry(esd->IPMPDescriptorPointers, 0);
			ChainDeleteEntry(esd->IPMPDescriptorPointers, 0);
			ChainAddEntry(avc->descr->descriptors, desc);
		}
		if (esd->langDesc) {
			ChainAddEntry(avc->descr->descriptors, esd->langDesc);
			esd->langDesc = NULL;
		}
		while (ChainGetCount(esd->extensionDescriptors)) {
			Descriptor *desc = ChainGetEntry(esd->extensionDescriptors, 0);
			ChainDeleteEntry(esd->extensionDescriptors, 0);
			ChainAddEntry(avc->descr->descriptors, desc);
		}
	}

	/*update AVCConfig*/
	if (!avc->avc_config) avc->avc_config = (AVCConfigurationAtom *)CreateAtom(AVCConfigurationAtomType);
	if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) {
		if (avc->avc_config->config) AVC_DeleteConfig(avc->avc_config->config);
		avc->avc_config->config = AVC_ReadDSI(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength);
	}
	OD_DeleteDescriptor((Descriptor **)&esd);
	AVC_RewriteESDescriptor(avc);
	return M4OK;
}

static AVCConfig *AVC_DuplicateConfig(AVCConfig *cfg)
{
	u32 i, count;
	AVCConfigSlot *p1, *p2;
	AVCConfig *cfg_new = AVC_NewConfig();
	cfg_new->AVCLevelIndication = cfg->AVCLevelIndication;
	cfg_new->AVCProfileIndication = cfg->AVCProfileIndication;
	cfg_new->configurationVersion = cfg->configurationVersion;
	cfg_new->nal_unit_size = cfg->nal_unit_size;
	cfg_new->profile_compatibility = cfg->profile_compatibility;

	count = ChainGetCount(cfg->sequenceParameterSets);
	for (i=0; i<count; i++) {
		p1 = ChainGetEntry(cfg->sequenceParameterSets, i);
		p2 = malloc(sizeof(AVCConfigSlot));
		p2->size = p1->size;
		p2->data = malloc(sizeof(char)*p1->size);
		memcpy(p2->data, p1->data, sizeof(char)*p1->size);
		ChainAddEntry(cfg_new->sequenceParameterSets, p2);
	}

	count = ChainGetCount(cfg->pictureParameterSets);
	for (i=0; i<count; i++) {
		p1 = ChainGetEntry(cfg->pictureParameterSets, i);
		p2 = malloc(sizeof(AVCConfigSlot));
		p2->size = p1->size;
		p2->data = malloc(sizeof(char)*p1->size);
		memcpy(p2->data, p1->data, sizeof(char)*p1->size);
		ChainAddEntry(cfg_new->pictureParameterSets, p2);
	}
	return cfg_new;	
}

#ifndef M4_READ_ONLY
M4Err M4_AVC_NewStreamConfig(M4File *the_file, u32 trackNumber, AVCConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex)
{
	TrackAtom *trak;
	M4Err e;
	u32 dataRefIndex;
	TrackReferenceTypeAtom *dpnd;
	TrackReferenceAtom *tref;
	AVCSampleEntryAtom *entry;

	e = CanAccessMovie(the_file, M4_OPEN_WRITE);
	if (e) return e;
	
	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak || !trak->Media || !cfg) return M4BadParam;

	dpnd = NULL;
	tref = NULL;

	//get or create the data ref
	e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex);
	if (e) return e;
	if (!dataRefIndex) {
		e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex);
		if (e) return e;
	}
	trak->Media->mediaHeader->modificationTime = GetMP4Time();

	//create a new entry
	entry = (AVCSampleEntryAtom *) CreateAtom(AVCSampleEntryAtomType);
	if (!entry) return M4OutOfMem;
	entry->avc_config = (AVCConfigurationAtom*)CreateAtom(AVCConfigurationAtomType);
	entry->avc_config->config = AVC_DuplicateConfig(cfg);
	entry->dataReferenceIndex = dataRefIndex;
	e = ChainAddEntry(trak->Media->information->sampleTable->SampleDescription->atomList, entry);
	*outDescriptionIndex = ChainGetCount(trak->Media->information->sampleTable->SampleDescription->atomList);
	AVC_RewriteESDescriptor(entry);
	return e;
}

M4Err M4_AVC_UpdateStreamConfig(M4File *the_file, u32 trackNumber, u32 DescriptionIndex, AVCConfig *cfg)
{
	TrackAtom *trak;
	M4Err e;
	AVCSampleEntryAtom *entry;

	e = CanAccessMovie(the_file, M4_OPEN_WRITE);
	if (e) return e;
	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak || !trak->Media || !cfg || !DescriptionIndex) return M4BadParam;
	entry = ChainGetEntry(trak->Media->information->sampleTable->SampleDescription->atomList, DescriptionIndex-1);
	if (!entry) return M4BadParam;
	if (entry->type != AVCSampleEntryAtomType) return M4BadParam;

	if (entry->avc_config->config) AVC_DeleteConfig(entry->avc_config->config);
	entry->avc_config->config = AVC_DuplicateConfig(cfg);
	AVC_RewriteESDescriptor(entry);
	return M4OK;
}
#endif

AVCConfig *M4_AVC_GetStreamConfig(M4File *the_file, u32 trackNumber, u32 DescriptionIndex)
{
	TrackAtom *trak;
	AVCSampleEntryAtom *entry;
	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak || !trak->Media || !DescriptionIndex) return NULL;
	entry = ChainGetEntry(trak->Media->information->sampleTable->SampleDescription->atomList, DescriptionIndex-1);
	if (!entry) return NULL;
	if (entry->type != AVCSampleEntryAtomType) return NULL;

	return AVC_DuplicateConfig(entry->avc_config->config);
}

void btrt_del(Atom *s)
{
	MPEG4BitRateAtom *ptr = (MPEG4BitRateAtom *)s;
	if (ptr) free(ptr);
}
M4Err btrt_Read(Atom *s, BitStream *bs, u64 *read)
{
	MPEG4BitRateAtom *ptr = (MPEG4BitRateAtom *)s;
	ptr->bufferSizeDB = BS_ReadU32(bs);
	ptr->maxBitrate = BS_ReadU32(bs);
	ptr->avgBitrate = BS_ReadU32(bs);
	*read += 12;
	return (*read != ptr->size) ? M4ReadAtomFailed : M4OK;
}
Atom *btrt_New()
{
	MPEG4BitRateAtom *tmp = (MPEG4BitRateAtom *) malloc(sizeof(MPEG4BitRateAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(MPEG4BitRateAtom));
	tmp->type = MPEG4BitRateAtomType;
	return (Atom *)tmp;
}

#ifndef M4_READ_ONLY
M4Err btrt_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	MPEG4BitRateAtom *ptr = (MPEG4BitRateAtom *) s;
	if (!s) return M4BadParam;
	e = Atom_Write(s, bs);
	if (e) return e;
	BS_WriteU32(bs, ptr->bufferSizeDB);
	BS_WriteU32(bs, ptr->maxBitrate);
	BS_WriteU32(bs, ptr->avgBitrate);
	return M4OK;
}
M4Err btrt_Size(Atom *s)
{
	M4Err e;
	MPEG4BitRateAtom *ptr = (MPEG4BitRateAtom *)s;
	e = Atom_Size(s);
	ptr->size += 12;
	return e;
}
#endif



void m4ds_del(Atom *s)
{
	MPEG4ExtensionDescriptorsAtom *ptr = (MPEG4ExtensionDescriptorsAtom *)s;
	OD_DeleteDescList(ptr->descriptors);
	DeleteChain(ptr->descriptors);
	free(ptr);
}
M4Err m4ds_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	char *enc_od;
	MPEG4ExtensionDescriptorsAtom *ptr = (MPEG4ExtensionDescriptorsAtom *)s;
	u32 od_size = (u32) (ptr->size - *read);
	if (!od_size) return M4OK;
	enc_od = malloc(sizeof(char) * od_size);
	BS_ReadData(bs, enc_od, od_size);
	e = OD_ReadDescList(enc_od, od_size, ptr->descriptors);
	free(enc_od);
	*read = ptr->size;
	return e;
}
Atom *m4ds_New()
{
	MPEG4ExtensionDescriptorsAtom *tmp = (MPEG4ExtensionDescriptorsAtom *) malloc(sizeof(MPEG4ExtensionDescriptorsAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(MPEG4ExtensionDescriptorsAtom));
	tmp->type = MPEG4ExtensionDescriptorsAtomType;
	tmp->descriptors = NewChain();
	return (Atom *)tmp;
}

#ifndef M4_READ_ONLY
M4Err m4ds_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	char *enc_ods;
	u32 enc_od_size;
	MPEG4ExtensionDescriptorsAtom *ptr = (MPEG4ExtensionDescriptorsAtom *) s;
	if (!s) return M4BadParam;
	e = Atom_Write(s, bs);
	if (e) return e;
	enc_ods = NULL;
	enc_od_size = 0;
	e = OD_EncDescList(ptr->descriptors, &enc_ods, &enc_od_size);
	if (e) return e;
	if (enc_od_size) {
		BS_WriteData(bs, enc_ods, enc_od_size);
		free(enc_ods);
	}
	return M4OK;
}
M4Err m4ds_Size(Atom *s)
{
	M4Err e;
	u32 descSize;
	MPEG4ExtensionDescriptorsAtom *ptr = (MPEG4ExtensionDescriptorsAtom *)s;
	e = Atom_Size(s);
	if (!e) e = OD_SizeDescList(ptr->descriptors, &descSize);
	ptr->size += descSize;
	return e;
}
#endif



void avcc_del(Atom *s)
{
	AVCConfigurationAtom *ptr = (AVCConfigurationAtom *)s;
	if (ptr->config) AVC_DeleteConfig(ptr->config);
	free(ptr);
}
M4Err avcc_Read(Atom *s, BitStream *bs, u64 *read)
{
	u32 i, count;
	AVCConfigurationAtom *ptr = (AVCConfigurationAtom *)s;

	if (ptr->config) AVC_DeleteConfig(ptr->config);
	ptr->config = AVC_NewConfig();
	ptr->config->configurationVersion = BS_ReadU8(bs);
	ptr->config->AVCProfileIndication = BS_ReadU8(bs);
	ptr->config->profile_compatibility = BS_ReadU8(bs);
	ptr->config->AVCLevelIndication = BS_ReadU8(bs);
	BS_ReadInt(bs, 6);
	ptr->config->nal_unit_size = 1 + BS_ReadInt(bs, 2);
	BS_ReadInt(bs, 3);
	count = BS_ReadInt(bs, 5);
	*read += 6;

	for (i=0; i<count; i++) {
		AVCConfigSlot *sl = malloc(sizeof(AVCConfigSlot));
		sl->size = BS_ReadU16(bs);
		sl->data = malloc(sizeof(char) * sl->size);
		BS_ReadData(bs, sl->data, sl->size);
		ChainAddEntry(ptr->config->sequenceParameterSets, sl);
		*read += 2+sl->size;
	}

	count = BS_ReadU8(bs);
	*read += 1;
	for (i=0; i<count; i++) {
		AVCConfigSlot *sl = malloc(sizeof(AVCConfigSlot));
		sl->size = BS_ReadU16(bs);
		sl->data = malloc(sizeof(char) * sl->size);
		BS_ReadData(bs, sl->data, sl->size);
		ChainAddEntry(ptr->config->pictureParameterSets, sl);
		*read += 2+sl->size;
	}
	/*"Readers should be prepared to ignore unrecognised data beyond the definition of the data they understand"*/
	if (*read < ptr->size) {
		BS_SkipBytes(bs, (ptr->size - *read) );
		*read = ptr->size;
	}
	return (*read != ptr->size) ? M4ReadAtomFailed : M4OK;
}
Atom *avcc_New()
{
	AVCConfigurationAtom *tmp = (AVCConfigurationAtom *) malloc(sizeof(MPEG4BitRateAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(AVCConfigurationAtom));
	tmp->type = AVCConfigurationAtomType;
	return (Atom *)tmp;
}

#ifndef M4_READ_ONLY
M4Err avcc_Write(Atom *s, BitStream *bs)
{
	u32 i, count;
	M4Err e;
	AVCConfigurationAtom *ptr = (AVCConfigurationAtom *) s;
	if (!s) return M4BadParam;
	e = Atom_Write(s, bs);
	if (e) return e;
	if (!ptr->config) return M4OK;

	BS_WriteU8(bs, ptr->config->configurationVersion);
	BS_WriteU8(bs, ptr->config->AVCProfileIndication);
	BS_WriteU8(bs, ptr->config->profile_compatibility);
	BS_WriteU8(bs, ptr->config->AVCLevelIndication);
	BS_WriteInt(bs, 0x3F, 6);
	BS_WriteInt(bs, ptr->config->nal_unit_size - 1, 2);
	BS_WriteInt(bs, 0x7, 3);
	count = ChainGetCount(ptr->config->sequenceParameterSets);
	BS_WriteInt(bs, count, 5);
	for (i=0; i<count; i++) {
		AVCConfigSlot *sl = ChainGetEntry(ptr->config->sequenceParameterSets, i);
		BS_WriteU16(bs, sl->size);
		BS_WriteData(bs, sl->data, sl->size);
	}

	count = ChainGetCount(ptr->config->pictureParameterSets);
	BS_WriteU8(bs, count);
	for (i=0; i<count; i++) {
		AVCConfigSlot *sl = ChainGetEntry(ptr->config->pictureParameterSets, i);
		BS_WriteU16(bs, sl->size);
		BS_WriteData(bs, sl->data, sl->size);
	}
	return M4OK;
}
M4Err avcc_Size(Atom *s)
{
	M4Err e;
	u32 i, count;
	AVCConfigurationAtom *ptr = (AVCConfigurationAtom *)s;
	e = Atom_Size(s);
	if (e || !ptr->config) return e;
	ptr->size += 7;
	count = ChainGetCount(ptr->config->sequenceParameterSets);
	for (i=0; i<count; i++) ptr->size += 2 + ((AVCConfigSlot *)ChainGetEntry(ptr->config->sequenceParameterSets, i))->size;
	count = ChainGetCount(ptr->config->pictureParameterSets);
	for (i=0; i<count; i++) ptr->size += 2 + ((AVCConfigSlot *)ChainGetEntry(ptr->config->pictureParameterSets, i))->size;
	return M4OK;
}
#endif



void avc1_del(Atom *s)
{
	AVCSampleEntryAtom *ptr = (AVCSampleEntryAtom *)s;
	if (ptr == NULL) return;
	if (ptr->avc_config) DelAtom((Atom *) ptr->avc_config);
	if (ptr->bitrate) DelAtom((Atom *) ptr->bitrate);
	if (ptr->descr) DelAtom((Atom *) ptr->descr);
	/*for publishing*/
	if (ptr->slc) OD_DeleteDescriptor((Descriptor **)&ptr->slc);
	if (ptr->esd) OD_DeleteDescriptor((Descriptor **)&ptr->esd);
	free(ptr);
}

M4Err avc1_AddAtom(AVCSampleEntryAtom *ptr, Atom *a)
{
	if (!a) return M4OK;
	switch (a->type) {
	case AVCConfigurationAtomType:
		if (ptr->avc_config) return M4InvalidAtom;
		ptr->avc_config = (AVCConfigurationAtom *)a;
		break;
	case MPEG4BitRateAtomType:
		if (ptr->bitrate) return M4InvalidAtom;
		ptr->bitrate = (MPEG4BitRateAtom *)a;
		break;
	case MPEG4ExtensionDescriptorsAtomType:
		if (ptr->descr) return M4InvalidAtom;
		ptr->descr = (MPEG4ExtensionDescriptorsAtom *)a;
		break;
	default:
		DelAtom(a);
	}
	return M4OK;
}
M4Err avc1_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	u64 sr;
	Atom *a;
	AVCSampleEntryAtom *ptr = (AVCSampleEntryAtom *)s;

	ReadVideoSampleEntry((VisualSampleEntryAtom *)ptr, bs, read);
	
	while (*read < ptr->size) {
		e = ParseAtom(&a, bs, &sr);
		if (e) return e;
		*read += a->size;
		e = avc1_AddAtom(ptr, a);
		if (e) return e;
	}
	AVC_RewriteESDescriptor(ptr);
	return (*read != ptr->size) ? M4ReadAtomFailed : M4OK;
}
Atom *avc1_New()
{
	AVCSampleEntryAtom *tmp = (AVCSampleEntryAtom *) malloc(sizeof(AVCSampleEntryAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(AVCSampleEntryAtom));

	InitVideoSampleEntry((VisualSampleEntryAtom *)tmp);
	tmp->type = AVCSampleEntryAtomType;
	return (Atom *)tmp;
}

#ifndef M4_READ_ONLY
M4Err avc1_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	AVCSampleEntryAtom *ptr = (AVCSampleEntryAtom *)s;
	if (!s) return M4BadParam;
	e = Atom_Write(s, bs);
	if (e) return e;

	WriteVideoSampleEntry((VisualSampleEntryAtom *)s, bs);
	if (ptr->avc_config) {
		e = WriteAtom((Atom *) ptr->avc_config, bs);
		if (e) return e;
	}
	if (ptr->bitrate) {
		e = WriteAtom((Atom *) ptr->bitrate, bs);
		if (e) return e;
	}
	if (ptr->descr)	{
		e = WriteAtom((Atom *) ptr->descr, bs);
		if (e) return e;
	}
	return M4OK;
}

M4Err avc1_Size(Atom *s)
{
	M4Err e;
	AVCSampleEntryAtom *ptr = (AVCSampleEntryAtom *)s;
	e = Atom_Size(s);
	if (e) return e;

	SizeVideoSampleEntry((VisualSampleEntryAtom *)s);

	if (ptr->avc_config) {
		e = SizeAtom((Atom *) ptr->avc_config); 
		if (e) return e;
		ptr->size += ptr->avc_config->size;
	}
	if (ptr->bitrate) {
		e = SizeAtom((Atom *) ptr->bitrate);
		if (e) return e;
		ptr->size += ptr->bitrate->size;
	}
	if (ptr->descr) {
		e = SizeAtom((Atom *) ptr->descr);
		if (e) return e;
		ptr->size += ptr->descr->size;
	}
	return M4OK;
}
#endif 

