// spri.cpp
#include "spri.h"

#ifndef PandaWave_RESOURCES
#include "RevengeResources.h"
#endif

#include "RevengeApp.h"
#include "tagpick.h"
#include <assert.h>
#include "TBitmapView.h"
#include <XMessages.h>
#include <XList.h>
#include <XView.h>
#include <XWindow.h>
#include <XButtons.h>
#include <XDialog.h>
#include "texturestack.h"
#include "jpeg.h"
#include "formatters.h"

static byte_swap_code _4byte_bs[] = 
{
	_begin_bs_array, 1, 
	_4byte,
	_end_bs_array
};

static byte_swap_code sprite_frame_definition_bs[] =
{
	_begin_bs_array, 1,
	
	// float xy[4][2]
	_begin_bs_array, 4,
		_begin_bs_array, 2,
			_4byte,
		_end_bs_array,
	_end_bs_array,
		
	// float st[4][2]
	_begin_bs_array, 4,
		_begin_bs_array, 2,
			_4byte,
		_end_bs_array,
	_end_bs_array,
		
	// float slot_st[2][2]
	_begin_bs_array, 2,
		_begin_bs_array, 2,
			_4byte,
		_end_bs_array,
	_end_bs_array,
	
	// float slot_xy[2]
	_begin_bs_array, 2,
		_4byte,
	_end_bs_array,
	
	// float slot_wh[2]
	_begin_bs_array, 2, 
		_4byte,
	_end_bs_array,
	
	_4byte,
	_4byte,
	_4byte,
	
	sizeof(void*),
	sizeof(int)*2,
	sizeof(short)*4,
	
	_end_bs_array
};

static byte_swap_code sprite_sequence_definition_bs[] = 
{
	_begin_bs_array, 1, 
	
	// int num_frames
	_4byte,
	// int probability
	_4byte,
	// int time
	_4byte,
	// int flags,
	_4byte, 
	
	sizeof(int*),
	sizeof(int*),
	sizeof(float*),
	sizeof(float*),
	sizeof(tag*),
	sizeof(float*),
	
	_end_bs_array
	
	// note there is stuff at the end but it is variable, so we
	// have to swap load it in seperately.
};

static byte_swap_code sprite_type_definition_bs[] = 
{
	_begin_bs_array, 1,
	
	// int num_frames
	_4byte,
	// int num_sequences
	_4byte,
	// int total_sequence_probability
	_4byte,
	// int flags
	_4byte,
	
	sizeof(tag),
	sizeof( struct sprite_frame_definition*),
	sizeof( struct sprite_sequence_definition*),
	
	_end_bs_array
	
	// note there is stuff at the end but it is variable, so we
	// have to swap load it in seperately.
};

void load_frame_texture( sprite_frame_definition* frame );
void load_frame_texture( sprite_frame_definition* frame )
{
	stack_set* ss;
	tag_entry* tx_entry;
	stacked_texture* tex;
	
	if( frame->data )
		free( frame->data );
	frame->data = 0;
	
	tx_entry = get_entry( 'txst', frame->texture_stack_tag );
	if( !tx_entry )
		return;
	ss = load_stackset( tx_entry );
	if( !ss )
		return;
	tex = get_texture( ss, frame->stack_num, frame->tex_num );
	if( !tex )
		return;
	
	frame->width = tex->width;
	frame->height = tex->height;
	frame->channels = tex->channels;
	frame->data = malloc(tex->width*tex->height*tex->channels);
	assert( frame->data );
	
	if( tex->format == 1 ) // jpg.
		decompress_jpeg( frame->data, tex->compressed_data, tex->compressed_length, tex->width, tex->height, tex->channels, TRUE );
	else
		memcpy( frame->data, tex->compressed_data, tex->compressed_length );
		
	dispose_stackset( ss );
}

void defaultnew_spri( tag_entry* e )
{
	sprite_type_definition def;
	
	def.num_frames = 0;
	def.num_sequences = 0;
	def.total_sequence_probability = 0;
	def.id = 0;
	def.flags = 0;
	def.frames = 0;
	def.sequences = 0;
	
	save_sprite_type_definition( &def, e );
}

void edit_spri( tag_entry* e )
{
	TspriEditForm* form = new TspriEditForm( e );
	form->AttachWindow( KWindowSpriteDefinition );
}


void dependency_spri( tag_entry *e )
{
	sprite_type_definition *def;
	def = alloc_sprite_type_definition( MAX_SPRITE_FRAMES, MAX_SPRITE_SEQUENCES, MAX_FRAMES_PER_SEQUENCE );
	
	read_sprite_type_definition( def, e, false );
	int i, j;
	for (i = 0; i < def->num_frames; ++i)
	{
		add_tag_dependency( 'txst', def->frames[i].texture_stack_tag );
	}
	for (i = 0; i < def->num_sequences; ++i)
	{
		for (j = 0; j < def->sequences[i].num_frames; ++j)
		{
			add_tag_dependency( 'soun', def->sequences[i].sound_tags[j] );
		}
	}
	
	free_sprite_type_definition( def );
}


