/*
 *			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 gppa_del(Atom *s)
{
	GP3AudioSampleEntryAtom *ptr = (GP3AudioSampleEntryAtom *)s;
	if (ptr == NULL) return;
	if (ptr->info) DelAtom((Atom *)ptr->info);
	free(ptr);
}


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

	ReadAudioSampleEntry((AudioSampleEntryAtom*)s, bs, read);

	e = ParseAtom((Atom **)&ptr->info, bs, &sub_read);
	if (e) return e;
	*read += sub_read;
	ptr->info->cfg.type = ptr->type;
	if (*read != s->size) return M4ReadAtomFailed;
	return M4OK;
}

Atom *gppa_New(u32 type)
{
	GP3AudioSampleEntryAtom *tmp;
	SAFEALLOC(tmp, sizeof(GP3AudioSampleEntryAtom));
	if (tmp == NULL) return NULL;
	InitAudioSampleEntry((AudioSampleEntryAtom*)tmp);
	tmp->type = type;
	return (Atom *)tmp;
}

//from here, for write/edit versions
#ifndef M4_READ_ONLY

M4Err gppa_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	GP3AudioSampleEntryAtom *ptr = (GP3AudioSampleEntryAtom *)s;
	e = Atom_Write(s, bs);
	if (e) return e;

	WriteAudioSampleEntry((AudioSampleEntryAtom*)s, bs);
	return WriteAtom((Atom *)ptr->info, bs);
}

M4Err gppa_Size(Atom *s)
{
	M4Err e;
	GP3AudioSampleEntryAtom *ptr = (GP3AudioSampleEntryAtom *)s;
	e = Atom_Size(s);
	if (e) return e;
	SizeAudioSampleEntry((AudioSampleEntryAtom*)s);
	e = SizeAtom((Atom *)ptr->info);
	if (e) return e;
	ptr->size += ptr->info->size;
	return M4OK;
}

#endif //M4_READ_ONLY


Atom *gppv_New(u32 type)
{
	GP3VisualSampleEntryAtom *tmp;
	SAFEALLOC(tmp, sizeof(GP3VisualSampleEntryAtom));
	if (tmp == NULL) return NULL;
	InitVideoSampleEntry((VisualSampleEntryAtom *)tmp);
	tmp->type = type;
	return (Atom *)tmp;
}
void gppv_del(Atom *s)
{
	GP3VisualSampleEntryAtom *ptr = (GP3VisualSampleEntryAtom *)s;
	if (ptr == NULL) return;
	if (ptr->info) DelAtom((Atom *)ptr->info);
	free(ptr);
}

M4Err gppv_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	u64 sub_read;
	GP3VisualSampleEntryAtom *ptr = (GP3VisualSampleEntryAtom *)s;
	if (ptr == NULL) return M4BadParam;
	ReadVideoSampleEntry((VisualSampleEntryAtom *)ptr, bs, read);
	e = ParseAtom((Atom **)&ptr->info, bs, &sub_read);
	if (e) return e;
	*read += sub_read;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

//from here, for write/edit versions
#ifndef M4_READ_ONLY

M4Err gppv_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	GP3VisualSampleEntryAtom *ptr = (GP3VisualSampleEntryAtom*)s;
	e = Atom_Write(s, bs);
	if (e) return e;
	WriteVideoSampleEntry((VisualSampleEntryAtom *)s, bs);
	e = WriteAtom((Atom *)ptr->info, bs);
	if (e) return e;
	return M4OK;
}

M4Err gppv_Size(Atom *s)
{
	M4Err e;
	GP3VisualSampleEntryAtom *ptr = (GP3VisualSampleEntryAtom*)s;
	e = Atom_Size(s);
	if (e) return e;
	SizeVideoSampleEntry((VisualSampleEntryAtom *)s);
	e = SizeAtom((Atom *)ptr->info);
	if (e) return e;
	ptr->size += ptr->info->size;
	return M4OK;
}

#endif //M4_READ_ONLY


Atom *gppc_New(u32 type)
{
	GP3ConfigAtom *tmp = (GP3ConfigAtom *) malloc(sizeof(GP3ConfigAtom));
	if (tmp == NULL) return NULL;
	memset(tmp, 0, sizeof(GP3ConfigAtom));
	tmp->type = type;
	return (Atom *)tmp;
}

void gppc_del(Atom *s)
{
	GP3ConfigAtom *ptr = (GP3ConfigAtom *)s;
	if (ptr == NULL) return;
	free(ptr);
}


M4Err gppc_Read(Atom *s, BitStream *bs, u64 *read)
{
	GP3ConfigAtom *ptr = (GP3ConfigAtom *)s;
	if (ptr == NULL) return M4BadParam;
	memset(&ptr->cfg, 0, sizeof(GPPMediaConfig));

	ptr->cfg.vendor = BS_ReadU32(bs);
	ptr->cfg.decoder_version = BS_ReadU8(bs);
	*read += 5;

	switch (ptr->type) {
	case H263ConfigAtomType:
		ptr->cfg.H263_level = BS_ReadU8(bs);
		ptr->cfg.H263_profile = BS_ReadU8(bs);
		*read += 2;
		break;
	case AMRConfigAtomType:
		ptr->cfg.AMR_mode_set = BS_ReadU16(bs);
		ptr->cfg.AMR_mode_change_period = BS_ReadU8(bs);
		ptr->cfg.frames_per_sample = BS_ReadU8(bs);
		*read += 4;
		break;
	case EVRCConfigAtomType: 
	case QCELPConfigAtomType:
	case SMVConfigAtomType:
		ptr->cfg.frames_per_sample = BS_ReadU8(bs);
		*read += 1;
		break;
	}
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

//from here, for write/edit versions
#ifndef M4_READ_ONLY

M4Err gppc_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	GP3ConfigAtom *ptr = (GP3ConfigAtom *)s;
	e = Atom_Write(s, bs);
	if (e) return e;

	BS_WriteU32(bs, ptr->cfg.vendor);
	BS_WriteU8(bs, ptr->cfg.decoder_version);
	switch (ptr->cfg.type) {
	case M4_3GP_H263_SubType:
		BS_WriteU8(bs, ptr->cfg.H263_level);
		BS_WriteU8(bs, ptr->cfg.H263_profile);
		break;
	case M4_3GP_AMR_SubType:
	case M4_3GP_AMR_WB_SubType:
		BS_WriteU16(bs, ptr->cfg.AMR_mode_set);
		BS_WriteU8(bs, ptr->cfg.AMR_mode_change_period);
		BS_WriteU8(bs, ptr->cfg.frames_per_sample);
		break;
	case M4_3GP_EVRC_SubType:
	case M4_3GP_QCELP_SubType:
	case M4_3GP_SMV_SubType:
		BS_WriteU8(bs, ptr->cfg.frames_per_sample);
		break;
	}
	return M4OK;
}

M4Err gppc_Size(Atom *s)
{
	M4Err e;
	GP3ConfigAtom *ptr = (GP3ConfigAtom *)s;

	e = Atom_Size(s);
	if (e) return e;
	s->size += 5;
	switch (ptr->cfg.type) {
	case M4_3GP_H263_SubType:
		s->size += 2;
		break;
	case M4_3GP_AMR_SubType:
	case M4_3GP_AMR_WB_SubType:
		s->size += 4;
		break;
	case M4_3GP_EVRC_SubType:
	case M4_3GP_QCELP_SubType:
	case M4_3GP_SMV_SubType:
		s->size += 1;
		break;
	}
	return M4OK;
}

#endif //M4_READ_ONLY


Atom *ftab_New()
{
	FontTableAtom *tmp;
	SAFEALLOC(tmp, sizeof(FontTableAtom));
	if (!tmp) return NULL;
	tmp->type = FontTableAtomType;
	return (Atom *) tmp;
}
void ftab_del(Atom *s)
{
	FontTableAtom *ptr = (FontTableAtom *)s;
	if (ptr->fonts) {
		u32 i;
		for (i=0; i<ptr->entry_count; i++) 
			if (ptr->fonts[i].fontName) free(ptr->fonts[i].fontName);
		free(ptr->fonts);
	}
	free(ptr);
}
M4Err ftab_Read(Atom *s, BitStream *bs, u64 *read)
{
	u32 i;
	FontTableAtom *ptr = (FontTableAtom *)s;
	ptr->entry_count = BS_ReadU16(bs);
	*read += 2;
	SAFEALLOC(ptr->fonts, sizeof(FontRecord)*ptr->entry_count);
	for (i=0; i<ptr->entry_count; i++) {
		u32 len;
		ptr->fonts[i].fontID = BS_ReadU16(bs);
		len = BS_ReadU8(bs);
		*read += 3;
		if (len) {
			SAFEALLOC(ptr->fonts[i].fontName, sizeof(char)*(len+1));
			BS_ReadData(bs, ptr->fonts[i].fontName, len);
			*read += len;
		}
	}
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err ftab_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	u32 i;
	FontTableAtom *ptr = (FontTableAtom *)s;
	e = Atom_Write(s, bs);
	if (e) return e;
	BS_WriteU16(bs, ptr->entry_count);
	for (i=0; i<ptr->entry_count; i++) {
		BS_WriteU16(bs, ptr->fonts[i].fontID);
		if (ptr->fonts[i].fontName) {
			u32 len = strlen(ptr->fonts[i].fontName);
			BS_WriteU8(bs, len);
			BS_WriteData(bs, ptr->fonts[i].fontName, len);
		} else {
			BS_WriteU8(bs, 0);
		}
	}
	return M4OK;
}
M4Err ftab_Size(Atom *s)
{
	u32 i;
	FontTableAtom *ptr = (FontTableAtom *)s;
	M4Err e = Atom_Size(s);
	if (e) return e;
	s->size += 2;
	for (i=0; i<ptr->entry_count; i++) {
		s->size += 3;
		if (ptr->fonts[i].fontName) s->size += strlen(ptr->fonts[i].fontName);
	}
	return M4OK;
}

#endif




Atom *tx3g_New()
{
	TextSampleEntryAtom *tmp;
	SAFEALLOC(tmp, sizeof(TextSampleEntryAtom));
	if (!tmp) return NULL;
	tmp->type = TextSampleEntryAtomType;
	return (Atom *) tmp;
}

void tx3g_del(Atom *s)
{
	TextSampleEntryAtom *ptr = (TextSampleEntryAtom*)s;
	if (ptr->font_table) DelAtom((Atom *)ptr->font_table);
	free(ptr);
}

static u32 gpp_read_rgba(BitStream *bs)
{
	u8 r, g, b, a;
	u32 col;
	r = BS_ReadU8(bs);
	g = BS_ReadU8(bs);
	b = BS_ReadU8(bs);
	a = BS_ReadU8(bs);
	col = a; col<<=8; 
	col |= r; col<<=8; 
	col |= g; col<<=8; 
	col |= b;
	return col;
}

#define GPP_BOX_SIZE	8
static void gpp_read_box(BitStream *bs, BoxRecord *rec)
{
	rec->top = BS_ReadU16(bs);
	rec->left = BS_ReadU16(bs);
	rec->bottom = BS_ReadU16(bs);
	rec->right = BS_ReadU16(bs);
}

#define GPP_STYLE_SIZE	12
static void gpp_read_style(BitStream *bs, StyleRecord *rec)
{
	rec->startCharOffset = BS_ReadU16(bs);
	rec->endCharOffset = BS_ReadU16(bs);
	rec->fontID = BS_ReadU16(bs);
	rec->style_flags = BS_ReadU8(bs);
	rec->font_size = BS_ReadU8(bs);
	rec->text_color = gpp_read_rgba(bs);
}

M4Err tx3g_Read(Atom *s, BitStream *bs, u64 *read)
{
	M4Err e;
	u64 sr;
	Atom *a;
	TextSampleEntryAtom *ptr = (TextSampleEntryAtom*)s;

	BS_ReadData(bs, (unsigned char*)ptr->reserved, 6);
	ptr->dataReferenceIndex = BS_ReadU16(bs);
	ptr->displayFlags = BS_ReadU32(bs);
	ptr->horizontal_justification = BS_ReadU8(bs);
	ptr->vertical_justification = BS_ReadU8(bs);
	ptr->back_color = gpp_read_rgba(bs);
	gpp_read_box(bs, &ptr->default_box);
	gpp_read_style(bs, &ptr->default_style);
	*read += 18 + GPP_BOX_SIZE + GPP_STYLE_SIZE;

	while (*read < ptr->size) {
		e = ParseAtom(&a, bs, &sr);
		if (e) return e;
		*read += a->size;
		if (a->type==FontTableAtomType) {
			if (ptr->font_table) DelAtom((Atom *) ptr->font_table);
			ptr->font_table = (FontTableAtom *)a;
		} else {
			DelAtom(a);
		}
	}
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}


void gpp_write_rgba(BitStream *bs, u32 col)
{
	BS_WriteU8(bs, (col>>16) & 0xFF);
	BS_WriteU8(bs, (col>>8) & 0xFF);
	BS_WriteU8(bs, (col) & 0xFF);
	BS_WriteU8(bs, (col>>24) & 0xFF);
}

void gpp_write_box(BitStream *bs, BoxRecord *rec)
{
	BS_WriteU16(bs, rec->top);
	BS_WriteU16(bs, rec->left);
	BS_WriteU16(bs, rec->bottom);
	BS_WriteU16(bs, rec->right);
}

#define GPP_STYLE_SIZE	12
void gpp_write_style(BitStream *bs, StyleRecord *rec)
{
	BS_WriteU16(bs, rec->startCharOffset);
	BS_WriteU16(bs, rec->endCharOffset);
	BS_WriteU16(bs, rec->fontID);
	BS_WriteU8(bs, rec->style_flags);
	BS_WriteU8(bs, rec->font_size);
	gpp_write_rgba(bs, rec->text_color);
}

#ifndef M4_READ_ONLY

M4Err tx3g_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	TextSampleEntryAtom *ptr = (TextSampleEntryAtom*)s;

	e = Atom_Write(s, bs);
	if (e) return e;
	BS_WriteData(bs, (unsigned char*)ptr->reserved, 6);
	BS_WriteU16(bs, ptr->dataReferenceIndex);
	BS_WriteU32(bs, ptr->displayFlags);
	BS_WriteU8(bs, ptr->horizontal_justification);
	BS_WriteU8(bs, ptr->vertical_justification);
	gpp_write_rgba(bs, ptr->back_color);
	gpp_write_box(bs, &ptr->default_box);
	gpp_write_style(bs, &ptr->default_style);
	return WriteAtom((Atom *) ptr->font_table, bs);
}

M4Err tx3g_Size(Atom *s)
{
	TextSampleEntryAtom *ptr = (TextSampleEntryAtom*)s;
	M4Err e = Atom_Size(s);
	if (e) return e;
	/*base + this  + box + style*/
	s->size += 18 + GPP_BOX_SIZE + GPP_STYLE_SIZE;
	if (ptr->font_table) {
		e = SizeAtom((Atom *) ptr->font_table);
		if (e) return e;
		s->size += ptr->font_table->size;
	}
	return M4OK;
}

