/*
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"
#include "version.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 (GtkWidget *widget, gpointer data)
{
  GtkWidget *parent;
  int *loop, *ret;

  parent = gtk_widget_get_toplevel (widget);
  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 *window, *w, *vbox, *hbox;
  int mode = (uType & MB_TYPEMASK), ret, loop = 1;

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                      GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
  gtk_signal_connect (GTK_OBJECT (window), "destroy",
                      GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
  gtk_window_set_title (GTK_WINDOW (window), lpCaption);
  gtk_container_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);

  vbox = gtk_vbox_new (FALSE, 10);
  gtk_container_add (GTK_CONTAINER (window), vbox);
  gtk_widget_show (vbox);

  w = gtk_label_new (lpText);
  gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
  gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT);
  gtk_widget_show (w);

  w = gtk_hseparator_new ();
  gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
  gtk_widget_show (w);

  hbox = gtk_hbox_new (FALSE, 10);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
  gtk_widget_show (hbox);

  if (mode == MB_OK)
  {
    w = gtk_button_new_with_label ("Ok");
    gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT (w), "clicked",
                        GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
    GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
    gtk_widget_grab_default (w);
    gtk_widget_show (w);
    ret = IDOK;
  }
  else if (mode ==  MB_OKCANCEL)
  {
    w = gtk_button_new_with_label ("Ok");
    gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT (w), "clicked",
                        GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
    GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
    gtk_widget_grab_default (w);
    gtk_widget_show (w);

    w = gtk_button_new_with_label ("Cancel");
    gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT (w), "clicked",
                        GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
    gtk_widget_show (w);
    ret = IDCANCEL;
  }
  else if (mode == MB_YESNOCANCEL)
  {
    w = gtk_button_new_with_label ("Yes");
    gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT (w), "clicked",
                        GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES));
    GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
    gtk_widget_grab_default (w);
    gtk_widget_show (w);

    w = gtk_button_new_with_label ("No");
    gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT (w), "clicked",
                        GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO));
    gtk_widget_show (w);

    w = gtk_button_new_with_label ("Cancel");
    gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT (w), "clicked",
                        GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
    gtk_widget_show (w);
    ret = IDCANCEL;
  }
  else /* if (mode == MB_YESNO) */
  {
    w = gtk_button_new_with_label ("Yes");
    gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT (w), "clicked",
                        GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES));
    GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
    gtk_widget_grab_default (w);
    gtk_widget_show (w);

    w = gtk_button_new_with_label ("No");
    gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
    gtk_signal_connect (GTK_OBJECT (w), "clicked",
                        GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO));
    gtk_widget_show (w);
    ret = IDNO;
  }

  gtk_widget_show (window);
  gtk_grab_add (window);

  while (loop)
    gtk_main_iteration ();

  gtk_grab_remove (window);
  gtk_widget_destroy (window);

  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;
}