bool read_sprite_type_definition( sprite_type_definition* def, tag id, bool load_textures )
{
	tag_entry* e;
	
	assert( def );
	e = get_entry( 'spri', id );
	if( 0 == e )
		return FALSE;
	
	return read_sprite_type_definition( def, e, load_textures );
}

bool save_sprite_type_definition( sprite_type_definition* def, tag id )
{
	tag_entry* e;
	
	assert( def );
	e = get_entry( 'spri', id );
	if( 0 == e )
		return FALSE;
		
	return save_sprite_type_definition( def, e );
}

bool read_sprite_type_definition( sprite_type_definition* def, tag_entry* e, bool load_textures )
{
	int i;
	unsigned int length;
	const unsigned char* data;
	sprite_type_definition temp;
	sprite_sequence_definition temp_seq;
	
	if( !get_entry_data( e, (const void**)&data, &length ) )
		return FALSE;
	
	free_sprite_type_definition_frame_textures( def );
	
	temp = *def; // save off ptr's.
		
	byte_swap_move_be( "std", def, data, sizeof( sprite_type_definition ), 1, sprite_type_definition_bs );
	data += sizeof(sprite_type_definition);
	assert( def->num_frames <= MAX_SPRITE_FRAMES );
	assert( def->num_sequences <= MAX_SPRITE_SEQUENCES );
	
	// load the frames up.
	def->frames = temp.frames;
	def->sequences = temp.sequences;
	
	if(def->num_frames > 0)
	{
		byte_swap_move_be( "sfd", def->frames, data, sizeof( sprite_frame_definition ), def->num_frames, sprite_frame_definition_bs );
		data += (sizeof(sprite_frame_definition)*def->num_frames);
		for(i = 0; i < def->num_frames; i++)
			def->frames[i].data = 0;
	}
	
	// load the sequences up.
	for(i = 0; i < def->num_sequences; i++)
	{
		temp_seq = def->sequences[i];
		byte_swap_move_be( "ssd", &def->sequences[i], data, sizeof( sprite_sequence_definition ), 1, sprite_sequence_definition_bs );
		data += sizeof(sprite_sequence_definition);
		// restore ptrs to memory.
		def->sequences[i].frames = temp_seq.frames;
		def->sequences[i].ticks  = temp_seq.ticks;
		def->sequences[i].colors = temp_seq.colors;
		def->sequences[i].rots   = temp_seq.rots;
		def->sequences[i].sound_tags = temp_seq.sound_tags;
		def->sequences[i].scales = temp_seq.scales;
	}
	
	for(i = 0; i < def->num_sequences; i++)
	{
		byte_swap_move_be( "ssf", def->sequences[i].frames, data, sizeof(int), def->sequences[i].num_frames, _4byte_bs );
		data += sizeof(int)*def->sequences[i].num_frames;
		byte_swap_move_be( "sst", def->sequences[i].ticks, data, sizeof(int), def->sequences[i].num_frames, _4byte_bs );
		data += sizeof(int)*def->sequences[i].num_frames;
		byte_swap_move_be( "ssc", def->sequences[i].colors, data, sizeof(float), def->sequences[i].num_frames*4, _4byte_bs );
		data += sizeof(float)*def->sequences[i].num_frames*4;
		byte_swap_move_be( "ssr", def->sequences[i].rots, data, sizeof(float), def->sequences[i].num_frames, _4byte_bs );
		data += sizeof(float)*def->sequences[i].num_frames;
		byte_swap_move_be( "ssf", def->sequences[i].sound_tags, data, sizeof(int), def->sequences[i].num_frames, _4byte_bs );
		data += sizeof(tag)*def->sequences[i].num_frames;
		byte_swap_move_be( "sss", def->sequences[i].scales, data, sizeof(float), def->sequences[i].num_frames, _4byte_bs );
		data += sizeof(float)*def->sequences[i].num_frames;
	}
	
	if (load_textures)
	{
		load_sprite_type_definition_frame_textures( def );
	}
	
	return TRUE;
}

