/*	TAllTagForm.cpp
 *
 *		The project form object; this is the template for handling
 *	messages destined for my window object
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/

#include <XConfig.h>
#include <XMessages.h>
#include <XAppMenu.h>
#include <XStringUtil.h>
#include <XWindow.h>
#include <XAutoPtr.h>
#include <XError.h>
#include <XDialog.h>
#include <XPopup.h>
#include <XList.h>
#include <XDragDrop.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "RevengeResources.h"
#include "RevengeApp.h"
#include "TTreeView.h"
#include "TAllTagForm.h"
#include "TTagFileDocument.h"
#include "TTagEditForm.h"
#include "tags.h"
#include "texturestack.h"
#include "canm.h"
#include "formatters.h"
#include "tagpick.h"
#include "ctd.h"
#include "ditlmaker.h"

/************************************************************************/
/*																		*/
/*	Resource clipboard													*/
/*																		*/
/************************************************************************/

struct tag_ref {
	tag_file *file;
	tag type;
	tag_group *group;
	tag id;
	tag_entry *entry;
};

TAllTagForm* TAllTagForm::st_form=0;

void TAllTagForm::Show(bool s)
{
	if( !s )
		return;
	
	if( !st_form )
	{
		st_form = new TAllTagForm();
		st_form->AttachWindow( KWindowTagFile );
	}
	
	if( st_form )
	{
		st_form->ReloadTableContents();
		st_form->GetWindow()->SelectWindow();
	}
}

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*	TTagFileForm::TTagFileForm
 *
 *		make me
 */

TAllTagForm::TAllTagForm()
{
}

/*	TTagFileForm::~TTagFileForm
 *
 *		Destruct me
 */

TAllTagForm::~TAllTagForm()
{
}

/************************************************************************/
/*																		*/
/*	Events																*/
/*																		*/
/************************************************************************/

/*	TTagFileForm::DoFormInitialize
 *
 *		Initialize this form. Basically this updates the name of the
 *	window by calling the DoDocumentRenamed() method, and then sets
 *	up the various subviews
 */

void TAllTagForm::DoFormInitialize()
{
	DoDocumentRenamed();
	ReloadTableContents();
}

/*	TTagFileForm::DoDocumentRenamed
 *
 *		Handle the fact that the document was renamed by changing the
 *	name of my window
 */

void TAllTagForm::DoDocumentRenamed()
{
	XGWindow *window;
	
	window = GetWindow();
	if( window != NULL )
	{
		window->SetTitle("All Tag Files");
	}
}

bool TAllTagForm::DoQueryDestroyWindow()
{
	st_form=0;
	return true;
}

/************************************************************************/
/*																		*/
/*	Events																*/
/*																		*/
/************************************************************************/

BEGINDISPATCH(TAllTagForm,XGForm)
	DISPATCHTABLE(KXGGetMenuStatus,TAllTagForm::DoGetMenuStatus)
	DISPATCHTABLE(KXGDoMenuCommand,TAllTagForm::DoMenuCommand)
	DISPATCHTABLE(KEventTreeDoubleClick,TAllTagForm::DoDoubleClick)
	DISPATCHTABLE(KXGKeyEvent,TAllTagForm::DoKeyEvent)
	DISPATCHTABLE(KEventTreeCanDrop, TAllTagForm::DoTreeCanDrop)
	DISPATCHTABLE(KEventTreeDoDrop, TAllTagForm::DoTreeDrop)
ENDDISPATCH

/*	TTagFileForm::DoDoubleClick
 *
 *		We've had a double-click in the tree view. This responds
 *	by opening the appropriate resource
 */

long TAllTagForm::DoDoubleClick(long, void *ptr)
{
	TreeEventRecord *eptr = (TreeEventRecord *)ptr;
	TTreeView *tv;
	tag_ref ref;
		
	
	tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	if (!tv) return 0;
	if (sizeof(ref) != tv->GetExtraData(eptr->child,&ref,sizeof(ref))) return 0;
	if (ref.entry) {
		//TRevengeApp::OpenTagFileWindow( ref.file, FALSE );
		TRevengeApp::OpenItem(ref.entry, eptr->ev_flags&KKeyControl);
		return 0;
	}
	return 0;
}

