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

Atom *meta_New()
{
	MetaAtom *tmp = (MetaAtom *) malloc(sizeof(MetaAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(MetaAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = MetaAtomType;
	tmp->other_boxes = NewChain();
	return (Atom *)tmp;
}

void meta_del(Atom *s)
{
	u32 count, i;
	MetaAtom *ptr = (MetaAtom *)s;
	if (ptr == NULL) return;
	DelAtom((Atom *)ptr->handler);
	if (ptr->primary_resource) DelAtom((Atom *)ptr->primary_resource);
	if (ptr->file_locations) DelAtom((Atom *)ptr->file_locations);
	if (ptr->item_locations) DelAtom((Atom *)ptr->item_locations);
	if (ptr->protections) DelAtom((Atom *)ptr->protections);
	if (ptr->item_infos) DelAtom((Atom *)ptr->item_infos);
	if (ptr->IPMP_control) DelAtom((Atom *)ptr->IPMP_control);
	count = ChainGetCount(ptr->other_boxes);
	for (i = 0; i < count; i++) {
		Atom *a = ChainGetEntry(ptr->other_boxes, i);
		DelAtom(a);
	}
	DeleteChain(ptr->other_boxes);
	free(ptr);
}

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

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

		switch (a->type) {
		case PrimaryItemAtomType: ptr->primary_resource = (PrimaryItemAtom*)a; break;
		case DataInformationAtomType: ptr->file_locations = (DataInformationAtom*)a; break;
		case ItemLocationAtomType: ptr->item_locations = (ItemLocationAtom*)a; break;
		case ItemProtectionAtomType: ptr->protections = (ItemProtectionAtom*)a; break;
		case ItemInfoAtomType: ptr->item_infos = (ItemInfoAtom*)a; break;
		//case ???: ptr->IPMP_control = (???*)a; break;
		case XMLAtomType: 
		case BinaryXMLAtomType: 
			ChainAddEntry(ptr->other_boxes, a); break;
		default: DelAtom(a); break;
		}
	}
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err meta_Write(Atom *s, BitStream *bs)
{
	u32 count, i;
	M4Err e;
	MetaAtom *ptr = (MetaAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	e = WriteAtom((Atom *) ptr->handler, bs);
	if (e) return e;
	if (ptr->primary_resource) {
		e = WriteAtom((Atom *) ptr->primary_resource, bs);
		if (e) return e;
	}
	if (ptr->file_locations) {
		e = WriteAtom((Atom *) ptr->file_locations, bs);
		if (e) return e;
	}
	if (ptr->item_locations) {
		e = WriteAtom((Atom *) ptr->item_locations, bs);
		if (e) return e;
	}
	if (ptr->protections) {
		e = WriteAtom((Atom *) ptr->protections, bs);
		if (e) return e;
	}
	if (ptr->item_infos) {
		e = WriteAtom((Atom *) ptr->item_infos, bs);
		if (e) return e;
	}
	if (ptr->IPMP_control) {
		e = WriteAtom((Atom *) ptr->IPMP_control, bs);
		if (e) return e;
	}
	if ((count = ChainGetCount(ptr->other_boxes))) {
		for (i = 0; i < count; i++) {
			Atom *a = ChainGetEntry(ptr->other_boxes, i);
			e = WriteAtom(a, bs);
			if (e) return e;
		}
	}
	return M4OK;
}

M4Err meta_Size(Atom *s)
{
	u32 i, count;
	M4Err e;
	MetaAtom *ptr = (MetaAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	e = SizeAtom((Atom *) ptr->handler);
	if (e) return e;
	ptr->size += ptr->handler->size;
	if (ptr->primary_resource) {
		e = SizeAtom((Atom *) ptr->primary_resource);
		if (e) return e;
		ptr->size += ptr->primary_resource->size;
	}
	if (ptr->file_locations) {
		e = SizeAtom((Atom *) ptr->file_locations);
		if (e) return e;
		ptr->size += ptr->file_locations->size;
	}
	if (ptr->item_locations) {
		e = SizeAtom((Atom *) ptr->item_locations);
		if (e) return e;
		ptr->size += ptr->item_locations->size;
	}
	if (ptr->protections) {
		e = SizeAtom((Atom *) ptr->protections);
		if (e) return e;
		ptr->size += ptr->protections->size;
	}
	if (ptr->item_infos) {
		e = SizeAtom((Atom *) ptr->item_infos);
		if (e) return e;
		ptr->size += ptr->item_infos->size;
	}
	if (ptr->IPMP_control) {
		e = SizeAtom((Atom *) ptr->IPMP_control);
		if (e) return e;
		ptr->size += ptr->IPMP_control->size;
	}
	if ((count = ChainGetCount(ptr->other_boxes))) {
		for (i = 0; i < count; i++) {
			Atom *a = ChainGetEntry(ptr->other_boxes, i);
			e = SizeAtom(a);
			if (e) return e;
			ptr->size += a->size;
		}
	}
	return M4OK;
}
#endif

Atom *xml_New()
{
	XMLAtom *tmp = (XMLAtom *) malloc(sizeof(XMLAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(XMLAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = XMLAtomType;
	return (Atom *)tmp;
}

void xml_del(Atom *s)
{
	XMLAtom *ptr = (XMLAtom *)s;
	if (ptr == NULL) return;
	if (ptr->xml_length && ptr->xml) free(ptr->xml);
	free(ptr);
}

M4Err xml_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	XMLAtom *ptr = (XMLAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);

	ptr->xml_length = (u32)(ptr->size - *read);
	ptr->xml = malloc(ptr->xml_length);
	if (!ptr->xml) return M4OutOfMem;

	*read += BS_ReadData(bs, ptr->xml, ptr->xml_length);
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err xml_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	XMLAtom *ptr = (XMLAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	if (ptr->xml_length) BS_WriteData(bs, ptr->xml, ptr->xml_length);
	return M4OK;
}

M4Err xml_Size(Atom *s)
{
	M4Err e;
	XMLAtom *ptr = (XMLAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += ptr->xml_length;
	return M4OK;
}
#endif

Atom *bxml_New()
{
	BinaryXMLAtom *tmp = (BinaryXMLAtom *) malloc(sizeof(BinaryXMLAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(BinaryXMLAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = BinaryXMLAtomType;
	return (Atom *)tmp;
}

void bxml_del(Atom *s)
{
	BinaryXMLAtom *ptr = (BinaryXMLAtom *)s;
	if (ptr == NULL) return;
	if (ptr->data_length && ptr->data) free(ptr->data);
	free(ptr);
}

M4Err bxml_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	BinaryXMLAtom *ptr = (BinaryXMLAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);

	ptr->data_length = (u32)(ptr->size - *read);
	ptr->data = malloc(ptr->data_length);
	if (!ptr->data) return M4OutOfMem;

	*read += BS_ReadData(bs, ptr->data, ptr->data_length);
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err bxml_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	BinaryXMLAtom *ptr = (BinaryXMLAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	if (ptr->data_length) BS_WriteData(bs, ptr->data, ptr->data_length);
	return M4OK;
}

M4Err bxml_Size(Atom *s)
{
	M4Err e;
	BinaryXMLAtom *ptr = (BinaryXMLAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += ptr->data_length;
	return M4OK;
}
#endif

Atom *iloc_New()
{
	ItemLocationAtom *tmp = (ItemLocationAtom *) malloc(sizeof(ItemLocationAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(ItemLocationAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = ItemLocationAtomType;
	tmp->location_entries = NewChain();
	return (Atom *)tmp;
}

void iloc_del(Atom *s)
{
	u32 i, j, item_count, extent_count;
	ItemLocationAtom *ptr = (ItemLocationAtom *)s;
	if (ptr == NULL) return;
	item_count = ChainGetCount(ptr->location_entries);
	if (item_count) {
		for (i = 0; i < item_count; i++) {
			ItemLocationEntry *location = (ItemLocationEntry *)ChainGetEntry(ptr->location_entries, i);
			extent_count = ChainGetCount(location->extent_entries);
			for (j = 0; j < extent_count; j++) {
				ItemExtentEntry *extent = (ItemExtentEntry *)ChainGetEntry(location->extent_entries, j);
				free(extent);
			}
			DeleteChain(location->extent_entries);
			free(location);
		}
		DeleteChain(ptr->location_entries);
	}
	free(ptr);
}

M4Err iloc_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	u32 item_count, extent_count, i, j;
	ItemLocationAtom *ptr = (ItemLocationAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);

	ptr->offset_size = BS_ReadInt(bs, 4);
	ptr->length_size = BS_ReadInt(bs, 4);
	ptr->base_offset_size = BS_ReadInt(bs, 4);
	BS_ReadInt(bs, 4);
	item_count = BS_ReadU16(bs);
	*read += 4;
	for (i = 0; i < item_count; i++) {
		ItemLocationEntry *location_entry = malloc(sizeof(ItemLocationEntry));
		ChainAddEntry(ptr->location_entries, location_entry);
		location_entry->item_ID = BS_ReadU16(bs);
		location_entry->data_reference_index = BS_ReadU16(bs);
		location_entry->base_offset = BS_ReadInt(bs, 8*ptr->base_offset_size);
#ifndef M4_READ_ONLY
		location_entry->original_base_offset = location_entry->base_offset;
#endif

		extent_count = BS_ReadU16(bs);
		location_entry->extent_entries = NewChain();
		*read += 6 + ptr->base_offset_size;
		for (j = 0; j < extent_count; j++) {
			ItemExtentEntry *extent_entry = malloc(sizeof(ItemExtentEntry));
			ChainAddEntry(location_entry->extent_entries, extent_entry);
			extent_entry->extent_offset = BS_ReadInt(bs, 8*ptr->offset_size);
			extent_entry->extent_length = BS_ReadInt(bs, 8*ptr->length_size);
			*read += ptr->offset_size + ptr->length_size;
#ifndef M4_READ_ONLY
			extent_entry->original_extent_offset = extent_entry->extent_offset;
#endif
		}
	}
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err iloc_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	u32 i, j, item_count, extent_count;
	ItemLocationAtom *ptr = (ItemLocationAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	BS_WriteInt(bs, ptr->offset_size, 4);
	BS_WriteInt(bs, ptr->length_size, 4);
	BS_WriteInt(bs, ptr->base_offset_size, 4);
	BS_WriteInt(bs, 0, 4);
	item_count = ChainGetCount(ptr->location_entries);
	BS_WriteU16(bs, item_count);
	for (i = 0; i < item_count; i++) {
		ItemLocationEntry *location = (ItemLocationEntry *)ChainGetEntry(ptr->location_entries, i);
		BS_WriteU16(bs, location->item_ID);
		BS_WriteU16(bs, location->data_reference_index);
		BS_WriteLongInt(bs, location->base_offset, 8*ptr->base_offset_size);
		extent_count = ChainGetCount(location->extent_entries);
		BS_WriteU16(bs, extent_count);
		for (j=0; j<extent_count; j++) {
			ItemExtentEntry *extent = (ItemExtentEntry *)ChainGetEntry(location->extent_entries, j);
			BS_WriteLongInt(bs, extent->extent_offset, 8*ptr->offset_size);
			BS_WriteLongInt(bs, extent->extent_length, 8*ptr->length_size);
		}
	}
	return M4OK;
}

M4Err iloc_Size(Atom *s)
{
	M4Err e;
	u32 i, item_count, extent_count;
	ItemLocationAtom *ptr = (ItemLocationAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += 4;
	item_count = ChainGetCount(ptr->location_entries);
	for (i = 0; i < item_count; i++) {
		ItemLocationEntry *location = (ItemLocationEntry *)ChainGetEntry(ptr->location_entries, i);
		extent_count = ChainGetCount(location->extent_entries);
		ptr->size += 6 + ptr->base_offset_size + extent_count*(ptr->offset_size + ptr->length_size);
	}
	return M4OK;
}
#endif

Atom *pitm_New()
{
	PrimaryItemAtom *tmp = (PrimaryItemAtom *) malloc(sizeof(PrimaryItemAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(PrimaryItemAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = PrimaryItemAtomType;
	return (Atom *)tmp;
}

void pitm_del(Atom *s)
{
	PrimaryItemAtom *ptr = (PrimaryItemAtom *)s;
	if (ptr == NULL) return;
	free(ptr);
}

M4Err pitm_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	PrimaryItemAtom *ptr = (PrimaryItemAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);
	
	ptr->item_ID = BS_ReadU16(bs);
	*read += 2;

	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err pitm_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	PrimaryItemAtom *ptr = (PrimaryItemAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	BS_WriteU16(bs, ptr->item_ID);
	return M4OK;
}

M4Err pitm_Size(Atom *s)
{
	M4Err e;
	ItemLocationAtom *ptr = (ItemLocationAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += 16;
	return M4OK;
}
#endif

Atom *ipro_New()
{
	ItemProtectionAtom *tmp = (ItemProtectionAtom *) malloc(sizeof(ItemProtectionAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(ItemProtectionAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = ItemProtectionAtomType;
	tmp->protection_information = NewChain();
	return (Atom *)tmp;
}

void ipro_del(Atom *s)
{
	u32 count, i;
	ItemProtectionAtom *ptr = (ItemProtectionAtom *)s;
	if (ptr == NULL) return;
	count = ChainGetCount(ptr->protection_information);
	for (i = 0; i < count; i++) {
		Atom *a = ChainGetEntry(ptr->protection_information, i);
		DelAtom(a);
	}
	DeleteChain(ptr->protection_information);
	free(ptr);
}

M4Err ipro_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	Atom *a;
	u64 sub_read;
	ItemProtectionAtom *ptr = (ItemProtectionAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);
	
	while (*read < ptr->size) {
		e = ParseAtom(&a, bs, &sub_read);
		if (e) return e;
		*read += a->size;

		if (a->type == ProtectionInfoAtomType) 
			ChainAddEntry(ptr->protection_information, a);
		else 
			DelAtom(a);
	}

	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err ipro_Write(Atom *s, BitStream *bs)
{
	u32 count, i;
	M4Err e;
	ItemProtectionAtom *ptr = (ItemProtectionAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	count = ChainGetCount(ptr->protection_information);
	BS_WriteU16(bs, count);
	if (count) {
		for (i = 0; i < count; i++) {
			Atom *a = ChainGetEntry(ptr->protection_information, i);
			e = WriteAtom(a, bs);
			if (e) return e;
		}
	}
	return M4OK;
}

M4Err ipro_Size(Atom *s)
{
	u32 i, count;
	M4Err e;
	ItemProtectionAtom *ptr = (ItemProtectionAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += 2;
	if ((count = ChainGetCount(ptr->protection_information))) {
		for (i = 0; i < count; i++) {
			Atom *a = ChainGetEntry(ptr->protection_information, i);
			e = SizeAtom(a);
			if (e) return e;
			ptr->size += a->size;
		}
	}
	return M4OK;
}
#endif

Atom *infe_New()
{
	ItemInfoEntryAtom *tmp = (ItemInfoEntryAtom *) malloc(sizeof(ItemInfoEntryAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(ItemInfoEntryAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = ItemInfoEntryType;
	return (Atom *)tmp;
}

void infe_del(Atom *s)
{
	ItemInfoEntryAtom *ptr = (ItemInfoEntryAtom *)s;
	if (ptr == NULL) return;
	if (ptr->item_name) free(ptr->item_name);
	if (ptr->full_path) free(ptr->full_path);
	if (ptr->content_type) free(ptr->content_type);
	if (ptr->content_encoding) free(ptr->content_encoding);
	free(ptr);
}

M4Err infe_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	u8 *buf;
	u32 buf_len, i, string_len, string_start;
	ItemInfoEntryAtom *ptr = (ItemInfoEntryAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);
	
	ptr->item_ID = BS_ReadU16(bs);
	ptr->item_protection_index = BS_ReadU16(bs);
	*read += 4;

	buf_len = (u32) (ptr->size - *read);
	buf = malloc(buf_len);
	if (buf_len != BS_ReadData(bs, buf, buf_len)) {
		free(buf);
		return M4ReadAtomFailed;
	} 
	*read += buf_len;
	string_len = 1;
	string_start = 0;
	for (i = 0; i < buf_len; i++) {
		if (buf[i] == 0) {
			if (!ptr->item_name) {
				ptr->item_name = malloc(string_len);
				memcpy(ptr->item_name, buf+string_start, string_len);
			} else if (!ptr->content_type) {
				ptr->content_type = malloc(string_len);
				memcpy(ptr->content_type, buf+string_start, string_len);
			} else {
				ptr->content_encoding = malloc(string_len);
				memcpy(ptr->content_encoding, buf+string_start, string_len);
			}
			string_start += string_len;
			string_len = 0;
		}
		string_len++;
	}
	free(buf);
	if (!ptr->item_name || !ptr->content_type) return M4ReadAtomFailed;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err infe_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	u32 len;
	ItemInfoEntryAtom *ptr = (ItemInfoEntryAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	BS_WriteU16(bs, ptr->item_ID);
	BS_WriteU16(bs, ptr->item_protection_index);
	if (ptr->item_name) {
		len = strlen(ptr->item_name)+1;
		BS_WriteData(bs, ptr->item_name, len);
	}
	if (ptr->content_type) {
		len = strlen(ptr->content_type)+1;
		BS_WriteData(bs, ptr->content_type, len);
	}
	if (ptr->content_encoding) {
		len = strlen(ptr->content_encoding)+1;
		BS_WriteData(bs, ptr->content_encoding, len);
	}
	return M4OK;
}

M4Err infe_Size(Atom *s)
{
	M4Err e;
	ItemInfoEntryAtom *ptr = (ItemInfoEntryAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += 4;
	if (ptr->item_name) ptr->size += strlen(ptr->item_name)+1;
	if (ptr->content_type) ptr->size += strlen(ptr->content_type)+1;
	if (ptr->content_encoding) ptr->size += strlen(ptr->content_encoding)+1;
	return M4OK;
}
#endif

Atom *iinf_New()
{
	ItemInfoAtom *tmp = (ItemInfoAtom *) malloc(sizeof(ItemInfoAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(ItemInfoAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = ItemInfoAtomType;
	tmp->item_infos = NewChain();
	return (Atom *)tmp;
}

void iinf_del(Atom *s)
{
	u32 count, i;
	ItemInfoAtom *ptr = (ItemInfoAtom *)s;
	if (ptr == NULL) return;
	count = ChainGetCount(ptr->item_infos);
	for (i = 0; i < count; i++) {
		Atom *a = ChainGetEntry(ptr->item_infos, i);
		DelAtom(a);
	}
	DeleteChain(ptr->item_infos);
	free(ptr);
}

M4Err iinf_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	Atom *a;
	u32 count;
	u64 sub_read;
	ItemInfoAtom *ptr = (ItemInfoAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);
	count = BS_ReadU16(bs);
	*read += 2;
	while (*read < ptr->size) {
		e = ParseAtom(&a, bs, &sub_read);
		if (e) return e;
		*read += a->size;
		if (a->type == ItemInfoEntryType)
			ChainAddEntry(ptr->item_infos, a); 
		else 
			DelAtom(a);
	}
	if (count != ChainGetCount(ptr->item_infos)) return M4ReadAtomFailed;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err iinf_Write(Atom *s, BitStream *bs)
{
	u32 count, i;
	M4Err e;
	ItemInfoAtom *ptr = (ItemInfoAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	count = ChainGetCount(ptr->item_infos);
	BS_WriteU16(bs, count);
	if (count) {
		for (i = 0; i < count; i++) {
			Atom *a = ChainGetEntry(ptr->item_infos, i);
			e = WriteAtom(a, bs);
			if (e) return e;
		}
	}
	return M4OK;
}

M4Err iinf_Size(Atom *s)
{
	u32 i, count;
	M4Err e;
	ItemInfoAtom *ptr = (ItemInfoAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += 2;
	if ((count = ChainGetCount(ptr->item_infos))) {
		for (i = 0; i < count; i++) {
			Atom *a = ChainGetEntry(ptr->item_infos, i);
			e = SizeAtom(a);
			if (e) return e;
			ptr->size += a->size;
		}
	}
	return M4OK;
}
#endif



Atom *imif_New()
{
	IPMPInfoAtom *tmp = (IPMPInfoAtom*) malloc(sizeof(IPMPInfoAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(IPMPInfoAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = IPMPInfoAtomType;
	tmp->descriptors = NewChain();
	return (Atom *)tmp;
}

void imif_del(Atom *s)
{
	IPMPInfoAtom *ptr = (IPMPInfoAtom*)s;
	if (ptr == NULL) return;
	OD_DeleteDescList(ptr->descriptors);
	DeleteChain(ptr->descriptors);
	free(ptr);
}

M4Err imif_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	u32 size;
	char *data;
	IPMPInfoAtom *ptr = (IPMPInfoAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);

	size = (u32) (s->size - *read);
	data = malloc(sizeof(char)*size);
	e = OD_ReadDescList(data, size, ptr->descriptors);
	free(data);
	return e;
}

#ifndef M4_READ_ONLY
M4Err imif_Write(Atom *s, BitStream *bs)
{
	u32 dlen;
	char *data;
	M4Err e;
	IPMPInfoAtom *ptr = (IPMPInfoAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	e = OD_EncDescList(ptr->descriptors, &data, &dlen);
	if (e) return e;
	BS_WriteData(bs, data, dlen);
	free(data);
	return M4OK;
}

M4Err imif_Size(Atom *s)
{
	u32 size;
	M4Err e;
	IPMPInfoAtom *ptr = (IPMPInfoAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	e = OD_SizeDescList(ptr->descriptors, &size);
	if (e) return e;
	ptr->size += size;
	return M4OK;
}
#endif


Atom *ipmc_New()
{
	IPMPControlAtom *tmp = (IPMPControlAtom*) malloc(sizeof(IPMPControlAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(IPMPControlAtom));
	InitFullAtom((Atom *)tmp);
	tmp->type = IPMPControlAtomType;
	tmp->descriptors = NewChain();
	return (Atom *)tmp;
}

void ipmc_del(Atom *s)
{
	IPMPControlAtom *ptr = (IPMPControlAtom*)s;
	if (ptr == NULL) return;
	OD_DeleteDescriptor((Descriptor **)&ptr->ipmp_tools);
	OD_DeleteDescList(ptr->descriptors);
	DeleteChain(ptr->descriptors);
	free(ptr);
}

M4Err ipmc_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	u32 size, i, count;
	Descriptor *desc;
	M4Err ParseDescriptor(BitStream *bs, Descriptor **desc, u32 *desc_size);
	s32 GetSizeFieldSize(u32 size_desc);

	IPMPControlAtom *ptr = (IPMPControlAtom *)s;
	if (ptr == NULL) return M4BadParam;
	e = FullAtom_Read(s, bs, read);

	e =	ParseDescriptor(bs, (Descriptor **)&ptr->ipmp_tools, &size); 
	*read += size;
	if (e) return e;
	*read += 2;
	count = BS_ReadU8(bs);

	for (i=0; i<count; i++) {
		e =	ParseDescriptor(bs, &desc, &size); 
		if (e) break;
		ChainAddEntry(ptr->descriptors, desc);
		*read += size + GetSizeFieldSize(size);
	}
	if (*read<ptr->size) {
		BS_SkipBytes(bs, (u32) (ptr->size - *read));
	} else if (*read>ptr->size) e = M4ReadAtomFailed;
	return e;
}

#ifndef M4_READ_ONLY
M4Err ipmc_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	M4Err writeDescList(BitStream *bs, Chain *descList);
	M4Err WriteDesc(BitStream *bs, Descriptor *desc);

	IPMPControlAtom *ptr = (IPMPControlAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Write(s, bs);
	if (e) return e;
	e = WriteDesc(bs, (Descriptor *)ptr->ipmp_tools);
	if (e) return e;
	BS_WriteU8(bs, ChainGetCount(ptr->descriptors));
	return writeDescList(bs, ptr->descriptors);
}

M4Err ipmc_Size(Atom *s)
{
	u32 size;
	M4Err e;
	IPMPControlAtom *ptr = (IPMPControlAtom *)s;
	if (!s) return M4BadParam;
	e = FullAtom_Size(s);
	if (e) return e;
	ptr->size += OD_GetDescSize((Descriptor *)ptr->ipmp_tools) + 1;
	e = OD_SizeDescList(ptr->descriptors, &size);
	if (e) return e;
	ptr->size += size;
	return M4OK;
}
#endif

