/*
   ======================================================================
   clip.c

   Functions for LWO2 image references.

   Ernie Wright  17 Sep 00
   ====================================================================== */

#include "../picointernal.h"
#include "lwo2.h"


/*
   ======================================================================
   lwFreeClip()

   Free memory used by an lwClip.
   ====================================================================== */

void lwFreeClip( lwClip *clip ){
	if ( clip ) {
		lwListFree(clip->ifilter, (void (*)(void *)) lwFreePlugin);
		lwListFree(clip->pfilter, (void (*)(void *)) lwFreePlugin);

		switch ( clip->type ) {
		case ID_STIL:
			_pico_free( clip->source.still.name );
			break;

		case ID_ISEQ:
			_pico_free( clip->source.seq.prefix );
			_pico_free( clip->source.seq.suffix );
			break;

		case ID_ANIM:
			_pico_free( clip->source.anim.name );
			_pico_free( clip->source.anim.server );
			_pico_free( clip->source.anim.data );
			break;

		case ID_XREF:
			_pico_free( clip->source.xref.string );
			break;

		case ID_STCC:
			_pico_free( clip->source.cycle.name );
			break;

		default:
			break;
		}

		_pico_free( clip );
	}
}


/*
   ======================================================================
   lwGetClip()

   Read image references from a CLIP chunk in an LWO2 file.
   ====================================================================== */

lwClip *lwGetClip( picoMemStream_t *fp, int cksize ){
	lwClip *clip;
	lwPlugin *filt;
	unsigned int id;
	unsigned short sz;
	int pos, rlen;


	/* allocate the Clip structure */

	clip = _pico_calloc( 1, sizeof( lwClip ) );
	if ( !clip ) {
		goto Fail;
	}

	clip->contrast.val = 1.0f;
	clip->brightness.val = 1.0f;
	clip->saturation.val = 1.0f;
	clip->gamma.val = 1.0f;

	/* remember where we started */

	set_flen( 0 );
	pos = _pico_memstream_tell( fp );

	/* index */

	clip->index = getI4( fp );

	/* first subchunk header */

	clip->type = getU4( fp );
	sz = getU2( fp );
	if ( 0 > get_flen() ) {
		goto Fail;
	}

	sz += sz & 1;
	set_flen( 0 );

	switch ( clip->type ) {
	case ID_STIL:
		clip->source.still.name = getS0( fp );
		break;

	case ID_ISEQ:
		clip->source.seq.digits  = getU1( fp );
		clip->source.seq.flags   = getU1( fp );
		clip->source.seq.offset  = getI2( fp );
		getU2( fp );   /* not sure what this is yet */
		clip->source.seq.start   = getI2( fp );
		clip->source.seq.end     = getI2( fp );
		clip->source.seq.prefix  = getS0( fp );
		clip->source.seq.suffix  = getS0( fp );
		break;

	case ID_ANIM:
		clip->source.anim.name   = getS0( fp );
		clip->source.anim.server = getS0( fp );
		rlen = get_flen();
		clip->source.anim.data   = getbytes( fp, sz - rlen );
		break;

	case ID_XREF:
		clip->source.xref.index  = getI4( fp );
		clip->source.xref.string = getS0( fp );
		break;

	case ID_STCC:
		clip->source.cycle.lo   = getI2( fp );
		clip->source.cycle.hi   = getI2( fp );
		clip->source.cycle.name = getS0( fp );
		break;

	default:
		break;
	}

	/* error while reading current subchunk? */

	rlen = get_flen();
	if ( rlen < 0 || rlen > sz ) {
		goto Fail;
	}

	/* skip unread parts of the current subchunk */

	if ( rlen < sz ) {
		_pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
	}

	/* end of the CLIP chunk? */

	rlen = _pico_memstream_tell( fp ) - pos;
	if ( cksize < rlen ) {
		goto Fail;
	}
	if ( cksize == rlen ) {
		return clip;
	}

	/* process subchunks as they're encountered */

	id = getU4( fp );
	sz = getU2( fp );
	if ( 0 > get_flen() ) {
		goto Fail;
	}

	while ( 1 ) {
		sz += sz & 1;
		set_flen( 0 );

		switch ( id ) {
		case ID_TIME:
			clip->start_time = getF4( fp );
			clip->duration = getF4( fp );
			clip->frame_rate = getF4( fp );
			break;

		case ID_CONT:
			clip->contrast.val = getF4( fp );
			clip->contrast.eindex = getVX( fp );
			break;

		case ID_BRIT:
			clip->brightness.val = getF4( fp );
			clip->brightness.eindex = getVX( fp );
			break;

		case ID_SATR:
			clip->saturation.val = getF4( fp );
			clip->saturation.eindex = getVX( fp );
			break;

		case ID_HUE:
			clip->hue.val = getF4( fp );
			clip->hue.eindex = getVX( fp );
			break;

		case ID_GAMM:
			clip->gamma.val = getF4( fp );
			clip->gamma.eindex = getVX( fp );
			break;

		case ID_NEGA:
			clip->negative = getU2( fp );
			break;

		case ID_IFLT:
		case ID_PFLT:
			filt = _pico_calloc( 1, sizeof( lwPlugin ) );
			if ( !filt ) {
				goto Fail;
			}

			filt->name = getS0( fp );
			filt->flags = getU2( fp );
			rlen = get_flen();
			filt->data = getbytes( fp, sz - rlen );

			if ( id == ID_IFLT ) {
				lwListAdd( (void *) &clip->ifilter, filt );
				clip->nifilters++;
			}
			else {
				lwListAdd( (void *) &clip->pfilter, filt );
				clip->npfilters++;
			}
			break;

		default:
			break;
		}

		/* error while reading current subchunk? */

		rlen = get_flen();
		if ( rlen < 0 || rlen > sz ) {
			goto Fail;
		}

		/* skip unread parts of the current subchunk */

		if ( rlen < sz ) {
			_pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
		}

		/* end of the CLIP chunk? */

		rlen = _pico_memstream_tell( fp ) - pos;
		if ( cksize < rlen ) {
			goto Fail;
		}
		if ( cksize == rlen ) {
			break;
		}

		/* get the next chunk header */

		set_flen( 0 );
		id = getU4( fp );
		sz = getU2( fp );
		if ( 6 != get_flen() ) {
			goto Fail;
		}
	}

	return clip;

Fail:
	lwFreeClip( clip );
	return NULL;
}


/*
   ======================================================================
   lwFindClip()

   Returns an lwClip pointer, given a clip index.
   ====================================================================== */

lwClip *lwFindClip( lwClip *list, int index ){
	lwClip *clip;

	clip = list;
	while ( clip ) {
		if ( clip->index == index ) {
			break;
		}
		clip = clip->next;
	}
	return clip;
}