/*	TTagFileForm::DoGetMenuStatus
 *
 *		Get the menu status for this
 */

long TAllTagForm::DoGetMenuStatus(long arg, void *ptr)
{
	XGSMenuStatus *e = (XGSMenuStatus *)ptr;
	TTreeView *tv;
	
	switch (arg) {
		case KMenuResourceCreate:
		case KXGFileSave:
		case KXGFileSaveAs:
		case KXGFileRevert:		
		case KXGEditCut:
		case KXGEditClear: 
		case KXGEditCopy:
		case KXGEditPaste:
		case KMenuMeshNewColormap:
		case KMenuImportCameraMotion:
		case KMenuMeshCollapseDetail:
		case kMenuImportCINFile:

			e->enable = false;
			return 0;
			
		case KMenuResourceOpen:
		case KMenuResourceInfo:
			tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
			e->enable = (tv->GetFirstSelected() != 0);
			break;
			
		case KMenuFindDuplicates:
			tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID( 1 ));
			e->enable = (tv->GetSelectionCount() <= 1);
			return 0;
			
		default:
			break;
	}
	return -1;
}

/*	TTagFileForm::DoMenuCommand
 *
 *		Do the menu command
 */
 
 
void TAllTagForm::DoToAllSelectedEntries( void (TAllTagForm::* proc)( tag_entry *e ) )
{
	TTreeView *tv;
	uint32 item;
	tag_ref ref;
	tag_entry *e;
	tag_entry *next;
	tag selgroups[128];
	int selgroupcount;
	int i;
	
	tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	if (!tv) return;
	
	selgroupcount = 0;
	for (item = tv->GetFirstSelected(); item; item = tv->GetNextSelected( item )) {
		if (sizeof(ref) != tv->GetExtraData(item,&ref,sizeof(ref))) continue;
		if (ref.entry == 0 && ref.group != 0) {
			selgroups[selgroupcount++] = ref.type;
			for (e = get_next_entry_in_group( ref.group, 0 ); e; e = next) {
				next = get_next_entry_in_group( ref.group, e );
				(this->*proc)( e );
			}
		}
	}
	for (item = tv->GetFirstSelected(); item; item = tv->GetNextSelected( item )) {
		if (sizeof(ref) != tv->GetExtraData(item,&ref,sizeof(ref))) continue;
		if (ref.entry != 0) {
			for (i = 0; i < selgroupcount; ++i) {
				if (selgroups[i] == ref.type) goto entrydone;
			}
			(this->*proc)( ref.entry );
		}
entrydone:;
	}
}
 

long TAllTagForm::DoMenuCommand(long arg, void *)
{
	TTreeView *tv;
	uint32 item;
	tag_ref ref;
	tag_entry *e;

	tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	if (!tv) return 0;
	
	switch (arg) {
		case KMenuResourceCreate:
			return 0;
		case KMenuResourceOpen:
			for (item = tv->GetFirstSelected(); item; item = tv->GetNextSelected(item)) {
				if (sizeof(ref) != tv->GetExtraData(item,&ref,sizeof(ref))) continue;
				e = ref.entry;
				if (e) {
					TRevengeApp::OpenItem( e );
				}
			}
			return 0;
		case KMenuResourceInfo:
			for (item = tv->GetFirstSelected(); item; item = tv->GetNextSelected(item)) {
				if (sizeof(ref) != tv->GetExtraData(item,&ref,sizeof(ref))) continue;
				e = ref.entry;
				if (e) {
					GetInfoItem( e );
				}
			}
			return 0;
		case KXGEditClear:
		case KXGEditCut:
		case KXGEditCopy:
		case KMenuFindDuplicates:
		case KMenuMeshNewColormap:
		case KMenuImportCameraMotion:
		case KXGFileSave:
		case KXGFileSaveAs:
		case KXGEditPaste:
		case KMenuMeshCollapseDetail:
		case kMenuImportCINFile:
			return 0;
				
		default:
			break;
	}
	return -1;
}


long TAllTagForm::DoKeyEvent( long arg, void * )
{
	return -1;
}

long TAllTagForm::DoTreeCanDrop( long , void* ptr )
{
	return 0;
}

