/*
** DITLMAKER.CPP
** I am the leprichan!
*/
#include "ditlmaker.h"
#include "tags.h"
#include "byte_swapping.h"
#include <xfile.h>
#include <xview.h>
#include <xdatautil.h>
#include <stdio.h>
#include <assert.h>

/* all controls in the .cin have these basics.
{
	[12 bytes of stuff]
	[fourcc] - 4
	[viewid] - 4
	[bool] - 2
	[bool] - 2
	[bool] - 2
	[bool] - 2
	[bool] - 2
	[bool] - 2
	[bool] - 2
	[bool] - 2
}

these are all big endian which i blame squarely on those skanky ass macs.
all we need is the viewid, fourcc, and positions.

these are at the following byte offsets of the resources.
12 - fourcc
16 - viewid
32 - left
36 - top
40 - right
44 - bottom
*/

/*
cin fourcc idents.
*/
enum
{
	_vres = 'VRes',
	_wind = 'wind',
	_static_text = 'text',
	_edit_box = 'edit',
	_button = 'pbtn',
	_check_box = 'chkb',
	_radio_button = 'radb',
	_group_box = 'grou',
	_list_box = 'list'
};

static byte_swap_code _dialog_header_bs_data[]= {
	_begin_bs_array, 1,
	_2byte, // version
	_2byte, // control_count
	_2byte, _2byte, _2byte, _2byte, // bounds
	
	_end_bs_array
};

static byte_swap_code control_bs_data[]= {
	_begin_bs_array, 1,
	_4byte, // tag
	_2byte, _2byte, // type, item_id	
	_2byte, _2byte, // bitmap_ids
	_2byte, // flags
	_2byte, // state
	_2byte, _2byte, _2byte, _2byte, // bounds
	
	_end_bs_array
};

typedef struct
{
	short version;
	short control_count;
	rectangle2d bounds;
} dialog_t;

typedef struct
{
	unsigned long tag;
	short padding;
	short item_id;
	short bitmap_ids[2];
	word flags;
	word state;
	rectangle2d bounds;
} control_t;

#define MAX_CONTROLS	256
static int c_block_size=0;
static char string_block[MAX_CONTROLS*256];

int StoreString( XGHandle& data, int off )
{
	unsigned char len = (unsigned char)data.GetChar(off);
	if( len == 0 )
	{
		string_block[c_block_size] = 0;
		c_block_size++;
		return 2;
	}
	
	data.GetMemory( off+1, len, &string_block[c_block_size] );
	c_block_size += len+1; // +1 for null.
	//len += (len&1);
	len += 1;
	len += (len&1);
	return len;
}

void StoreEmptyString()
{
	string_block[c_block_size] = 0;
	c_block_size++;
}

int LoadStaticText( control_t* ctrl, XGHandle& data, int off )
{
	if( ctrl->item_id == 888 )
		ctrl->tag = 'game';	// game graphic control.
	if( ctrl->item_id == 1099 )
		ctrl->tag = 'repo';
	if( ctrl->item_id == 2222 )
		ctrl->tag = 'prol';
	return StoreString( data, off ) + 4;	
}

int LoadEditText( control_t* ctrl, XGHandle& data, int off )
{
	return StoreString( data, off ) + 26;
}

int LoadButton( control_t* ctrl, XGHandle& data, int off )
{
	return StoreString( data, off+8 ) + 8;
}

int LoadScrollBar( control_t* ctrl, XGHandle& data, int off )
{
	StoreEmptyString();
	return 20;
}

/*
int LoadColorPicker( control_t* ctrl, XGHandle& data, int off )
{
	StoreEmptyString();	
	return 14;
}
*/

int LoadUpDownButtonThingy( control_t* ctrl, XGHandle& data, int off )
{
	StoreEmptyString();
	return 12;
}

int LoadPopup( control_t* ctrl, XGHandle& data, int off )
{
	StoreEmptyString();
	return 4;
}

int LoadHeader( control_t* ctrl, XGHandle& data, int off )
{
	if( ctrl->item_id == 1000 || ctrl->item_id == 2000)
		ctrl->tag = 'colr';
	if( ctrl->item_id == 1042 )
		ctrl->tag = 'ched';	// entry point list box.
	
	StoreEmptyString();
	return 14;
}

