/*
   Copyright (C) 1999-2006 Id Software, Inc. and contributors.
   For a list of contributors, see the accompanying CONTRIBUTORS file.

   This file is part of GtkRadiant.

   GtkRadiant 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 of the License, or
   (at your option) any later version.

   GtkRadiant 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 GtkRadiant; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "plugin.h"

/*! \file plugin.cpp
    \brief HydraToolz!

    HydraToolz by Dominic Clifton - Hydra (Hydra@Hydras-World.com)

    Overview
    ========


    Version History
    ===============

    v0.1 - 28/May/2002
      - Initial version.


    ToDo
    ====

 * Code it ? :)

 */

// =============================================================================
// Globals

_QERFuncTable_1 g_FuncTable;
_QERFileSystemTable g_FileSystemTable;
_QEREntityTable g_EntityTable;

// cast to GtkWidget*
void *g_pMainWnd;

// =============================================================================
// Ripped from TexTool.cpp

static void dialog_button_callback( ui::Widget widget, gpointer data ){
	int *loop, *ret;

	auto parent = widget.window();
	loop = (int*)gtk_object_get_data( GTK_OBJECT( parent ), "loop" );
	ret = (int*)gtk_object_get_data( GTK_OBJECT( parent ), "ret" );

	*loop = 0;
	*ret = gpointer_to_int( data );
}

static gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
	int *loop;

	gtk_widget_hide( widget );
	loop = (int*)gtk_object_get_data( GTK_OBJECT( widget ), "loop" );
	*loop = 0;

	return TRUE;
}