long TAllTagForm::DoTreeDrop( long , void* ptr )
{
	return 1;
}

/************************************************************************/
/*																		*/
/*	Support																*/
/*																		*/
/************************************************************************/

/*	TTagFileForm::ReloadTableContents
 *
 *		This is used to reload the contents of my table with the
 *	contents of my project
 */

struct group_mark
{
	tag group;
	uint32 par;
};

group_mark* find_group_mark( tag group, group_mark* mark_sets, int* num_sets, int max_sets )
{
	int i, count;
	group_mark* mark;
	
	count = *num_sets;
	for( mark=mark_sets, i=0; i<count; i++, mark++ )
	{
		if( mark->group == group )
			return mark;
	}
	
	// not found add one?
	if( count < max_sets )
	{
		mark_sets[count].group = group;
		mark_sets[count++].par = -1;
		*num_sets = count;
		return &mark_sets[count-1];
	}
	
	return 0;	
}

void TAllTagForm::ReloadTableContents(bool enableflag)
{
	TTreeView *tv;
	uint32 par;
	char name[256];
	tag_group* group;
	tag_entry* entry;
	tag open_groups[128];
	int open_group_count = 0;
	tag_ref ref;
	tag_file* tf;
	group_mark marks[256];
	group_mark* m;
	int num_marks;
	
	num_marks = 0;
	
	tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	
	if (tv) {
		tv->DisableDrawing();
		
		for (TableEntryRecord *r = (TableEntryRecord *)tv->GetItemHandle( 0, 0 ); r && open_group_count < 128; r = r->next) {
			if (r->fOpen) {
				if (tv->GetExtraData( (uint32)r, &ref, sizeof(ref) ) == sizeof(ref) && ref.type) {
					open_groups[open_group_count++] = ref.type;
				}
			}
		}
		
		tv->DeleteAll();
		
		//basePar = tv->AppendItem(0,0,"TagFile Database", 130);
		
		for( tf = get_next_tag_file( 0 ); tf != 0; tf = get_next_tag_file( tf )  )
		{
			ref.file = tf;
			
			for(group = get_next_group_in_file(tf, 0); group != 0; group = get_next_group_in_file(tf, group))
			{
				tag type = get_group_type(group);
				
				// need to add?
				m = find_group_mark( type, marks, &num_marks, 256 );
				assert( m );
				if( m->par == -1 )
				{
					par = tv->AppendItem(0, 0, get_plural_name_for_type(type), 135);
					m->par = par;
				}
				else
					par = m->par;
					
				ref.type = type;
				ref.group = group;
				ref.id = 0;
				ref.entry = 0;
				tv->SetExtraData( par, &ref, sizeof(ref) );
				
				short plainicon = get_type_editor( get_group_type(group) ) ? 130 : 131;
				short changeicon = get_type_editor( get_group_type(group) ) ? 132 : 131;
				
				// add all children.
				for(entry = get_next_entry_in_group(group, 0); entry != 0; entry = get_next_entry_in_group(group, entry))
				{
					tag id = get_entry_id(entry);
					sprintf( name, "%s (%s) [%.fk]", get_entry_name(entry), tagstr(id), ((float)get_entry_size(entry) / 1000.0) );
					uint32 item = tv->AppendItem(par, 0, name, is_entry_modified(entry) ? changeicon : plainicon );
					ref.id = id;
					ref.entry = entry;
					tv->SetExtraData( item, &ref, sizeof(ref) );
				}
				
				tv->SortChildrenOf( par );
				
				for (int i = 0; i < open_group_count; ++i) {
					if (type == open_groups[i]) {
						tv->OpenItem( par, true );
						open_groups[i] = open_groups[--open_group_count];
						break;
					}
				}
			}
		}
		
		tv->SortChildrenOf( 0 );
		if (enableflag) tv->EnableDrawing();
	}
}

struct TagEntryCreationData {
	tag_file *file;
	char name[128];
	tag type;
	tag id;
};