#endif

Atom *styl_New()
{
	TextStyleAtom *tmp;
	SAFEALLOC(tmp, sizeof(TextStyleAtom));
	if (!tmp) return NULL;
	tmp->type = TextStyleAtomType;
	return (Atom *) tmp;
}

void styl_del(Atom *s)
{
	TextStyleAtom*ptr = (TextStyleAtom*)s;
	if (ptr->styles) free(ptr->styles);
	free(ptr);
}

M4Err styl_Read(Atom *s, BitStream *bs, u64 *read)
{
	u32 i;
	TextStyleAtom*ptr = (TextStyleAtom*)s;
	ptr->entry_count = BS_ReadU16(bs);
	*read += 2;
	if (ptr->entry_count) {
		SAFEALLOC(ptr->styles, sizeof(StyleRecord)*ptr->entry_count);
		for (i=0; i<ptr->entry_count; i++) {
			gpp_read_style(bs, &ptr->styles[i]);
			*read += GPP_STYLE_SIZE;
		}
	}
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err styl_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	u32 i;
	TextStyleAtom*ptr = (TextStyleAtom*)s;
	e = Atom_Write(s, bs);

	BS_WriteU16(bs, ptr->entry_count);
	for (i=0; i<ptr->entry_count; i++) gpp_write_style(bs, &ptr->styles[i]);
	return M4OK;
}