int LoadPicture( control_t *ctrl, XGHandle &data, int off)
{
	StoreEmptyString();
	return 12;
}

int LoadProgressBar( control_t *ctrl, XGHandle &data, int off)
{
	StoreEmptyString();
	return 12;
}

int LoadListBox( control_t* ctrl, XGHandle& data, int off )
{
	if( ctrl->item_id == 666 )
		ctrl->tag = 'lstm';	// entry point list box.
	if( ctrl->item_id == 999 )
		ctrl->tag = 'lstg'; // tag list box.
	if( ctrl->item_id == 555 )
		ctrl->tag = 'plli'; // player list box.
	if( ctrl->item_id == 556 )
		ctrl->tag = 'pbli'; // buddy list box.
	if( ctrl->item_id == 557 )
		ctrl->tag = 'poli'; // order list box.
	if( ctrl->item_id == 444 )
		ctrl->tag = 'chat'; // dialog chat list box.
	if( ctrl->item_id == 777 )
		ctrl->tag = 'roli'; // room list box.
	if( ctrl->item_id == 888 )
		ctrl->tag = 'gaml'; // game list box.
	if( ctrl->item_id == 1040 )	//	Carnage stats list
		ctrl->tag = 'cstt';
	if( ctrl->item_id == 1041 )	//	Carnage graph
		ctrl->tag = 'cgrf';
	if( ctrl->item_id == 1099 || ctrl->item_id == 1098 )
		ctrl->tag = 'slst';
	if( ctrl->item_id == 6969 )
		ctrl->tag = 'kbls';
	if( ctrl->item_id == 4444 )
		ctrl->tag = 'game';
	if( ctrl->item_id == 4000 )
		ctrl->tag = 'logo';
	if( ctrl->item_id == 4010 )	// play list in game
		ctrl->tag = 'plig';
		
	StoreEmptyString();
	return 14;
}

typedef int (*load_control_proc_t)(control_t* ctrl, XGHandle& data, int off);

typedef struct
{
	unsigned long cin;
	unsigned long tag;
	load_control_proc_t load_proc;
} control_loader_t;

control_loader_t control_types[] = 
{
	{'pbtn', 'butt', LoadButton},
	{'text', 'stat', LoadStaticText},
	{'chkb', 'chek', LoadButton},
	{'edit', 'edit', LoadEditText},
	{'list', 'list', LoadListBox},
	{'scrb', 'slid', LoadScrollBar},
//	{'head', 'colr', LoadColorPicker},
	{'stsb', 'numb', LoadUpDownButtonThingy},
	{'popm', 'popu', LoadPopup},
	{'head', 'head', LoadHeader},
	{'pict', 'bitm', LoadPicture},
	{'stat', 'prog', LoadProgressBar}
};

static control_t control_set[MAX_CONTROLS];

void AssertResHeader( XGHandle& data )
{
	int id;
	data.GetMemory(0,4,(unsigned char*)&id);
	assert( id == NSwapLong('@!YC') );
	id = data.GetLong(4);
	assert( id == 2 );
}

int FirstResourceOffs( XGHandle& data )
{
	assert(data.GetSize() > 16);
	return 16;
}

int NextResourceOffs( XGHandle& data, int off )
{
	int len = data.GetLong(off);
	assert( len >= 10 );
	off += len;
	if( off >= data.GetSize() ) return 0;	// so we can quit.
	return off;	
}

control_loader_t* FindControlLoader( unsigned int type )
{
	int i, num;
	
	num = sizeof(control_types)/sizeof(control_loader_t);
	for(i = 0; i < num; i++)
	{
		if( control_types[i].cin == type )
			return &control_types[i];
	}
	
	return 0;
}