void TAllTagForm::NoticeItem( tag_entry *e, bool exclusive )
{
	if (!e) return;
	TTreeView *tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	if (!tv) return;
	tag type = get_group_type(get_entry_group(e));
	if (!type) return;
	
	if (exclusive) tv->ClearSelection();
	
	for (TableEntryRecord *r = (TableEntryRecord *)tv->GetItemHandle( 0, 0 ); r; r = r->next) {
		tag_ref ref;
		if (tv->GetExtraData( (uint32)r, &ref, sizeof(ref) ) == sizeof(ref) && ref.type == type && ref.id == 0) {
			tv->OpenItem( (uint32)r, true );
			for (TableEntryRecord *r2 = (TableEntryRecord *)tv->GetItemHandle( (uint32)r, 0 ); r2; r2 = r2->next) {
				tag_ref ref;
				if (tv->GetExtraData( (uint32)r2, &ref, sizeof(ref) ) == sizeof(ref) && ref.entry == e) {
					tv->SetSelected( (uint32)r2, true );
					break;
				}
			}
		}
	}
}

/*	TTagFileForm::GetInfoItem
 *
 *		Get info about this item
 */

void TAllTagForm::GetInfoItem( tag_entry *entry )
{
	TagEntryCreationData cd;
	
	if( !entry || get_entry_group(entry) == 0 )
		return;
		
	strcpy(cd.name, get_entry_name(entry) );
	cd.id = get_entry_id( entry );
	cd.type = get_entry_type( entry );
	cd.file = get_entry_file( entry );
	
	if( XGDialog::ModalDialog( KWindowTagInfo, InfoDialogProc, &cd ) )
	{
		set_entry_name( entry, cd.name );
		set_entry_id( entry, cd.id );
		set_entry_group( entry, cd.type );
		
		ReloadTableContents();
		NoticeItem( entry, true );
		
		TTagEditForm* form = get_entry_form( entry );
		if(form)
			form->TagInfoChanged();
	}
}


static tag get_selected_type( XGDialog *dlog, TagEntryCreationData *cd )
{
//	cd->type = 0;
	XGListControl *list = dynamic_cast<XGListControl *>(dlog->FindViewByID(2));
	if (!list) {
		return cd->type;
	}

	int value = list->GetValue();
	if (value < 0) {
		return cd->type;
	}
	char name[256];
	list->GetItem( value, name );
	
	int i = 0;
	tag itype;
	
	while ((itype = get_indexed_type(i)) != 0) {
		if (strcmp( get_singular_name_for_type(itype), name ) == 0) {
			cd->type = itype;
			break;
		}
		++i;
	}
	return cd->type;
}

static tag update_id_field( XGDialog *dlog, TagEntryCreationData *cd )
{
	char idstr[40];
	tag id;
	int mode = 4;
	for (int i = 3; i <= 6; ++i) {
		if (dlog->GetValue( i )) {
			mode = i;
			break;
		}
	}
	
	dlog->GetText( 1, sizeof(cd->name), cd->name );
	dlog->GetText( 7, 40, idstr );
	
	if (mode == 6) {
		id = tag_type_from_string( idstr );
	} else {
		if (mode == 3) {
			id = new_tag_id_from_string( cd->name, 0, 0 );
		} else if (mode == 4) {
			id = new_tag_id_from_string( cd->name, get_selected_type( dlog, cd ), 0 );
		} else {	// mode == 5
			id = new_tag_id_from_string( cd->name, get_selected_type( dlog, cd ), cd->file );
		}
	}
	
	cd->id = id;
	if (strcmp( idstr, tagstr(id) ) != 0) {
		dlog->SetText( 7, tagstr(id) );
	}
	
	return id;
}

static tag update_info_field( XGDialog *dlog, TagEntryCreationData *cd )
{
	char idstr[40];
	tag id;
	int mode = 4;
	for (int i = 3; i <= 6; ++i) {
		if (dlog->GetValue( i )) {
			mode = i;
			break;
		}
	}
	
	dlog->GetText( 1, sizeof(cd->name), cd->name );
	dlog->GetText( 7, 40, idstr );
	
	if (mode == 6) {
		id = tag_type_from_string( idstr );
	} else {
		if (mode == 3) {
			id = new_tag_id_from_string( cd->name, 0, 0 );
		} else if (mode == 4) {
			id = new_tag_id_from_string( cd->name, cd->type, 0 );
		} else {	// mode == 5
			id = new_tag_id_from_string( cd->name, cd->type, cd->file );
		}
	}
	
	cd->id = id;
	if (strcmp( idstr, tagstr(id) ) != 0) {
		dlog->SetText( 7, tagstr(id) );
	}
	
	return id;
}