M4Err styl_Size(Atom *s)
{
	TextStyleAtom*ptr = (TextStyleAtom*)s;
	M4Err e = Atom_Size(s);
	if (e) return e;
	s->size += 2 + ptr->entry_count * GPP_STYLE_SIZE;
	return M4OK;
}

#endif

Atom *hlit_New()
{
	TextHighlightAtom *tmp;
	SAFEALLOC(tmp, sizeof(TextHighlightAtom));
	if (!tmp) return NULL;
	tmp->type = TextHighlightAtomType;
	return (Atom *) tmp;
}

void hlit_del(Atom *s)
{
	free(s);
}

M4Err hlit_Read(Atom *s, BitStream *bs, u64 *read)
{
	TextHighlightAtom *ptr = (TextHighlightAtom *)s;
	ptr->startcharoffset = BS_ReadU16(bs);
	ptr->endcharoffset = BS_ReadU16(bs);
	*read += 4;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err hlit_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	TextHighlightAtom *ptr = (TextHighlightAtom *)s;
	e = Atom_Write(s, bs);
	if (e) return e;
	BS_WriteU16(bs, ptr->startcharoffset);
	BS_WriteU16(bs, ptr->endcharoffset);
	return M4OK;
}

M4Err hlit_Size(Atom *s)
{
	M4Err e = Atom_Size(s);
	if (e) return e;
	s->size += 4;
	return M4OK;
}