int DoMessageBox( const char* lpText, const char* lpCaption, guint32 uType ){
	GtkWidget *w, *hbox;
	int mode = ( uType & MB_TYPEMASK ), ret, loop = 1;

	auto window = ui::Window( ui::window_type::TOP );
	window.connect( "delete_event",
						G_CALLBACK( dialog_delete_callback ), NULL );
	window.connect( "destroy",
						G_CALLBACK( gtk_widget_destroy ), NULL );
	gtk_window_set_title( window, lpCaption );
	gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );
	gtk_object_set_data( GTK_OBJECT( window ), "loop", &loop );
	gtk_object_set_data( GTK_OBJECT( window ), "ret", &ret );
	gtk_widget_realize( window );

	auto vbox = ui::VBox( FALSE, 10 );
	window.add(vbox);
	vbox.show();

	w = ui::Label( lpText );
	vbox.pack_start( w, FALSE, FALSE, 2 );
	gtk_label_set_justify( GTK_LABEL( w ), GTK_JUSTIFY_LEFT );
	w.show();

	w = gtk_hseparator_new();
	vbox.pack_start( w, FALSE, FALSE, 2 );
	w.show();

	hbox = ui::HBox( FALSE, 10 );
	vbox.pack_start( hbox, FALSE, FALSE, 2 );
	hbox.show();

	if ( mode == MB_OK ) {
		w = ui::Button( "Ok" );
		hbox.pack_start( w, TRUE, TRUE, 0 );
		w.connect( "clicked",
							G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
		gtk_widget_set_can_default( w, true );
		gtk_widget_grab_default( w );
		w.show();
		ret = IDOK;
	}
	else if ( mode ==  MB_OKCANCEL ) {
		w = ui::Button( "Ok" );
		hbox.pack_start( w, TRUE, TRUE, 0 );
		w.connect( "clicked",
							G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
		gtk_widget_set_can_default( w, true );
		gtk_widget_grab_default( w );
		w.show();

		w = ui::Button( "Cancel" );
		hbox.pack_start( w, TRUE, TRUE, 0 );
		w.connect( "clicked",
							G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
		w.show();
		ret = IDCANCEL;
	}
	else if ( mode == MB_YESNOCANCEL ) {
		w = ui::Button( "Yes" );
		hbox.pack_start( w, TRUE, TRUE, 0 );
		w.connect( "clicked",
							G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
		gtk_widget_set_can_default( w, true );
		gtk_widget_grab_default( w );
		w.show();

		w = ui::Button( "No" );
		hbox.pack_start( w, TRUE, TRUE, 0 );
		w.connect( "clicked",
							G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
		w.show();

		w = ui::Button( "Cancel" );
		hbox.pack_start( w, TRUE, TRUE, 0 );
		w.connect( "clicked",
							G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
		w.show();
		ret = IDCANCEL;
	}
	else /* if (mode == MB_YESNO) */
	{
		w = ui::Button( "Yes" );
		hbox.pack_start( w, TRUE, TRUE, 0 );
		w.connect( "clicked",
							G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
		gtk_widget_set_can_default( w, true );
		gtk_widget_grab_default( w );
		w.show();

		w = ui::Button( "No" );
		hbox.pack_start( w, TRUE, TRUE, 0 );
		w.connect( "clicked",
							G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
		w.show();
		ret = IDNO;
	}

	window.show();
	gtk_grab_add( window );

	while ( loop )
		gtk_main_iteration();

	gtk_grab_remove( window );
	window.destroy();

	return ret;
}

// End of rip from TexTool.cpp

// =============================================================================
// Ripped from cmdlib.cpp

/*
   ====================
   Extract file parts
   ====================
 */
void ExtractFilePath( const char *path, char *dest ){
	const char *src;

	src = path + strlen( path ) - 1;

//
// back up until a \ or the start
//
	while ( src != path && *( src - 1 ) != '/' && *( src - 1 ) != '\\' )
		src--;

	memcpy( dest, path, src - path );
	dest[src - path] = 0;
}

void ExtractFileName( const char *path, char *dest ){
	const char *src;

	src = path + strlen( path ) - 1;

//
// back up until a \ or the start
//
	while ( src != path && *( src - 1 ) != '/'
			&& *( src - 1 ) != '\\' )
		src--;

	while ( *src )
	{
		*dest++ = *src++;
	}
	*dest = 0;
}

void ConvertDOSToUnixName( char *dst, const char *src ){
	while ( *src )
	{
		if ( *src == '\\' ) {
			*dst = '/';
		}
		else{
			*dst = *src;
		}
		dst++; src++;
	}
	*dst = 0;
}

// End of rip from cmdlib.cpp

// =============================================================================
// Actual Plugin Code

// get the wad name from the shader name (or an actual wadname) and add to a list of wad names making
// sure we don't add duplicates.

GSList *AddToWadList( GSList *wadlist, const char *shadername, const char *wad ){
	char tmpstr[QER_MAX_NAMELEN];
	char *wadname;
	if ( !shadername && !wad ) {
		return wadlist;
	}

	if ( shadername ) {
		if ( strcmp( shadername,"color" ) == 0 ) {
			return wadlist;
		}
		ExtractFilePath( shadername,tmpstr );
		// Sys_Printf("checking: %s\n",shadername);

		int l = strlen( tmpstr ) - 1;

		if ( tmpstr[l] == '/' || tmpstr[l] == '\\' ) {
			tmpstr[l] = 0;
		}
		else
		{
			Sys_Printf( "WARNING: Unknown wad file for shader %s\n",shadername );
			return wadlist;
		}

		ExtractFileName( tmpstr,tmpstr );

		wadname = (char *)malloc( strlen( tmpstr ) + 5 );
		sprintf( wadname,"%s.wad",tmpstr );
	}
	else
	{
		wadname = strdup( wad );
	}

	for ( GSList *l = wadlist; l != NULL ; l = l->next )
	{
		if ( string_equal_nocase( (char *)l->data,wadname ) ) {
			free( wadname );
			return wadlist;
		}
	}

	Sys_Printf( "Adding Wad File to WAD list: %s (reason: ",wadname );
	if ( shadername ) {
		Sys_Printf( "see shader \"%s\")\n", shadername );
	}
	else{
		Sys_Printf( "already in WAD key. )\n" );
	}
	return ( g_slist_append( wadlist, wadname ) );
}

void UpdateWadKeyPair( void ){
	int i,nb;

	char wads[2048]; // change to CString usage ?
	wads[0] = 0;
	char *p1,*p2;
	entity_t *pEntity;
	epair_t *pEpair;
	GSList *wadlist = NULL;
	face_t  *f;
	brush_t *b;
	char cleanwadname[QER_MAX_NAMELEN];
	const char *actualwad;


	pEntity = (entity_t *)g_FuncTable.m_pfnGetEntityHandle( 0 ); // get the worldspawn ent

	Sys_Printf( "Searching for in-use wad files...\n" );
	for ( pEpair = pEntity->epairs; pEpair != NULL; pEpair = pEpair->next )
	{
		if ( string_equal_nocase( pEpair->key,"wad" ) ) {
			strcpy( wads,pEpair->value );
			ConvertDOSToUnixName( wads,wads );

			// ok, we got the list of ; delimited wads, now split it into a GSList that contains
			// just the wad names themselves.

			p1 = wads;

			do
			{
				p2 = strchr( p1,';' );
				if ( p2 ) {
					*p2 = 0; // swap the ; with a null terminator

				}
				if ( strchr( p1,'/' ) || strchr( p1,'\\' ) ) {
					ExtractFileName( p1,cleanwadname );
					wadlist = AddToWadList( wadlist, NULL, cleanwadname );
				}
				else
				{
					wadlist = AddToWadList( wadlist, NULL, p1 );
				}
				if ( p2 ) {
					p1 = p2 + 1; // point back to the remainder of the string
				}
				else{
					p1 = NULL; // make it so we exit the loop.

				}
			} while ( p1 );

			// ok, now we have a list of wads in GSList.
			// now we need to add any new wadfiles (with their paths) to this list
			// so scan all brushes and see what wads are in use
			// FIXME: scan brushes only in the region ?

			break; // we don't need to process any more key/pairs.
		}
	}

	nb = g_FuncTable.m_pfnAllocateActiveBrushHandles();
	for ( i = 0; i < nb; i++ )
	{
		b = (brush_t *)g_FuncTable.m_pfnGetActiveBrushHandle( i );
		if ( b->patchBrush ) { // patches in halflife ?
			wadlist = AddToWadList( wadlist, b->pPatch->pShader->getName(),NULL );
		}
		else
		{
			for ( f = b->brush_faces ; f ; f = f->next )
			{
				wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL );
			}
		}
	}
	g_FuncTable.m_pfnReleaseActiveBrushHandles();

	nb = g_FuncTable.m_pfnAllocateSelectedBrushHandles();
	for ( i = 0; i < nb; i++ )
	{
		b = (brush_t *)g_FuncTable.m_pfnGetSelectedBrushHandle( i );
		if ( b->patchBrush ) { // patches in halflife ?
			wadlist = AddToWadList( wadlist, b->pPatch->pShader->getName(),NULL );
		}
		else
		{
			for ( f = b->brush_faces ; f ; f = f->next )
			{
				wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL );
			}
		}
	}
	g_FuncTable.m_pfnReleaseSelectedBrushHandles();

	// Now we have a complete list of wadnames (without paths) so we just have to turn this
	// back to a ; delimited list.

	wads[0] = 0;
	while ( wadlist )
	{
		if ( string_equal_nocase( (char *)wadlist->data,"common-hydra.wad" ) ) {
			Sys_Printf( "Skipping radiant-supplied wad file %s\n",(char *)wadlist->data );
		}
		else
		{
			if ( wads[0] ) {
				strcat( wads,";" );
			}

			actualwad = vfsGetFullPath( (char *)wadlist->data );

			if ( actualwad ) {
				strcat( wads, actualwad );
			}
			else
			{
				Sys_Printf( "WARNING: could not locate wad file %s\n",(char *)wadlist->data );
				strcat( wads, (char *)wadlist->data );
			}
		}

		free( wadlist->data );
		wadlist = g_slist_remove( wadlist, wadlist->data );
	}

	// store the wad list back in the worldspawn.
	if ( wads[0] ) {
		//free(pEpair->value);
		//pEpair->value = strdup(wads);
		SetKeyValue( pEntity, "wad", wads );
	}

}

// =============================================================================
// PLUGIN INTERFACE STUFF

// plugin name
const char *PLUGIN_NAME = "Q3 Texture Tools";

// commands in the menu
const char *PLUGIN_COMMANDS = "About...;Create/Update WAD keypair";

const char *PLUGIN_ABOUT = "HydraToolz for GTKRadiant\n\n"
						   "By Hydra!";

extern "C" void* WINAPI QERPlug_GetFuncTable(){
	return &g_FuncTable;
}

const char* QERPlug_Init( void* hApp, void *pWidget ){
	GtkWidget* pMainWidget = static_cast<GtkWidget*>( pWidget );

	g_pMainWnd = pMainWidget;
	memset( &g_FuncTable, 0, sizeof( _QERFuncTable_1 ) );
	g_FuncTable.m_nSize = sizeof( _QERFuncTable_1 );
	return "HydraToolz for GTKRadiant"; // do we need this ? hmmm
}

const char* QERPlug_GetName(){
	return (char*)PLUGIN_NAME;
}

const char* QERPlug_GetCommandList(){
	return PLUGIN_COMMANDS;
}

extern "C" void QERPlug_Dispatch( const char* p, vec3_t vMin, vec3_t vMax, bool bSingleBrush ){
	if ( !strcmp( p, "Create/Update WAD keypair" ) ) {
		UpdateWadKeyPair();
	}
	else if ( !strcmp( p, "About..." ) ) {
		g_FuncTable.m_pfnMessageBox( (GtkWidget*)NULL, PLUGIN_ABOUT, "About", eMB_OK );
	}
}

// =============================================================================
// SYNAPSE

CSynapseServer* g_pSynapseServer = NULL;
CSynapseClientHydraToolz g_SynapseClient;

extern "C" CSynapseClient * SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ){
	if ( strcmp( version, SYNAPSE_VERSION ) ) {
		Syn_Printf( "ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version );
		return NULL;
	}
	g_pSynapseServer = pServer;
	g_pSynapseServer->IncRef();
	Set_Syn_Printf( g_pSynapseServer->Get_Syn_Printf() );

	g_SynapseClient.AddAPI( PLUGIN_MAJOR, "HydraToolz", sizeof( _QERPluginTable ) );
	g_SynapseClient.AddAPI( RADIANT_MAJOR, NULL, sizeof( g_FuncTable ), SYN_REQUIRE, &g_FuncTable );
	g_SynapseClient.AddAPI( VFS_MAJOR, "wad", sizeof( g_FileSystemTable ), SYN_REQUIRE, &g_FileSystemTable );
	g_SynapseClient.AddAPI( ENTITY_MAJOR, NULL, sizeof( g_EntityTable ), SYN_REQUIRE, &g_EntityTable );
	return &g_SynapseClient;
}

bool CSynapseClientHydraToolz::RequestAPI( APIDescriptor_t *pAPI ){
	if ( !strcmp( pAPI->major_name, PLUGIN_MAJOR ) ) {
		_QERPluginTable *pTable = static_cast<_QERPluginTable*>( pAPI->mpTable );
		pTable->m_pfnQERPlug_Init = QERPlug_Init;
		pTable->m_pfnQERPlug_GetName = QERPlug_GetName;
		pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList;
		pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch;
		return true;
	}

	Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() );
	return false;
}

const char* CSynapseClientHydraToolz::GetInfo(){
	return "HydraToolz plugin built " __DATE__ " " RADIANT_VERSION;
}