bool save_sprite_type_definition( sprite_type_definition* def, tag_entry* e )
{	
	/*
	i just want you to know this is going to be a pain in the ass.
	*/
	int i, size;
	unsigned char* data, *base;
	
	// compute the block size we need.
	size = sizeof(sprite_type_definition);
	size += sizeof(sprite_frame_definition)*def->num_frames;
	size += sizeof(sprite_sequence_definition)*def->num_sequences;
	
	for(i = 0; i < def->num_sequences; i++)
		size += (sizeof(float)+sizeof(int)+sizeof(int)+sizeof(tag)+sizeof(float)+(sizeof(float)*4))*def->sequences[i].num_frames;	
	
	assert( size > 0 );
	base = data = (unsigned char*)malloc(size);
	
	byte_swap_move_be( "std", data, def, sizeof( sprite_type_definition ), 1, sprite_type_definition_bs );
	data += sizeof(sprite_type_definition);
	
	if( def->num_frames > 0 )
	{
		byte_swap_move_be( "sfd", data, def->frames, sizeof( sprite_frame_definition ), def->num_frames, sprite_frame_definition_bs );
		data += sizeof(sprite_frame_definition)*def->num_frames;
	}
	
	if( def->num_sequences > 0 )
	{
		byte_swap_move_be( "ssd", data, def->sequences, sizeof( sprite_sequence_definition ), def->num_sequences, sprite_sequence_definition_bs );
		data += sizeof(sprite_sequence_definition)*def->num_sequences;
		
		for(i = 0; i < def->num_sequences; i++)
		{
			if( def->sequences[i].num_frames > 0 )
			{
				byte_swap_move_be( "ssf", data, def->sequences[i].frames, sizeof(int), def->sequences[i].num_frames, _4byte_bs );
				data += sizeof(int)*def->sequences[i].num_frames;
				byte_swap_move_be( "sst", data, def->sequences[i].ticks, sizeof(int), def->sequences[i].num_frames, _4byte_bs );
				data += sizeof(int)*def->sequences[i].num_frames;
				byte_swap_move_be( "ssc", data, def->sequences[i].colors, sizeof(float), def->sequences[i].num_frames*4, _4byte_bs );
				data += sizeof(float)*def->sequences[i].num_frames*4;
				byte_swap_move_be( "ssr", data, def->sequences[i].rots, sizeof(float), def->sequences[i].num_frames, _4byte_bs );
				data += sizeof(float)*def->sequences[i].num_frames;
				byte_swap_move_be( "ssf", data, def->sequences[i].sound_tags, sizeof(tag), def->sequences[i].num_frames, _4byte_bs );
				data += sizeof(tag)*def->sequences[i].num_frames;
				byte_swap_move_be( "sss", data, def->sequences[i].scales, sizeof(float), def->sequences[i].num_frames, _4byte_bs );
				data += sizeof(float)*def->sequences[i].num_frames;
			}
		}	
	}
	
	set_entry_data( e, base, size, TRUE );
	
	return TRUE;
}

sprite_type_definition* alloc_sprite_type_definition( int num_frames, int num_sequences, int max_frames_per_sequence )
{
	int i;
	sprite_type_definition* def;
	
	def = (sprite_type_definition*)malloc(sizeof(sprite_type_definition));
	assert( def );
	
	def->num_frames = num_frames;
	def->num_sequences = num_sequences;
	def->total_sequence_probability = 0;
	def->flags = 0;
	def->id = 0;
	def->frames = 0;
	def->sequences = 0;
	
	if( num_frames > 0 )
	{
		def->frames = (sprite_frame_definition*)malloc(sizeof(sprite_frame_definition)*num_frames);
		assert( def->frames );
		for(i = 0; i < num_frames; i++)
			def->frames[i].data = 0;
	}
	
	if( num_sequences > 0 )
	{
		def->sequences = (sprite_sequence_definition*)malloc(sizeof(sprite_sequence_definition)*num_sequences);
		assert( def->sequences );
		for(i = 0; i < num_sequences; i++)
		{
			def->sequences[i].num_frames = 0;
			def->sequences[i].probability = 0;
			def->sequences[i].time = 0;
			def->sequences[i].flags = 0;
			
			if( max_frames_per_sequence > 0 )
			{
				def->sequences[i].frames = (int*)malloc(sizeof(int)*max_frames_per_sequence);
				assert( def->sequences[i].frames );
				def->sequences[i].ticks  = (int*)malloc(sizeof(int)*max_frames_per_sequence);
				assert( def->sequences[i].ticks );
				def->sequences[i].colors = (float*)malloc(sizeof(float)*max_frames_per_sequence*4);
				assert( def->sequences[i].colors );
				def->sequences[i].rots = (float*)malloc(sizeof(float)*max_frames_per_sequence);
				assert( def->sequences[i].rots );
				def->sequences[i].sound_tags = (tag*)malloc(sizeof(tag)*max_frames_per_sequence);
				assert( def->sequences[i].sound_tags );
				def->sequences[i].scales = (float*)malloc(sizeof(float)*max_frames_per_sequence);
				assert( def->sequences[i].scales );
			}
		}
	}
	
	
	return def;
}