#endif

Atom *hclr_New()
{
	TextHighlightColorAtom*tmp;
	SAFEALLOC(tmp, sizeof(TextHighlightColorAtom));
	if (!tmp) return NULL;
	tmp->type = TextHighlightColorAtomType;
	return (Atom *) tmp;
}

void hclr_del(Atom *s)
{
	free(s);
}

M4Err hclr_Read(Atom *s, BitStream *bs, u64 *read)
{
	TextHighlightColorAtom*ptr = (TextHighlightColorAtom*)s;
	ptr->hil_color = gpp_read_rgba(bs);
	*read += 4;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err hclr_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	TextHighlightColorAtom*ptr = (TextHighlightColorAtom*)s;
	e = Atom_Write(s, bs);
	if (e) return e;
	gpp_write_rgba(bs, ptr->hil_color);
	return M4OK;
}

M4Err hclr_Size(Atom *s)
{
	M4Err e = Atom_Size(s);
	if (e) return e;
	s->size += 4;
	return M4OK;
}

#endif

Atom *krok_New()
{
	TextKaraokeAtom*tmp;
	SAFEALLOC(tmp, sizeof(TextKaraokeAtom));
	if (!tmp) return NULL;
	tmp->type = TextKaraokeAtomType;
	return (Atom *) tmp;
}

