/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Cyril Concolato 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>

/* ProtectionInfo Box */
Atom *sinf_New()
{
	ProtectionInfoAtom *tmp = (ProtectionInfoAtom *) malloc(sizeof(ProtectionInfoAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(ProtectionInfoAtom));
	tmp->type = ProtectionInfoAtomType;
	return (Atom *)tmp;
}

void sinf_del(Atom *s)
{
	ProtectionInfoAtom *ptr = (ProtectionInfoAtom *)s;
	if (ptr == NULL) return;
	if (ptr->original_format) DelAtom((Atom *)ptr->original_format);
	if (ptr->info) DelAtom((Atom *)ptr->info);
	if (ptr->scheme_type) DelAtom((Atom *)ptr->scheme_type);
	free(ptr);
}

M4Err sinf_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	Atom *a;
	u64 sub_read;
	ProtectionInfoAtom *ptr = (ProtectionInfoAtom *)s;
	if (ptr == NULL) return M4BadParam;

	while (*read < ptr->size) {
		e = ParseAtom(&a, bs, &sub_read);
		if (e) return e;
		*read += a->size;

		switch (a->type) {
		case OriginalFormatAtomType: ptr->original_format = (OriginalFormatAtom*)a; break;
		case SchemeTypeAtomType: ptr->scheme_type = (SchemeTypeAtom*)a; break;
		case SchemeInformationAtomType: ptr->info = (SchemeInformationAtom*)a; break;
		default: DelAtom(a); break;
		}
	}
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err sinf_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	ProtectionInfoAtom *ptr = (ProtectionInfoAtom *)s;
	if (!s) return M4BadParam;
	e = Atom_Write(s, bs);
	if (e) return e;
	//frma
	e = WriteAtom((Atom *) ptr->original_format, bs);
	if (e) return e;
	// schm
	e = WriteAtom((Atom *) ptr->scheme_type, bs);
	if (e) return e;
	// schi
	e = WriteAtom((Atom *) ptr->info, bs);
	if (e) return e;
	return M4OK;
}

M4Err sinf_Size(Atom *s)
{
	M4Err e;
	ProtectionInfoAtom *ptr = (ProtectionInfoAtom *)s;
	if (!s) return M4BadParam;
	e = Atom_Size(s);
	if (e) return e;
	e = SizeAtom((Atom *) ptr->original_format);
	if (e) return e;
	ptr->size += ptr->original_format->size;
	e = SizeAtom((Atom *) ptr->scheme_type);
	if (e) return e;
	ptr->size += ptr->scheme_type->size;
	e = SizeAtom((Atom *) ptr->info);
	if (e) return e;
	ptr->size += ptr->info->size;
	return M4OK;
}
#endif //M4_READ_ONLY