bool sprite_type_definitions_differ( sprite_type_definition* def_a, sprite_type_definition* def_b )
{
	int i;
	sprite_sequence_definition* seq_a, *seq_b;
	
	if( def_a->num_frames != def_b->num_frames ||
		def_a->num_sequences != def_b->num_sequences ||
		def_a->flags != def_b->flags )
			return TRUE;
			
	for(i = 0; i < def_a->num_frames; i++)
	{
		if( memcmp( &def_a->frames[i], &def_b->frames[i], 108 ) ) // hardcoded value of things that matter.
			return TRUE;
	}
	
	for(i = 0; i < def_a->num_sequences; i++)
	{
		seq_a = &def_a->sequences[i];
		seq_b = &def_b->sequences[i];
		
		if( seq_a->num_frames != seq_b->num_frames ||
			seq_a->probability != seq_b->probability ||
			seq_a->time != seq_b->time ||
			seq_a->flags != seq_b->flags )
				return TRUE;
				
		if( memcmp( seq_a->frames, seq_b->frames, sizeof(int)*seq_a->num_frames ) )
			return TRUE;
		if( memcmp( seq_a->ticks, seq_b->ticks, sizeof(int)*seq_a->num_frames ) )
			return TRUE;
		if( memcmp( seq_a->colors, seq_b->colors, sizeof(float)*seq_a->num_frames*4 ) )
			return TRUE;
		if( memcmp( seq_a->rots, seq_b->rots, sizeof(float)*seq_a->num_frames ) ) 
			return TRUE;
		if( memcmp( seq_a->sound_tags, seq_b->sound_tags, sizeof(tag)*seq_a->num_frames ) )
			return TRUE;
		if( memcmp( seq_a->scales, seq_b->scales, sizeof(float)*seq_a->num_frames ) )
			return TRUE;
	}
	
	return FALSE;
}

void free_sprite_type_definition( sprite_type_definition* def )
{
	assert( def );
	
	if( def->frames )
	{
		int i;
		for(i = 0; i < def->num_frames; i++)
		{
			if( def->frames[i].data )
				free( def->frames[i].data );
		}
		free( def->frames );
	}
	
	if( def->sequences )
	{	
		int i;
		for(i = 0; i < def->num_sequences; i++)
		{
			if( def->sequences[i].frames )
				free( def->sequences[i].frames );
			if( def->sequences[i].ticks )
				free( def->sequences[i].ticks );
			if( def->sequences[i].colors )
				free( def->sequences[i].colors );
			if( def->sequences[i].rots )
				free( def->sequences[i].rots );
			if( def->sequences[i].sound_tags )
				free( def->sequences[i].sound_tags );
			if( def->sequences[i].scales )
				free( def->sequences[i].scales );
		}

		free( def->sequences);
	}
	
	free( def );
	
}

void load_sprite_type_definition_frame_textures( sprite_type_definition* def )
{
	int i;
		
	for(i = 0; i < def->num_frames; i++)
	{
		load_frame_texture( &def->frames[i] );
	}
}

void free_sprite_type_definition_frame_textures( sprite_type_definition* def )
{
	int i;
	
	assert( def );
	for(i = 0; i < def->num_frames; i++)
	{
		if( def->frames[i].data )
			free( def->frames[i].data );
		def->frames[i].data = 0;
	}
}



static long EditFrameProc( XGDialog *dlog, void *data, long msg, long arg, void * /*parg*/ );
static long EditFrameProc( XGDialog *dlog, void *data, long msg, long arg, void * /*parg*/ )
{
	if (!dlog) return -1;
	sprite_frame_definition* frame_def = (sprite_frame_definition*)data;
	if(!frame_def)
		return -1;
	
	switch (msg) {
		case KXGDialogMessage:
			switch (arg) {
				case KXGDMInitDialog: {
						SetTag( dlog, 1000, 'txst', frame_def->texture_stack_tag );
						SetInt( dlog, 1002, frame_def->stack_num );
						SetInt( dlog, 1003, frame_def->tex_num );
						
						// load up vals.
						int i, k, c = 1004;
						for(i = 0; i < 4; i++)
						{
							for(k = 0; k < 2; k++)
							{
								SetFloat( dlog, c, frame_def->xy[i][k], 3 );
								c++;
							}
						}
						
						for(i = 0; i < 4; i++)
						{
							for(k = 0; k < 2; k++)
							{
								SetFloat( dlog, c, frame_def->st[i][k], 3 );
								c++;
							}
						}
						
					} return 0;
				default:
					break;
			} break;
		
		case KXGControlClick:
			if( arg == 2000 )
			{
				frame_def->texture_stack_tag = GetTag( dlog, 1000 );
				frame_def->stack_num = GetInt( dlog, 1002 );
				frame_def->tex_num = GetInt( dlog, 1003 );
				
				// get vals.
				int i, k, c = 1004;
				
				for(i = 0; i < 4; i++)
				{
					for(k = 0; k < 2; k++)
					{
						frame_def->xy[i][k] = GetFloat( dlog, c );
						c++;
					}
				}
				
				for(i = 0; i < 4; i++)
				{
					for(k = 0; k < 2; k++)
					{
						frame_def->st[i][k] = GetFloat( dlog, c );
						c++;
					}
				}
				
				dlog->EndDialog( 1 );
				return 0;
			} else if (arg == 3000) { // cancel
				dlog->EndDialog( 0 );
				return 0;
			}
			else if( arg == 1000 )
			{
				tag new_id = pick_tag_of_type( 'txst', GetTag( dlog, 1000 ), (tag_file *)0 );
				if( new_id != 0 )
					SetTag( dlog, 1000, 'txst', new_id );
				return 0;
			}
			else if( arg == 1001 )
			{
				tag_entry* e = get_entry( 'txst', GetTag( dlog, 1000 ) );
				if( e )
					TRevengeApp::OpenItem( e );
				return 0;
			}
			return -1;
		default:
			break;
	}
	
	return -1;
}