long TAllTagForm::NewTagDialogProc(XGDialog *dlog, void *data, long msg, long arg, void *)
{
	TagEntryCreationData *cd = (TagEntryCreationData *)data;
	XGListControl *types = dynamic_cast<XGListControl *>(dlog->FindViewByID(2));
	if (!cd || !types) return 0;
	
	int i, j, k;
	
	switch (msg) {
		case KXGDialogMessage:
			switch (arg) {
				case KXGDMInitDialog:
					dlog->SetText(1,cd->name);
					i = 0;
					j = 0;
					k = 0;
					tag itype;
					while ((itype = get_indexed_type(i)) != 0) {
						if (get_type_creator( itype ) != 0) {
							types->Insert( j, get_singular_name_for_type(itype) );
							if (cd->type == itype) k = j;
							++j;
						}
						++i;
					}
					dlog->SetValue(2,k);
					update_id_field( dlog, cd );
					return 0;
				default:
					break;
			}
			break;
		case KXGKeyClick:
			update_id_field( dlog, cd );
			break;
		case KXGControlClick:
			switch (arg) {
				case 3:
				case 4:
				case 5:
				case 6: {
						for (i = 3; i <= 6; ++i) {
							dlog->SetValue( i, i == arg );
						}
						XGView *id = dlog->FindViewByID( 7 );
						if (arg == 6) {
							id->EnableView();
							id->SetFocus();
						} else {
							id->DisableView();
						}
						update_id_field( dlog, cd );
					} break;
				case 100:
					/*
					 *	OK: get the values and return
					 */
					update_id_field( dlog, cd );
					dlog->EndDialog(1);
					return 0;
				case 101:
					dlog->EndDialog(0);
					return 0;
				default:
					break;
			}
			break;
		case KXGListValue:
			i = dlog->GetValue(2);
			dlog->SetEnabled( 100, i >= 0 );
			update_id_field( dlog, cd );
		default:
			break;
	}
	return -1;
}

/*	TTagFileForm::InfoDialogProc
 *
 *		Get info; this thing is like the above, but doesn't allow
 *	the type of the resource to be set.
 */
long TAllTagForm::InfoDialogProc(XGDialog *dlog, void *data, long msg, long arg, void *)
{
	TagEntryCreationData* tag_stuffs = static_cast<TagEntryCreationData*>(data);
	int i;
	
	if(tag_stuffs == 0)
		return -1;
		
	switch(msg)
	{
		case KXGDialogMessage:
			switch(arg)
			{
				case KXGDMInitDialog:
				
					dlog->SetText(1, tag_stuffs->name );
					dlog->SetText(7, tagstr(tag_stuffs->id));
					dlog->SetText(8, tagstr(tag_stuffs->type));
					update_info_field( dlog, tag_stuffs );
					//dlog->SetValue(6, true );
				break;				
				
				default:
					return -1;
				break;
			}
			
		break;
		
		case KXGKeyClick:
			update_id_field( dlog, tag_stuffs );
		break;
		
		case KXGControlClick:
			switch (arg) {
				case 3:
				case 4:
				case 5:
				case 6: {
						for (i = 3; i <= 6; ++i) {
							dlog->SetValue( i, i == arg );
						}
						XGView *id = dlog->FindViewByID( 7 );
						if (arg == 6) {
							id->EnableView();
							id->SetFocus();
						} else {
							id->DisableView();
						}
						update_info_field( dlog, tag_stuffs );
					} 
				break;
				
				case 101:
					/*
					 *	OK: get the values and return
					 */
					update_id_field( dlog, tag_stuffs );
					{
						char id[256];
						dlog->GetText( 8, 256, id );
						tag_stuffs->type = tag_type_from_string(id);
					}
					dlog->EndDialog(1);
				break;
					
				case 100:
					dlog->EndDialog(0);
				break;
				
				default:
					return -1;
				break;
			}
		break;
	}
	
	return 0;
}