void krok_del(Atom *s)
{
	TextKaraokeAtom*ptr = (TextKaraokeAtom*)s;
	if (ptr->records) free(ptr->records);
	free(ptr);
}

M4Err krok_Read(Atom *s, BitStream *bs, u64 *read)
{
	TextKaraokeAtom*ptr = (TextKaraokeAtom*)s;

	ptr->highlight_starttime = BS_ReadU32(bs);
	ptr->entrycount = BS_ReadU16(bs);
	*read += 6;
	if (ptr->entrycount) {
		u32 i;
		SAFEALLOC(ptr->records, sizeof(KaraokeRecord)*ptr->entrycount);
		for (i=0; i<ptr->entrycount; i++) {
			ptr->records[i].highlight_endtime = BS_ReadU32(bs);
			ptr->records[i].start_charoffset = BS_ReadU16(bs);
			ptr->records[i].end_charoffset = BS_ReadU16(bs);
			*read += 8;
		}
	}
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err krok_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	u32 i;
	TextKaraokeAtom*ptr = (TextKaraokeAtom*)s;
	e = Atom_Write(s, bs);
	if (e) return e;

	BS_WriteU32(bs, ptr->highlight_starttime);
	BS_WriteU16(bs, ptr->entrycount);
	for (i=0; i<ptr->entrycount; i++) {
		BS_WriteU32(bs, ptr->records[i].highlight_endtime);
		BS_WriteU16(bs, ptr->records[i].start_charoffset);
		BS_WriteU16(bs, ptr->records[i].end_charoffset);
	}
	return M4OK;
}