BEGINDISPATCH(TspriEditForm,TTagEditForm)
	DISPATCHTABLE(KXGControlClick,TspriEditForm::DoControlClick)
	DISPATCHTABLE(KXGListValue,TspriEditForm::DoListSelect)
	DISPATCHTABLE(KXGListSelect, TspriEditForm::DoListDblClick)
ENDDISPATCH


TspriEditForm::TspriEditForm( tag_entry* e ) : TTagEditForm( e )
{	
	def = alloc_sprite_type_definition( MAX_SPRITE_FRAMES, MAX_SPRITE_SEQUENCES, MAX_FRAMES_PER_SEQUENCE );
	assert( def );
	cur = alloc_sprite_type_definition( MAX_SPRITE_FRAMES, MAX_SPRITE_SEQUENCES, MAX_FRAMES_PER_SEQUENCE );
	assert( cur );
	cur_seq = -1;
	cur_frame = -1;
	cur_global_frame = -1;
}

TspriEditForm::~TspriEditForm()
{
	if( def )
		free_sprite_type_definition( def );
	if( cur )
		free_sprite_type_definition( cur );
}

long TspriEditForm::DoListDblClick( int arg, void * )
{
	int sel = GetInt( arg );
	if( sel < 0 )
		return -1;
		
	switch( arg )
	{
		case 1000:
		{
			if( def->num_frames < 1 )
				return 0;
			int sel = GetInt( 1000 );
			XGDialog::ModalDialog( KWindowSpriteFrame, EditFrameProc, &def->frames[sel] );
			load_frame_texture( &def->frames[sel] );
			SetBitmapFrame( 1003, sel );
			cur_global_frame = sel;
			if( cur_seq > -1 && cur_frame > -1 && def->sequences[cur_seq].frames[cur_frame] == sel )
				SetBitmapFrame( 1013, sel );
		}
		return 0;
	}	
	return -1;
}

long TspriEditForm::DoListSelect( int arg, void * )
{
	int sel = GetInt( arg );
	switch( arg )
	{
		case 1000:
			cur_global_frame = sel;
			SetBitmapFrame( 1003, sel );
			return 0;
		case 1004:
			// load sequence stuff.
			// Save the sequence info and the frame.
			GetSequenceInfo( cur_seq );
			SetSequenceInfo( sel );
			cur_seq = sel;
			if( cur_frame > -1 && cur_seq > -1 )
				SetBitmapFrame( 1013, def->sequences[cur_seq].frames[cur_frame] );
			return 0;
		case 1007:
			// load frame stuff.
			GetFrameInfo( cur_seq, cur_frame );
			SetFrameInfo( GetInt(1004), sel );
			cur_frame = sel;
			if( cur_frame > -1 && cur_seq > -1 )
				SetBitmapFrame( 1013, def->sequences[cur_seq].frames[cur_frame] );
			return 0;
	};
	
	return -1;
}