void WriteMythDialog( tag_file* tf, unsigned int id, dialog_t* dlog, char* name )
{
	unsigned char* data;
	assert( dlog->control_count < MAX_CONTROLS );
	int len = sizeof(dialog_t)+(dlog->control_count*sizeof(control_t))+c_block_size;
	data = (unsigned char*)malloc(len);
	assert( data );
	
	dlog->version = 1;
	byte_swap_move_be( "dlog header", data, dlog, sizeof(dialog_t), 1, _dialog_header_bs_data );
	byte_swap_move_be( "dlog controls", data+sizeof(dialog_t), control_set, sizeof(control_t), dlog->control_count, control_bs_data );
	memcpy( data+sizeof(dialog_t)+(dlog->control_count*sizeof(control_t)), string_block, c_block_size );
		
	tag_entry* e = get_entry( 0x62696e61, id );
	if( !e )
	{
		e = add_tag_entry( tf, 0x62696e61, id, 1, 
			name, 0, len, data, TRUE );
		assert( e );
	}
	else
	{
		set_entry_data( e, data, len, TRUE );
		set_entry_name( e, name );
	}
}

void ExtractViewResource( tag_file* tf, XGHandle& data, int off, int end )
{
	unsigned int id = data.GetLong(off+4);
	if( id != 'VRes' )
		return;
	
	c_block_size = 0;
	memset( string_block, 0, sizeof(string_block) );
	
	// skip the view resource.
	int str_len = (unsigned char)data.GetChar(off+10);
	char name[256];
	data.GetMemory( off+11, str_len, name );
	name[str_len] = 0;
	
	str_len += 5;//4+(str_len&1);
	
	off = off + 10 + str_len; // extra 4 there because i don't know why, but it's off.
	//int end = data.GetLong(off)+data.GetLong(off+4);
	id = data.GetLong(off+8);
	assert( id == 'wind' );
	int num_controls = 0;
	dialog_t dlog;
	unsigned int dlog_id = 0;//data.GetLong(off+16);
	
	if( dlog_id == 0 )
		dlog_id = new_tag_id_from_string( name, 0x62696e61, NULL );
		
	dlog.bounds.left = data.GetLong(off+22);
	dlog.bounds.top = data.GetLong(off+26);
	dlog.bounds.right = dlog.bounds.left+data.GetLong(off+46);
	dlog.bounds.bottom = dlog.bounds.top+data.GetLong(off+50);
	
	/* skip dlog */
	str_len = data.GetChar(off+70)+1;
	str_len += 5;//4+2-(str_len&1);
	//end += off + str_len;
	
	off = off + 70 + str_len;
	control_t* ctrl;
	control_loader_t* ldr;
	int len;
	
	//end += off;
	
	while( off < end && num_controls < MAX_CONTROLS )
	{
		len = data.GetLong(off);
		id = data.GetLong(off+12);
		ldr = FindControlLoader(id);
		if(!ldr)
			return;
			
		ctrl = &control_set[num_controls++];
		ctrl->tag = ldr->tag;
		ctrl->item_id = data.GetLong(off+16);
		ctrl->flags = 0;
		ctrl->state = 0;
		
		ctrl->bounds.left = data.GetLong(off+32);
		ctrl->bounds.top = data.GetLong(off+36);
		ctrl->bounds.right = data.GetLong(off+40);
		ctrl->bounds.bottom = data.GetLong(off+44);
		
		/*
		myth3 wants the bottom corners.
		*/
		int height = 0;//(ctrl->bounds.bottom-ctrl->bounds.top)/2;
		ctrl->bounds.top += height;
		ctrl->bounds.bottom += height;
		
		ctrl->bitmap_ids[0] = 
		ctrl->bitmap_ids[1] = 0;
		
		if( ldr->load_proc )
			off += ldr->load_proc( ctrl, data, off+54 );
		if( len == 0 )
			break;
		//off += len;
		off += 54;
	}
	
	dlog.control_count = num_controls;
	WriteMythDialog( tf, dlog_id, &dlog, name );
}

void MakeDITLFromCIN( XGFileSpecifier& xgfilespec, tag_file* tf )
{
	FILE* fp = xgfilespec.OpenFile("r");
	assert( fp );
	
	fseek( fp, 0, SEEK_END );
	int len = ftell(fp);
	fseek( fp, 0, SEEK_SET );
	
	XGHandle data;
	data.SetSize( len );
	if( fread( *data, 1, len, fp ) != len )
		assert( 0 );
	fclose(fp);
		
	AssertResHeader( data );
	int k;
	int i = FirstResourceOffs( data );
	for(; i > 0; i = NextResourceOffs( data, i ) )
	{
		k = NextResourceOffs( data, i );
		if( k == 0 )
			k = len;
		ExtractViewResource( tf, data, i, k );
	}
}