M4Err krok_Size(Atom *s)
{
	TextKaraokeAtom*ptr = (TextKaraokeAtom*)s;
	M4Err e = Atom_Size(s);
	if (e) return e;
	s->size += 6 * 8*ptr->entrycount;
	return M4OK;
}

#endif

Atom *dlay_New()
{
	TextScrollDelayAtom*tmp;
	SAFEALLOC(tmp, sizeof(TextScrollDelayAtom));
	if (!tmp) return NULL;
	tmp->type = TextScrollDelayAtomType;
	return (Atom *) tmp;
}

void dlay_del(Atom *s)
{
	free(s);
}

M4Err dlay_Read(Atom *s, BitStream *bs, u64 *read)
{
	TextScrollDelayAtom*ptr = (TextScrollDelayAtom*)s;
	ptr->scroll_delay = BS_ReadU32(bs);
	*read += 4;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err dlay_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	TextScrollDelayAtom*ptr = (TextScrollDelayAtom*)s;
	e = Atom_Write(s, bs);
	if (e) return e;
	BS_WriteU32(bs, ptr->scroll_delay);
	return M4OK;
}

M4Err dlay_Size(Atom *s)
{
	M4Err e = Atom_Size(s);
	if (e) return e;
	s->size += 4;
	return M4OK;
}

#endif

Atom *href_New()
{
	TextHyperTextAtom*tmp;
	SAFEALLOC(tmp, sizeof(TextHyperTextAtom));
	if (!tmp) return NULL;
	tmp->type = TextHyperTextAtomType;
	return (Atom *) tmp;
}

void href_del(Atom *s)
{
	TextHyperTextAtom*ptr = (TextHyperTextAtom*)s;
	if (ptr->URL) free(ptr->URL);
	if (ptr->URL_hint) free(ptr->URL_hint);
	free(ptr);
}