long TspriEditForm::DoControlClick( long arg, void* ptr )
{
#ifdef __MWERKS__
#pragma unused( ptr )
#endif
	
	switch( arg )
	{
		case 1001: // add a frame.
		{
			if( def->num_frames == MAX_SPRITE_FRAMES )
				return 0;
			
			sprite_frame_definition* f = &def->frames[def->num_frames];
				
			memset( f, 0, sizeof(sprite_frame_definition) );
			f->xy[0][0] = -0.15;
			f->xy[0][1] = -0.15;
			f->xy[1][0] = 0.15;
			f->xy[1][1] = -0.15;
			f->xy[2][0] = 0.15;
			f->xy[2][1] = 0.15;
			f->xy[3][0] = -0.15;
			f->xy[3][1] = 0.15;
			
			f->st[0][0] = 0;
			f->st[0][1] = 0;
			f->st[1][0] = 1;
			f->st[1][1] = 0;
			f->st[2][0] = 1;
			f->st[2][1] = 1;
			f->st[3][0] = 0;
			f->st[3][1] = 1;
			
			bool ok = XGDialog::ModalDialog( KWindowSpriteFrame, EditFrameProc, &def->frames[def->num_frames] );
			if( ok )
			{
				def->num_frames++;
				LoadFrameList();
				SetInt( 1000, def->num_frames-1 );
				load_frame_texture( &def->frames[def->num_frames-1] );
				SetBitmapFrame( 1003, def->num_frames-1 );
			}
		}
		return 0;
		
		case 1002: // remove selected frame.
		{
			if( def->num_frames < 1 )
				return 0;
			int sel = GetInt( 1000 );
			if( sel < 0 )
				return 0;
			RemoveFrameFromSequences( sel );
			
			def->num_frames--;
			if( def->num_frames > 0 && sel != def->num_frames)
			{
				memmove( &def->frames[sel], &def->frames[sel+1], sizeof(int)*(def->num_frames-sel) );
			}
			
			int old_frame = cur_global_frame;
			LoadFrameList();
			if( cur_seq > -1 )
				SetSequenceInfo( cur_seq );
			if( old_frame < def->num_frames )
				SetInt(1000, old_frame); // move to previous frame.
		}
		return 0;
		
		case 1024: // duplicate.
		{
			int sel = GetInt( 1000 );
			if( sel < 0 )
				return 0;
			if( def->num_frames == MAX_SPRITE_FRAMES )
				return 0;
				
			sprite_frame_definition* d1, *d2;
			
			d1 = &def->frames[sel];
			d2 = &def->frames[def->num_frames++];
			
			memcpy( d2, d1, sizeof(sprite_frame_definition) );
			d2->data = 0;
			load_frame_texture( d2 );
			
			LoadFrameList();
			SetInt( 1000, def->num_frames-1 );
			SetBitmapFrame( 1003, def->num_frames-1 );
		}
		return 0;
		
		case 1016: // edit selected frame.
		{
			if( def->num_frames < 1 )
				return 0;
			int sel = GetInt( 1000 );
			XGDialog::ModalDialog( KWindowSpriteFrame, EditFrameProc, &def->frames[sel] );
			load_frame_texture( &def->frames[sel] );
			SetBitmapFrame( 1003, sel );
			if( cur_seq > -1 && cur_frame > -1 && def->sequences[cur_seq].frames[cur_frame] == sel )
				SetBitmapFrame( 1013, sel );
		}
		return 0;
		
		case 1022: // add to sequence.
		{
			if( def->num_frames < 1 )
				return 0;
			if( def->num_sequences < 1 )
				return 0;
			int frame = GetInt( 1000 );
			int seq_num = GetInt( 1004 );
			
			sprite_sequence_definition* seq_def = &def->sequences[seq_num];
			if( seq_def->num_frames == MAX_FRAMES_PER_SEQUENCE )
				return 0;
			
			int num = seq_def->num_frames;
			seq_def->frames[num] = frame;
			seq_def->ticks[num] = 250;
			seq_def->rots[num] = 0.0f;
			seq_def->colors[num<<2] = 1.0f;
			seq_def->colors[(num<<2)+1] = 1.0f;
			seq_def->colors[(num<<2)+2] = 1.0f;
			seq_def->colors[(num<<2)+3] = 1.0f;
			seq_def->sound_tags[num] = -1;
			seq_def->scales[num] = 1.0f;
			seq_def->num_frames++;
			
			LoadSequenceFrameList( seq_num );
			SetInt( 1007, num );
			cur_frame = num;
			SetFrameInfo( seq_num, num );
			SetBitmapFrame( 1013, def->sequences[cur_seq].frames[cur_frame] );
		}
		return 0;
		
		case 1005: // new sequence.
		{
			if( def->num_sequences == MAX_SPRITE_SEQUENCES )
				return 0;
			sprite_sequence_definition* seq_def = &def->sequences[def->num_sequences];
			seq_def->num_frames = 0;
			seq_def->flags = _sprite_sequence_repeats_finite_flag;
			seq_def->probability = 100;
			seq_def->time = 1;
			
			def->num_sequences++;
			LoadSequenceList();	
			SetInt( 1004, def->num_sequences-1 );
			SetSequenceInfo( def->num_sequences-1 );	
		}
		return 0;
		
		case 1006:
		{
			if( def->num_sequences < 1 )
				return 0;
			def->num_sequences--;
			LoadSequenceList();
			SetSequenceInfo( 0 );
		}
		return 0;
		
		case 1008: // remove a frame from a sequence.
		{
			if( def->num_sequences < 1 )
				return 0;
			int seq_num = GetInt(1004);
			sprite_sequence_definition* seq_def = &def->sequences[seq_num];
			if( seq_def->num_frames < 1 )
				return 0;
			seq_def->num_frames--;
			int n = GetInt(1007);
			if( seq_def->num_frames > 1 )
			{
				memmove( &seq_def->frames[n], &seq_def->frames[n+1], sizeof(int)*(seq_def->num_frames-n) );
				memmove( &seq_def->ticks[n], &seq_def->ticks[n+1], sizeof(int)*(seq_def->num_frames-n) );
				memmove( &seq_def->rots[n], &seq_def->rots[n+1], sizeof(float)*(seq_def->num_frames-n) );
				memmove( &seq_def->colors[n<<2], &seq_def->colors[(n+1)<<2], sizeof(float)*(seq_def->num_frames-n)*4 );
				memmove( &seq_def->scales[n], &seq_def->scales[n+1], sizeof(float)*(seq_def->num_frames-n) );
			}
			
			LoadSequenceFrameList(seq_num);
			SetFrameInfo(seq_num, 0 );
		}
		return 0;
	}
	
	return -1;
}