/* OriginalFormat Box */
Atom *frma_New()
{
	OriginalFormatAtom *tmp = (OriginalFormatAtom *) malloc(sizeof(OriginalFormatAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(OriginalFormatAtom));
	tmp->type = OriginalFormatAtomType;
	return (Atom *)tmp;
}

void frma_del(Atom *s)
{
	OriginalFormatAtom *ptr = (OriginalFormatAtom *)s;
	if (ptr == NULL) return;
	free(ptr);
}

M4Err frma_Read(Atom *s, BitStream *bs, u64 *read)
{
	OriginalFormatAtom *ptr = (OriginalFormatAtom *)s;
	if (ptr == NULL) return M4BadParam;	
	ptr->data_format = BS_ReadU32(bs);
	*read += 4;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err frma_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	OriginalFormatAtom *ptr = (OriginalFormatAtom *)s;
	if (!s) return M4BadParam;
	e = Atom_Write(s, bs);
	if (e) return e;
	BS_WriteU32(bs, ptr->data_format);
	return M4OK;
}

M4Err frma_Size(Atom *s)
{
	M4Err e;
	OriginalFormatAtom *ptr = (OriginalFormatAtom *)s;
	if (!s) return M4BadParam;
	e = Atom_Size(s);
	if (e) return e;
	ptr->size += 4;
	return M4OK;
}
#endif //M4_READ_ONLY

/* SchemeType Box */
Atom *schm_New()
{
	SchemeTypeAtom *tmp = (SchemeTypeAtom *) malloc(sizeof(SchemeTypeAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(SchemeTypeAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = SchemeTypeAtomType;
	return (Atom *)tmp;
}

void schm_del(Atom *s)
{
	SchemeTypeAtom *ptr = (SchemeTypeAtom *)s;
	if (ptr == NULL) return;
	if (ptr->URI) free(ptr->URI);
	free(ptr);
}

M4Err schm_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	SchemeTypeAtom *ptr = (SchemeTypeAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);
	ptr->scheme_type = BS_ReadU32(bs);
	*read += 4;
	ptr->scheme_version = BS_ReadU32(bs);
	*read += 4;
	if (ptr->flags & 0x000001) {
		u32 len;
		if (ptr->size < *read) return M4InvalidAtom;
		len = (u32) (ptr->size - *read);
		ptr->URI = (char*)malloc(sizeof(char)*len);
		if (!ptr->URI) return M4OutOfMem;
		BS_ReadData(bs, (unsigned char*)ptr->URI, len);
		*read += len;
	}
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err schm_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	SchemeTypeAtom *ptr = (SchemeTypeAtom *) s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	BS_WriteU32(bs, ptr->scheme_type);
	BS_WriteU32(bs, ptr->scheme_version);
	if (ptr->flags & 0x000001) BS_WriteData(bs, ptr->URI, strlen(ptr->URI)+1);
	return M4OK;
}

M4Err schm_Size(Atom *s)
{
	M4Err e;
	SchemeTypeAtom *ptr = (SchemeTypeAtom *) s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += 8;
	if (ptr->flags & 0x000001) ptr->size += strlen(ptr->URI)+1;
	return M4OK;
}
#endif //M4_READ_ONLY

/* SchemeInformation Box */
Atom *schi_New()
{
	SchemeInformationAtom *tmp = (SchemeInformationAtom *) malloc(sizeof(SchemeInformationAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(SchemeInformationAtom));
	tmp->type = SchemeInformationAtomType;
	tmp->atomList= NewChain();
	return (Atom *)tmp;
}

void schi_del(Atom *s)
{
	SchemeInformationAtom *ptr = (SchemeInformationAtom *)s;
	if (ptr == NULL) return;
	if (ptr->ikms) DelAtom((Atom *)ptr->ikms);
	if (ptr->isfm) DelAtom((Atom *)ptr->isfm);
	while (ChainGetCount(ptr->atomList)) {
		Atom *a = ChainGetEntry(ptr->atomList, 0);
		ChainDeleteEntry(ptr->atomList, 0);
		DelAtom(a);
	}
	DeleteChain(ptr->atomList);
	free(ptr);
}

M4Err schi_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	u64 sub_read;
	Atom *a;
	SchemeInformationAtom *ptr = (SchemeInformationAtom *)s;
	if (ptr == NULL) return M4BadParam;
	while (*read < ptr->size) {
		e = ParseAtom(&a, bs, &sub_read);
		if (e) return e;
		*read += a->size;
		switch (a->type) {
		case ISMAKMSAtomType:
			if (ptr->ikms) DelAtom((Atom *)ptr->ikms);
			ptr->ikms = (ISMAKMSAtom*)a;
			break;
		case ISMASampleFormatAtomType:
			if (ptr->isfm) DelAtom((Atom *)ptr->isfm);
			ptr->isfm = (ISMASampleFormatAtom*)a;
			break;
		default:
			ChainAddEntry(ptr->atomList, a);
			break;
		}
	}	
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

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

	if (ptr->ikms) {
		e = WriteAtom((Atom *) ptr->ikms, bs);
		if (e) return e;
	}
	if (ptr->isfm) {
		e = WriteAtom((Atom *) ptr->isfm, bs);
		if (e) return e;
	}
	return WriteAtomList(s, ptr->atomList, bs);
}

M4Err schi_Size(Atom *s)
{
	M4Err e;
	SchemeInformationAtom *ptr = (SchemeInformationAtom *)s;
	if (!s) return M4BadParam;
	e = Atom_Size(s);
	if (e) return e;

	if (ptr->ikms) {
		e = SizeAtom((Atom *) ptr->ikms);
		if (e) return e;
		ptr->size += ptr->ikms->size;
	}
	if (ptr->isfm) {
		e = SizeAtom((Atom *) ptr->isfm);
		if (e) return e;
		ptr->size += ptr->isfm->size;
	}
	return SizeAtomList(s, ptr->atomList);
}

#endif //M4_READ_ONLY

/* ISMAKMS Box */
Atom *iKMS_New()
{
	ISMAKMSAtom *tmp = (ISMAKMSAtom *) malloc(sizeof(ISMAKMSAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(ISMAKMSAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = ISMAKMSAtomType;
	return (Atom *)tmp;
}

void iKMS_del(Atom *s)
{
	ISMAKMSAtom *ptr = (ISMAKMSAtom *)s;
	if (ptr == NULL) return;
	if (ptr->URI) free(ptr->URI);
	free(ptr);
}

M4Err iKMS_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	u32 len;
	ISMAKMSAtom *ptr = (ISMAKMSAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);

	if (*read > ptr->size) return M4InvalidAtom;
	len = (u32) (ptr->size - *read);

	ptr->URI = (char*) malloc(sizeof(char)*len);
	if (!ptr->URI) return M4OutOfMem;
	BS_ReadData(bs, (unsigned char*)ptr->URI, len);
	*read += len;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err iKMS_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	ISMAKMSAtom *ptr = (ISMAKMSAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	BS_WriteData(bs, ptr->URI, strlen(ptr->URI)+1);
	return M4OK;
}

M4Err iKMS_Size(Atom *s)
{
	M4Err e;
	ISMAKMSAtom *ptr = (ISMAKMSAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += strlen(ptr->URI)+1;
	return M4OK;
}
#endif //M4_READ_ONLY

/* ISMASampleFormat Box */
Atom *iSFM_New()
{
	ISMASampleFormatAtom *tmp = (ISMASampleFormatAtom *) malloc(sizeof(ISMASampleFormatAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(ISMASampleFormatAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = ISMASampleFormatAtomType;
	return (Atom *)tmp;
}

void iSFM_del(Atom *s)
{
	ISMASampleFormatAtom *ptr = (ISMASampleFormatAtom *)s;
	if (ptr == NULL) return;
	free(ptr);
}


M4Err iSFM_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	ISMASampleFormatAtom *ptr = (ISMASampleFormatAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);
	ptr->selective_encryption = BS_ReadInt(bs, 1);
	BS_ReadInt(bs, 7);
	*read += 1;
	ptr->key_indicator_length = BS_ReadU8(bs);
	*read += 1;
	ptr->IV_length = BS_ReadU8(bs);
	*read += 1;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err iSFM_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	ISMASampleFormatAtom *ptr = (ISMASampleFormatAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	BS_WriteInt(bs, ptr->selective_encryption, 1);
	BS_WriteInt(bs, 0, 7);
	BS_WriteU8(bs, ptr->key_indicator_length);
	BS_WriteU8(bs, ptr->IV_length);
	return M4OK;
}

M4Err iSFM_Size(Atom *s)
{
	M4Err e;
	ISMASampleFormatAtom *ptr = (ISMASampleFormatAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += 3;
	return M4OK;
}
#endif //M4_READ_ONLY