M4Err href_Read(Atom *s, BitStream *bs, u64 *read)
{
	u32 len;
	TextHyperTextAtom*ptr = (TextHyperTextAtom*)s;
	ptr->startcharoffset = BS_ReadU16(bs);
	ptr->endcharoffset = BS_ReadU16(bs);
	*read += 6;
	len = BS_ReadU8(bs);
	if (len) {
		SAFEALLOC(ptr->URL, sizeof(char) * (len+1));
		BS_ReadData(bs, ptr->URL, len);
		*read += len;
	}
	len = BS_ReadU8(bs);
	if (len) {
		SAFEALLOC(ptr->URL_hint, sizeof(char) * (len+1));
		BS_ReadData(bs, ptr->URL_hint, len);
		*read += len;
	}
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err href_Write(Atom *s, BitStream *bs)
{
	u32 len;
	M4Err e;
	TextHyperTextAtom*ptr = (TextHyperTextAtom*)s;
	e = Atom_Write(s, bs);
	if (e) return e;

	BS_WriteU16(bs, ptr->startcharoffset);
	BS_WriteU16(bs, ptr->endcharoffset);
	if (ptr->URL) {
		len = strlen(ptr->URL);
		BS_WriteU8(bs, len);
		BS_WriteData(bs, ptr->URL, len);
	} else {
		BS_WriteU8(bs, 0);
	}
	if (ptr->URL_hint) {
		len = strlen(ptr->URL_hint);
		BS_WriteU8(bs, len);
		BS_WriteData(bs, ptr->URL_hint, len);
	} else {
		BS_WriteU8(bs, 0);
	}
	return M4OK;
}

M4Err href_Size(Atom *s)
{
	TextHyperTextAtom*ptr = (TextHyperTextAtom*)s;
	M4Err e = Atom_Size(s);
	if (e) return e;
	s->size += 6;
	if (ptr->URL) s->size += strlen(ptr->URL);
	if (ptr->URL_hint) s->size += strlen(ptr->URL_hint);
	return M4OK;
}

#endif


Atom *tbox_New()
{
	TextBoxAtom*tmp;
	SAFEALLOC(tmp, sizeof(TextBoxAtom));
	if (!tmp) return NULL;
	tmp->type = TextBoxAtomType;
	return (Atom *) tmp;
}

void tbox_del(Atom *s)
{
	free(s);
}

M4Err tbox_Read(Atom *s, BitStream *bs, u64 *read)
{
	TextBoxAtom*ptr = (TextBoxAtom*)s;
	gpp_read_box(bs, &ptr->box);
	*read += GPP_BOX_SIZE;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err tbox_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	TextBoxAtom*ptr = (TextBoxAtom*)s;
	e = Atom_Write(s, bs);
	if (e) return e;
	gpp_write_box(bs, &ptr->box);
	return M4OK;
}

M4Err tbox_Size(Atom *s)
{
	M4Err e = Atom_Size(s);
	if (e) return e;
	s->size += 8;
	return M4OK;
}

#endif


Atom *blnk_New()
{
	TextBlinkAtom*tmp;
	SAFEALLOC(tmp, sizeof(TextBlinkAtom));
	if (!tmp) return NULL;
	tmp->type = TextBlinkAtomType;
	return (Atom *) tmp;
}

void blnk_del(Atom *s)
{
	free(s);
}

M4Err blnk_Read(Atom *s, BitStream *bs, u64 *read)
{
	TextBlinkAtom*ptr = (TextBlinkAtom*)s;
	ptr->startcharoffset = BS_ReadU16(bs);
	ptr->endcharoffset = BS_ReadU16(bs);
	*read += 4;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err blnk_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	TextBlinkAtom*ptr = (TextBlinkAtom*)s;
	e = Atom_Write(s, bs);
	if (e) return e;
	BS_WriteU16(bs, ptr->startcharoffset);
	BS_WriteU16(bs, ptr->endcharoffset);
	return M4OK;
}

M4Err blnk_Size(Atom *s)
{
	M4Err e = Atom_Size(s);
	if (e) return e;
	s->size += 4;
	return M4OK;
}

#endif

Atom *twrp_New()
{
	TextWrapAtom*tmp;
	SAFEALLOC(tmp, sizeof(TextWrapAtom));
	if (!tmp) return NULL;
	tmp->type = TextWrapAtomType;
	return (Atom *) tmp;
}

void twrp_del(Atom *s)
{
	free(s);
}

M4Err twrp_Read(Atom *s, BitStream *bs, u64 *read)
{
	TextWrapAtom*ptr = (TextWrapAtom*)s;
	ptr->wrap_flag = BS_ReadU8(bs);
	*read += 1;
	if (*read != ptr->size) return M4ReadAtomFailed;
	return M4OK;
}

#ifndef M4_READ_ONLY
M4Err twrp_Write(Atom *s, BitStream *bs)
{
	M4Err e;
	TextWrapAtom*ptr = (TextWrapAtom*)s;
	e = Atom_Write(s, bs);
	if (e) return e;
	BS_WriteU8(bs, ptr->wrap_flag);
	return M4OK;
}
M4Err twrp_Size(Atom *s)
{
	M4Err e = Atom_Size(s);
	if (e) return e;
	s->size += 1;
	return M4OK;
}

#endif