void TspriEditForm::CommitToTag()
{
	GetValues();
	save_sprite_type_definition( def, fEntry );
}

void TspriEditForm::ReloadTag()
{
	read_sprite_type_definition( def, fEntry, true );
	SetValues();
}

bool TspriEditForm::NeedCommit()
{
	GetValues();
	read_sprite_type_definition( cur, fEntry, true );
	return sprite_type_definitions_differ( cur, def );
}

void TspriEditForm::LoadFrameList( void )
{
	int i;
	XGListControl* list;

	list = dynamic_cast<XGListControl*>(GetWindow()->FindViewByID(1000));
	assert( list );
	list->DeleteAll();
	{
		char string[32];
		for(i = 0; i < def->num_frames; i++)
		{
			sprintf(string, "frame %d", i );
			list->Insert(i, string);
		}
		list->SetSelect(0, TRUE);
		cur_global_frame = GetInt( 1000 );
	}
}

void TspriEditForm::LoadSequenceList( void )
{
	int i;
	XGListControl* list;

	list = dynamic_cast<XGListControl*>(GetWindow()->FindViewByID(1004));
	assert( list );
	list->DeleteAll();
	{
		char string[32];
		for(i = 0; i < def->num_sequences; i++)
		{
			sprintf(string, "sequence %d", i);
			list->Insert(i, string);
		}
		list->SetValue(0);
		if( def->num_sequences > 0 )
			cur_seq = 0;
		else
			cur_seq = -1;
	}
}

void TspriEditForm::SetValues()
{
	LoadFrameList();
	LoadSequenceList();	
	SetSequenceInfo( 0 );
	SetInt( 1023, (def->flags & _sprite_def_dies_at_end_of_sequence_flag) ? 1 : 0 );
}

void TspriEditForm::GetValues()
{
	GetSequenceInfo( GetInt(1004) );
	def->flags = 0;
	if( GetInt( 1023 ) )
		def->flags |= _sprite_def_dies_at_end_of_sequence_flag;
}

void TspriEditForm::SetFrameInfo( int seq, int frame )
{
	if( seq < def->num_sequences && frame > -1 && seq > -1 )
	{
		sprite_sequence_definition* seq_def = &def->sequences[seq];
		SetInt(1014, seq_def->ticks[frame]);
		SetFloat(1015, seq_def->rots[frame]);
		SetFloat(1017, seq_def->colors[frame<<2]);
		SetFloat(1018, seq_def->colors[(frame<<2)+1]);
		SetFloat(1019, seq_def->colors[(frame<<2)+2]);
		SetFloat(1020, seq_def->colors[(frame<<2)+3]);
		SetFloat(1100, seq_def->scales[frame]);
	}
	else
	{
		SetText( 1014, "" );
		SetText( 1015, "" );
		SetText( 1017, "" );
		SetText( 1018, "" );
		SetText( 1019, "" );
		SetText( 1020, "" );
		SetText( 1100, "" );
		cur_frame = -1;
	}
		
}

void TspriEditForm::GetFrameInfo( int seq, int frame )
{
	if( seq < def->num_sequences && seq > -1 && frame > -1 )
	{
		sprite_sequence_definition* seq_def = &def->sequences[seq];
		seq_def->ticks[frame] = GetInt( 1014 );
		seq_def->rots[frame] = GetFloat( 1015 );
		seq_def->colors[(frame<<2)] = GetFloat( 1017 );
		seq_def->colors[(frame<<2)+1] = GetFloat( 1018 );
		seq_def->colors[(frame<<2)+2] = GetFloat( 1019 );
		seq_def->colors[(frame<<2)+3] = GetFloat( 1020 );
		seq_def->scales[frame] = GetFloat( 1100 );
	}
}

void TspriEditForm::LoadSequenceFrameList( int seq )
{
	if( seq < def->num_sequences && seq > -1 )
	{
		sprite_sequence_definition* seq_def = &def->sequences[seq];
		// load the list with the frame list.
		XGListControl* list = dynamic_cast<XGListControl*>(GetWindow()->FindViewByID(1007));
		assert( list );
		
		list->DeleteAll();
		{
			int i;
			char string[32];
			for(i = 0; i < seq_def->num_frames; i++)
			{
				sprintf(string, "frame %d", seq_def->frames[i]);
				list->Insert(i, string);				
			}
			
			list->SetValue(0);
			if( seq_def->num_frames > 0 )
				cur_frame = 0;
			else
				cur_frame = -1;
		}
	}
}

void TspriEditForm::SetSequenceInfo( int seq )
{
	if( seq < def->num_sequences && seq > -1 )
	{
		sprite_sequence_definition* seq_def = &def->sequences[seq];
		
		LoadSequenceFrameList( seq );
		
		SetInt( 1009, (seq_def->flags&_sprite_sequence_repeats_forever_flag) ? 1 : 0 );
		SetInt( 1010, (seq_def->flags&_sprite_sequence_repeats_finite_flag) ? 1 : 0 );
		SetInt( 1011, (seq_def->flags&_sprite_sequence_expires_by_time_flag) ? 1 : 0 );
		
		SetInt( 1012, seq_def->time );
		SetInt( 1021, seq_def->probability );
		
		SetFrameInfo( seq, cur_frame );
	}
	else
	{
		XGListControl* list = dynamic_cast<XGListControl*>(GetWindow()->FindViewByID(1007));
		assert( list );
		list->DeleteAll();
	}
}

void TspriEditForm::GetSequenceInfo( int seq )
{
	if( seq < def->num_sequences && seq > -1 )
	{
		sprite_sequence_definition* seq_def = &def->sequences[seq];
		
		if( GetInt( 1009 ) )
			seq_def->flags = _sprite_sequence_repeats_forever_flag;
		else
		{
			if( GetInt( 1010 ) )
				seq_def->flags = _sprite_sequence_repeats_finite_flag;
			else
				seq_def->flags = _sprite_sequence_expires_by_time_flag;
		}
		
		seq_def->time = GetInt( 1012 );
		seq_def->probability = GetInt( 1021 );
		
		GetFrameInfo( seq, cur_frame );
	}
}

void TspriEditForm::RemoveFrameFromSequences( int frame )
{
	int i, k;
	sprite_sequence_definition* seq_def;	
	
	for( i = 0; i < def->num_sequences; i++ )
	{
		seq_def = &def->sequences[i];
		for(k = 0; k < seq_def->num_frames;)
		{
			if( seq_def->frames[k] == frame )
			{
				// woah cowboy, back 'dat ass up!
				seq_def->num_frames--;
				if( seq_def->num_frames > 0 && k != seq_def->num_frames )
				{
					memmove( &seq_def->frames[k], &seq_def->frames[k+1], sizeof(int)*(seq_def->num_frames-k) );
					memmove( &seq_def->ticks[k], &seq_def->ticks[k+1], sizeof(int)*(seq_def->num_frames-k) );
					memmove( &seq_def->rots[k], &seq_def->rots[k+1], sizeof(float)*(seq_def->num_frames-k) );
					memmove( &seq_def->colors[k<<2], &seq_def->colors[(k+1)<<2], sizeof(float)*(seq_def->num_frames-k)*4 );
					
				}
			}
			else
			{
				if( seq_def->frames[k] > frame )
					seq_def->frames[k]--;	// we're moving back one.
				k++;
			}
		}		
	}
}

void TspriEditForm::RemoveSequence( int seq )
{
	if( seq >= def->num_sequences )
		return;
	
	def->num_sequences--;
		
	if( def->num_sequences > 0 && seq != def->num_sequences )
	{
		// move back.
		memmove( &def->sequences[seq], &def->sequences[seq+1], sizeof(sprite_sequence_definition)*(def->num_sequences-seq) );
	}
}
	
void TspriEditForm::SetBitmapFrame( int view_id, int frame_num )
{
	assert( frame_num < def->num_frames );
	sprite_frame_definition* frame = &def->frames[frame_num];
	
	if( !frame->data )
		return;
		
	TBitmapView* bmview = dynamic_cast<TBitmapView*>(GetWindow()->FindViewByID(view_id));
	assert( bmview );
	
	switch( frame->channels )
	{
		case 1:
			bmview->SetBitmapAlpha( frame->data, frame->width, frame->height );
			break;
		case 3:
			bmview->SetBitmapRGB( frame->data, frame->width, frame->height );
			break;
		case 4:
			bmview->SetBitmapRGBA( frame->data, frame->width, frame->height );
			break;
	}
	
	bmview->InvalView();
	bmview->UpdateView();
}
