// PlugInManager.cpp: implementation of the CPlugInManager class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "io.h"
#include "Radiant.h"
#include "PlugInManager.h"
#include "PlugIn.h"
#include "DialogInfo.h"
#include "pakstuff.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CPlugInManager::CPlugInManager()
{
  m_pTexturePlug = NULL;
  m_pSurfaceListPlug = NULL;
  PatchesMode = EActivePatches;
}

CPlugInManager::~CPlugInManager()
{
  Cleanup();
}

void CPlugInManager::Init(const char * pPath)
{
	Cleanup();

	// set some globals
	g_qeglobals.bSurfacePropertiesPlugin = false;
	g_qeglobals.bBSPFrontendPlugin = false;
	
	CString strPath(pPath);
	strPath += "*.dll";
	
	bool bGo = true;
	
	struct _finddata_t fileinfo;
	int handle = _findfirst (strPath, &fileinfo);
	if (handle != -1)
	{
		do
		{
			strPath.Format("%s\\%s", pPath, fileinfo.name);
			CPlugIn *pPlug = new CPlugIn();
			if (pPlug->load(strPath))
			{
				if(FillFuncTable(pPlug))		// PGM
				{
					m_PlugIns.Add(pPlug);

					pPlug->RegisterPluginEntities();

					// if this thing handles surface properties
					pPlug->InitSurfacePlugin();
					
					// will test and init if it's a BSP frontend
					pPlug->InitBSPFrontendPlugin();

					g_pParentWnd->AddPlugInMenuItem(pPlug);
					
					// if this thing handles textures
					if (pPlug->getTextureInfo() != NULL)
					{
						this->m_pTexturePlug = pPlug;
						
						// if this is a wad style texture extension, have it load everything now
						if (pPlug->getTextureInfo()->m_bWadStyle)
						{
							CString strPath = ValueForKey(g_qeglobals.d_project_entity, "texturepath");
							pPlug->loadTexture(strPath);
						}
					}
					
					if (pPlug->getSurfaceFlags() != NULL)
					{
						this->m_pSurfaceListPlug = pPlug;
					}
				}
				else
				{
					delete pPlug;				// PGM
				}
			}
			else
			{
				delete pPlug;
			}
		} while (_findnext( handle, &fileinfo ) != -1);
		_findclose (handle);
	}
	
}

void CPlugInManager::Cleanup()
{
	int i;
	for (i = 0; i < m_PlugIns.GetSize(); i++)
	{
		CPlugIn *plug = reinterpret_cast<CPlugIn*>(m_PlugIns.GetAt(i));
		plug->free();
		delete plug;
	}
	m_PlugIns.RemoveAll();
	
	for (i = 0; i < m_BrushHandles.GetSize(); i++)
	{
		brush_t *pb = reinterpret_cast<brush_t*>(m_BrushHandles.GetAt(i));
		Brush_Free(pb);
	}
	m_BrushHandles.RemoveAll();
	
	for (i = 0; i < m_EntityHandles.GetSize(); i++)
	{
		entity_t *pe = reinterpret_cast<entity_t*>(m_EntityHandles.GetAt(i));
		Entity_Free(pe);
	}
	m_EntityHandles.RemoveAll();
	
	// patches
	// these are linked into the map
	m_PatchesHandles.RemoveAll();
	// these patches were allocated by Radiant on plugin request
	// if the list is not empty, it means either the plugin asked for allocation and never commited them to the map
	// in which case we are supposed to delete them
	// or it commited them but never called m_pfnReleasePatchHandles, in case the patches may have already been
	// erased and we are trying a second time, therefore crashing ..
	//++timo FIXME: for now I leave a leak warning, we'd need a table to keep track of commited patches
#ifdef _DEBUG
	if (m_PluginPatches.GetSize() != 0)
		Sys_Printf("WARNING: m_PluginPatches.GetSize() != 0 in CPlugInManager::Cleanup, possible leak\n");
#endif
/*	for (i = 0; i < m_PluginPatches.GetSize(); i++)
	{
		patchMesh_t *pMesh = reinterpret_cast<patchMesh_t*>(m_PluginPatches.GetAt(i));
		if (pMesh->pSymbiot)
			delete pMesh;
	}
	m_PluginPatches.RemoveAll(); */
}

void CPlugInManager::Dispatch(int n, const char * p)
{
  for (int i = 0; i < m_PlugIns.GetSize(); i++)
  {
    CPlugIn *plug = reinterpret_cast<CPlugIn*>(m_PlugIns.GetAt(i));
    if (plug->ownsCommandID(n))
    {
      vec3_t vMin, vMax;
    	if (selected_brushes.next == &selected_brushes)
      {
        vMin[0] = vMin[1] = vMin[2] = 0;
        VectorCopy(vMin, vMax);
      }
      else
      {
        Select_GetBounds (vMin, vMax);
      }
      plug->dispatchCommand(p, vMin, vMax, QE_SingleBrush(true));	// PGM -- added quiet
      break;
    }
  }
}

// creates a dummy brush in the active brushes list
// FIXME : is this one really USED ?
void WINAPI QERApp_CreateBrush(vec3_t vMin, vec3_t vMax)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	
	brush_t* pBrush = Brush_Create(vMin, vMax, &g_qeglobals.d_texturewin.texdef);
	Entity_LinkBrush (world_entity, pBrush);
	Brush_Build(pBrush);
	Brush_AddToList (pBrush, &active_brushes);
	Select_Brush(pBrush);
	Sys_UpdateWindows(W_ALL);
}

LPVOID CPlugInManager::CreateBrushHandle()
{
	brush_t *pb = Brush_Alloc();
	pb->numberId = g_nBrushId++;
  m_BrushHandles.Add(pb);
  return (LPVOID)pb;
}

void CPlugInManager::DeleteBrushHandle(void * vp)
{
  CPtrArray* pHandles[3];
  pHandles[0] = &m_SelectedBrushHandles;
  pHandles[1] = &m_ActiveBrushHandles;
  pHandles[2] = &m_BrushHandles;

  for (int j = 0; j < 3; j++)
  {
    for (int i = 0; i < pHandles[j]->GetSize(); i++)
    {
      brush_t *pb = reinterpret_cast<brush_t*>(pHandles[j]->GetAt(i));
      if (pb == reinterpret_cast<brush_t*>(vp))
      {
        if (j == 2)
        {
          // only remove it from the list if it is work area
          // this allows the selected and active list indexes to remain constant
          // throughout a session (i.e. between an allocate and release)
          pHandles[j]->RemoveAt(i);
        }
        Brush_Free(pb);
		Sys_MarkMapModified();		// PGM
        return;
      }
    }
  }
}

void CPlugInManager::CommitBrushHandleToMap(void * vp)
{
  g_bScreenUpdates = false; 
  for (int i = 0; i < m_BrushHandles.GetSize(); i++)
  {
    brush_t *pb = reinterpret_cast<brush_t*>(m_BrushHandles.GetAt(i));
    if (pb == reinterpret_cast<brush_t*>(vp))
    {
      m_BrushHandles.RemoveAt(i);
	    Entity_LinkBrush (world_entity, pb);
      Brush_Build(pb);
	    Brush_AddToList (pb, &active_brushes);
      Select_Brush(pb);
    }
  }
  g_bScreenUpdates = true; 
  Sys_UpdateWindows(W_ALL);
}

void CPlugInManager::AddFaceToBrushHandle(void * vp, vec3_t v1, vec3_t v2, vec3_t v3)
{
  brush_t *bp = FindBrushHandle(vp);
  if (bp != NULL)
  {
		face_t *f = Face_Alloc();
		f->texdef = g_qeglobals.d_texturewin.texdef;
		f->texdef.flags &= ~SURF_KEEP;
		f->texdef.contents &= ~CONTENTS_KEEP;
		f->next = bp->brush_faces;
		bp->brush_faces = f;
		VectorCopy (v1, f->planepts[0]);
		VectorCopy (v2, f->planepts[1]);
		VectorCopy (v3, f->planepts[2]);
  }
}

brush_t* CPlugInManager::FindBrushHandle(void * vp)
{
  CPtrArray* pHandles[4];
  pHandles[0] = &m_SelectedBrushHandles;
  pHandles[1] = &m_ActiveBrushHandles;
  pHandles[2] = &m_BrushHandles;
  pHandles[3] = &m_EntityBrushHandles;

  for (int j = 0; j < 4; j++)
  {
    for (int i = 0; i < pHandles[j]->GetSize(); i++)
    {
      brush_t *pb = reinterpret_cast<brush_t*>(pHandles[j]->GetAt(i));
      if (pb == reinterpret_cast<brush_t*>(vp))
      {
        return pb;
      }
    }
  }
  return NULL;
}

patchMesh_t* CPlugInManager::FindPatchHandle(int index)
{
	switch (PatchesMode)
	{
	case EActivePatches:
	case ESelectedPatches:
		if ( index < m_PatchesHandles.GetSize() )
		{
			brush_t *pb = reinterpret_cast<brush_t *>(m_PatchesHandles.GetAt(index));
			return pb->pPatch;
		}
#ifdef _DEBUG
		Sys_Printf("WARNING: out of bounds in CPlugInManager::FindPatchHandle\n");
#endif
		break;
	case EAllocatedPatches:
		if ( index < m_PluginPatches.GetSize() )
		{
			patchMesh_t *pPatch = reinterpret_cast<patchMesh_t *>(m_PluginPatches.GetAt(index));
			return pPatch;
		}
#ifdef _DEBUG
		Sys_Printf("WARNING: out of bounds in CPlugInManager::FindPatchHandle\n");
#endif
		break;
	}
	return NULL;
}

LPVOID WINAPI QERApp_CreateBrushHandle()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  return g_pParentWnd->GetPlugInMgr().CreateBrushHandle();
}

void WINAPI QERApp_DeleteBrushHandle(LPVOID vp)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  g_pParentWnd->GetPlugInMgr().DeleteBrushHandle(vp);
}

void WINAPI QERApp_CommitBrushHandleToMap(LPVOID vp)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  g_pParentWnd->GetPlugInMgr().CommitBrushHandleToMap(vp);
}

void WINAPI QERApp_AddFace(LPVOID vp, vec3_t v1, vec3_t v2, vec3_t v3)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  g_pParentWnd->GetPlugInMgr().AddFaceToBrushHandle(vp, v1, v2, v3);
}

void WINAPI QERApp_DeleteSelection()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  Select_Delete();
}

void WINAPI QERApp_SysMsg(LPCSTR pMsg)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  CString str = pMsg;
  Sys_Printf(str.GetBuffer(0));
}

// NOTE: called only in case of plugin error. We can try a plugin refresh instead of a straight crash ?
void WINAPI QERApp_ErrorMsg(LPCSTR pMsg)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	CString str = pMsg;
	Error(str.GetBuffer(0));
}

void WINAPI QERApp_InfoMsg(LPCSTR pMsg)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  ShowInfoDialog(pMsg);
}

void WINAPI QERApp_HideInfoMsg()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  HideInfoDialog();
}

//=====
//PGM
void WINAPI QERApp_PositionView(vec3_t	v1, vec3_t v2)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	g_pParentWnd->ActiveXY()->SetOrigin(v1);
	// FIXME - implement this!
	Sys_UpdateWindows(W_ALL);
}
//PGM
//=====

//FIXME: this AcquirePath stuff is pretty much a mess and needs cleaned up
bool g_bPlugWait = false;
bool g_bPlugOK = false;
int  g_nPlugCount = 0;

void _PlugDone(bool b, int n)
{
  g_bPlugWait = false;
  g_bPlugOK = b;
  g_nPlugCount = n;
}

void WINAPI QERApp_GetPoints(int nMax, _QERPointData *pData, LPCSTR pMsg)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  ShowInfoDialog(pMsg);
  g_bPlugWait = true;
  g_bPlugOK = false;
  g_nPlugCount = 0;
//  g_nPlugCount=nMax-1;
  AcquirePath(nMax, &_PlugDone);
  while (g_bPlugWait)
  {
    MSG msg;
    if (::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )) 
    { 
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }
  HideInfoDialog();
  
  pData->m_nCount = 0;
  pData->m_pVectors = NULL;

  if (g_bPlugOK && g_nPlugCount > 0)
  {
    pData->m_nCount = g_nPlugCount;
    pData->m_pVectors = reinterpret_cast<vec3_t*>(qmalloc(g_nPlugCount * sizeof(vec3_t)));
    vec3_t *pOut = pData->m_pVectors;
    for (int i = 0; i < g_nPlugCount; i++)
    {
	    memcpy(pOut, &g_PathPoints[i],sizeof(vec3_t));
	    pOut++;
    }
  }
}

void WINAPI QERApp_AddFaceData(LPVOID pv, _QERFaceData *pData)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	brush_t* pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv);
  if (pBrush != NULL)
  {
		face_t *f = Face_Alloc();
		f->texdef = g_qeglobals.d_texturewin.texdef;
		f->texdef.flags = pData->m_nFlags;
    f->texdef.contents = pData->m_nContents;
    f->texdef.value = pData->m_nValue;
    Sys_Printf("TODO: fix BP mode brush in / out\n");
    if (pData->m_bBPrimit)
    {
      f->brushprimit_texdef = pData->brushprimit_texdef;
    }
    else
    {
      f->texdef.rotate = pData->m_fRotate;
      f->texdef.shift[0] = pData->m_fShift[0];
      f->texdef.shift[1] = pData->m_fShift[1];
      f->texdef.scale[0] = pData->m_fScale[0];
      f->texdef.scale[1] = pData->m_fScale[1];
    }
    //strcpy(f->texdef.name, pData->m_TextureName);
    f->texdef.SetName(pData->m_TextureName);
		f->next = pBrush->brush_faces;
		pBrush->brush_faces = f;
		VectorCopy (pData->m_v1, f->planepts[0]);
		VectorCopy (pData->m_v2, f->planepts[1]);
		VectorCopy (pData->m_v3, f->planepts[2]);
		Sys_MarkMapModified();		// PGM
  }
}

int WINAPI QERApp_GetFaceCount(LPVOID pv)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  int n = 0;
  brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv);
  if (pBrush != NULL)
  {
	  for (face_t *f = pBrush->brush_faces ; f; f = f->next)
    {
      n++;
    }
  }
  return n;
}

_QERFaceData* WINAPI QERApp_GetFaceData(LPVOID pv, int nFaceIndex)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  static _QERFaceData face;
  int n = 0;
  brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv);

  if (pBrush != NULL)
  {
    for (face_t *f = pBrush->brush_faces ; f; f = f->next)
    {

#ifdef _DEBUG
		if (!pBrush->brush_faces)
		{
			Sys_Printf( "Warning : pBrush->brush_faces is NULL in QERApp_GetFaceData\n" );
			return NULL;
		}
#endif

      if (n == nFaceIndex)
      {
        face.m_nContents = f->texdef.contents;
        face.m_nFlags = f->texdef.flags;
        face.m_nValue = f->texdef.value;
        if (g_qeglobals.bPrimitBrushes)
        {
          //++timo NOTE: we may want to convert back to old format for backward compatibility with some old plugins?
          face.m_bBPrimit = true;
          face.brushprimit_texdef = f->brushprimit_texdef;
        }
        else
        {
          face.m_fRotate = f->texdef.rotate;
          face.m_fScale[0] = f->texdef.scale[0];
          face.m_fScale[1] = f->texdef.scale[1];
          face.m_fShift[0] = f->texdef.shift[0];
          face.m_fShift[1] = f->texdef.shift[1];
        }
        strcpy(face.m_TextureName, f->texdef.name);
        VectorCopy(f->planepts[0], face.m_v1);
        VectorCopy(f->planepts[1], face.m_v2);
        VectorCopy(f->planepts[2], face.m_v3);
        return &face;
      }
      n++;
    }
  }
  return NULL;
}

void WINAPI QERApp_SetFaceData(LPVOID pv, int nFaceIndex, _QERFaceData *pData)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	int n = 0;
	brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv);

	if (pBrush != NULL)
	{
		for (face_t *f = pBrush->brush_faces ; f; f = f->next)
		{
			if (n == nFaceIndex)
			{
				f->texdef.flags = pData->m_nFlags;
				f->texdef.contents = pData->m_nContents;
				f->texdef.value = pData->m_nValue;
				f->texdef.rotate = pData->m_fRotate;
				f->texdef.shift[0] = pData->m_fShift[0];
				f->texdef.shift[1] = pData->m_fShift[1];
				f->texdef.scale[0] = pData->m_fScale[0];
				f->texdef.scale[1] = pData->m_fScale[1];
				//strcpy(f->texdef.name, pData->m_TextureName);
				f->texdef.SetName(pData->m_TextureName);
				VectorCopy(pData->m_v1, f->planepts[0]);
				VectorCopy(pData->m_v2, f->planepts[1]);
				VectorCopy(pData->m_v3, f->planepts[2]);
				Sys_MarkMapModified();		// PGM
				return;						// PGM
			}
			n++;
		}
	}
}

void WINAPI QERApp_DeleteFace(LPVOID pv, int nFaceIndex)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  int n = 0;
  brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv);
  if (pBrush != NULL)
  {
    face_t *pPrev = pBrush->brush_faces;
	  for (face_t *f = pBrush->brush_faces; f; f = f->next)
    {
      if (n == nFaceIndex)
      {
        pPrev->next = f->next;
			  Face_Free (f);
		Sys_MarkMapModified();		// PGM
        return;
      }
      n++;
      pPrev = f;
    }
  }
}

//==========
//PGM
void WINAPI QERApp_BuildBrush (LPVOID pv)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv);
	if (pBrush != NULL)
	{
		Brush_Build(pBrush);
		Sys_UpdateWindows(W_ALL);
	}
}

//Timo : another version with bConvert flag
//++timo since 1.7 is not compatible with earlier plugin versions, remove this one and update QERApp_BuildBrush
void WINAPI QERApp_BuildBrush2 (LPVOID pv, int bConvert)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv);
	if (pBrush != NULL)
	{
		Brush_Build( pBrush, true, true, bConvert );
		Sys_UpdateWindows(W_ALL);
	}
}

void WINAPI QERApp_SelectBrush (LPVOID pv)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv);
	if (pBrush != NULL)
	{
		Select_Brush(pBrush, false);
		Sys_UpdateWindows(W_ALL);
	}

}

void WINAPI QERApp_DeselectBrush (LPVOID pv)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	// FIXME - implement this!
}

void WINAPI QERApp_ResetPlugins()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  g_pParentWnd->OnPluginsRefresh();
}

void WINAPI QERApp_DeselectAllBrushes ()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	Select_Deselect();
	Sys_UpdateWindows(W_ALL);
}
//PGM
//==========

void WINAPI QERApp_TextureBrush(LPVOID pv, LPCSTR pName)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv);
  if (pBrush != NULL)
  {
	  for (face_t *f = pBrush->brush_faces ; f; f = f->next)
    {
      //strcpy(f->texdef.name, pName);
      f->texdef.SetName(pName);
    }
    Sys_MarkMapModified();		// PGM
  }
}

int WINAPI QERApp_SelectedBrushCount()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  int n = 0;
	for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
	{
    n++;
  }
  return n;
}

int WINAPI QERApp_ActiveBrushCount()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  int n = 0;
	for (brush_t *pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next)
	{
    n++;
  }
  return n;
}

int WINAPI QERApp_AllocateSelectedBrushHandles()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  int n = 0;
	for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
	{
    n++;
    g_pParentWnd->GetPlugInMgr().GetSelectedHandles().Add(pb);
  }
  return n;
}

int WINAPI QERApp_AllocateActiveBrushHandles()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  int n = 0;
	for (brush_t *pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next)
	{
    n++;
    g_pParentWnd->GetPlugInMgr().GetActiveHandles().Add(pb);
  }
  return n;
}

void WINAPI QERApp_ReleaseSelectedBrushHandles()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  g_pParentWnd->GetPlugInMgr().GetSelectedHandles().RemoveAll();
  Sys_UpdateWindows(W_ALL);
}

void WINAPI QERApp_ReleaseActiveBrushHandles()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  g_pParentWnd->GetPlugInMgr().GetActiveHandles().RemoveAll();
  Sys_UpdateWindows(W_ALL);
}

LPVOID WINAPI QERApp_GetActiveBrushHandle(int nIndex)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  if (nIndex < g_pParentWnd->GetPlugInMgr().GetActiveHandles().GetSize())
  {
    return reinterpret_cast<LPVOID>(g_pParentWnd->GetPlugInMgr().GetActiveHandles().GetAt(nIndex));
  }
  return NULL;
}

LPVOID WINAPI QERApp_GetSelectedBrushHandle(int nIndex)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  if (nIndex < g_pParentWnd->GetPlugInMgr().GetSelectedHandles().GetSize())
  {
    return reinterpret_cast<LPVOID>(g_pParentWnd->GetPlugInMgr().GetSelectedHandles().GetAt(nIndex));
  }
  return NULL;
}

int WINAPI QERApp_TextureCount()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	//++timo TODO: replace by QERApp_GetActiveShaderCount and verify
	Texture_StartPos ();
	int x, y;
	int n = 0;
	while (1)
	{
		IShader *pShader = Texture_NextPos (&x, &y);
		if (!pShader)
			break;
		n++;
	}
	return n;
}

LPCSTR WINAPI QERApp_GetTexture(int nIndex)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	//++timo TODO: replace by QERApp_ActiveShader_ForIndex
	// these funcs would end up being provided for backward compatibility
	static char name[QER_MAX_NAMELEN];
	Texture_StartPos ();
	int x, y;
	int n = 0;
	while (1)
	{
		IShader *pShader = Texture_NextPos (&x, &y);
		if (!pShader)
			break;
		if (n == nIndex)
		{
			strcpy(name, pShader->getName());
			return name;
		}
		n++;
	}
	return NULL;
}

LPCSTR WINAPI QERApp_GetCurrentTexture()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  return g_qeglobals.d_texturewin.texdef.name;
}

void WINAPI QERApp_SetCurrentTexture(LPCSTR strName)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	//++timo hu ?? tex is not initialized ?? can be any value ..
  texdef_t tex;
  //++timo added a brushprimit_texdef ..
  // smthg to be done here
  brushprimit_texdef_t brushprimit_tex;
  //strcpy(tex.name, strName);
  tex.SetName(strName);
  Texture_SetTexture(&tex,&brushprimit_tex);
}

int WINAPI QERApp_GetEClassCount()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  int n = 0;
  for (eclass_t *e = eclass ; e ; e = e->next)
	{
    n++;
  }
  return n;
}

LPCSTR WINAPI QERApp_GetEClass(int nIndex)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
  int n = 0;
  for (eclass_t *e = eclass ; e ; e = e->next)
	{
    if (n == nIndex)
    {
      return e->name;
    }
  }
  return NULL;
}

void WINAPI QERApp_LoadTextureRGBA(LPVOID vp)
{
  Texture_LoadFromPlugIn(vp);
}

// v1.70 code
// world_entity holds the worldspawn and is indexed as 0
// other entities are in the entities doubly linked list
// QERApp_GetEntityCount counts the entities like in any C array: [0..length-1]
int WINAPI QERApp_GetEntityCount()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	int n = 1;
	for (entity_t *pe = entities.next ; pe != &entities ; pe = pe->next)
	{
		n++;
	}
	return n;
}

// We don't store entities in CPtrArray, we need to walk the list
LPVOID WINAPI QERApp_GetEntityHandle(int nIndex)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	if (nIndex==0)
		// looks for the worldspawn
		return static_cast<LPVOID>(world_entity);
	entity_t *pe = &entities;
	int n = 0;
	while ( n < nIndex )
	{
		pe = pe->next;
		n++;
	}
	return static_cast<LPVOID>(pe);
}

epair_t** WINAPI QERApp_GetEntityKeyValList(LPVOID vp)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	entity_t *pe = static_cast<entity_t *>(vp);
	return &pe->epairs;
}

epair_t* WINAPI QERApp_AllocateEpair( char *key, char *val )
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	epair_t *e = (epair_t*)qmalloc (sizeof(*e));
	e->key = (char*)qmalloc(strlen(key)+1);
	strcpy (e->key, key);
	e->value = (char*)qmalloc(strlen(val)+1);
	strcpy (e->value, val);
	return e;
}

void WINAPI QERApp_SetEntityKeyValList(LPVOID vp, epair_t* ep)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	entity_t *pe = static_cast<entity_t *>(vp);
	if (pe->epairs)
		Sys_Printf( "Warning : pe->epairs != NULL in QERApp_SetEntityKeyValList, will not set\n" );
	else
		pe->epairs = ep;
}

int WINAPI QERApp_AllocateEntityBrushHandles(LPVOID vp)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	entity_t *pe = static_cast<entity_t *>(vp);
	int n = 0;
	if (!pe->brushes.onext)
		return 0;
	g_pParentWnd->GetPlugInMgr().GetEntityBrushHandles().RemoveAll();
	for (brush_t *pb = pe->brushes.onext ; pb != &pe->brushes ; pb=pb->onext)
	{
		n++;
		g_pParentWnd->GetPlugInMgr().GetEntityBrushHandles().Add(pb);
	}
	return n;
}
	
void WINAPI QERApp_ReleaseEntityBrushHandles()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	g_pParentWnd->GetPlugInMgr().GetEntityBrushHandles().RemoveAll();
}

LPVOID WINAPI QERApp_GetEntityBrushHandle(int nIndex)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	if (nIndex < g_pParentWnd->GetPlugInMgr().GetEntityBrushHandles().GetSize())
		return g_pParentWnd->GetPlugInMgr().GetEntityBrushHandles().GetAt(nIndex);
	return NULL;
}

LPVOID WINAPI QERApp_CreateEntityHandle()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	entity_t *pe = reinterpret_cast<entity_t*>(qmalloc(sizeof(entity_t)));
	pe->brushes.onext = pe->brushes.oprev = &pe->brushes;
	g_pParentWnd->GetPlugInMgr().GetEntityHandles().Add(static_cast<LPVOID>(pe));
	return static_cast<LPVOID>(pe);
}

// the vpBrush needs to be in m_BrushHandles
//++timo we don't have allocation nor storage for vpEntity, no checks for this one
void WINAPI QERApp_CommitBrushHandleToEntity(LPVOID vpBrush, LPVOID vpEntity)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	g_pParentWnd->GetPlugInMgr().CommitBrushHandleToEntity(vpBrush, vpEntity);
	return;
}

char* WINAPI QERApp_ReadProjectKey(char* key)
{
	return ValueForKey(g_qeglobals.d_project_entity, key);
}

int WINAPI QERApp_ScanFileForEClass(char *filename )
{
	// set single class parsing
	parsing_single = true;
	Eclass_ScanFile(filename);
	if (eclass_found)
	{
		eclass_e->nShowFlags |= ECLASS_PLUGINENTITY;
		return 1;
	}
	return 0;
}

// the vpBrush needs to be in m_BrushHandles
//++timo add a debug check to see if we found the brush handle
// NOTE : seems there's no way to check vpEntity is valid .. this is dangerous
// links the brush to its entity, everything else is done when commiting the entity to the map
void CPlugInManager::CommitBrushHandleToEntity(LPVOID vpBrush, LPVOID vpEntity)
{
	brush_t* pb;
	entity_t* pe;
	for (int i=0 ; i < m_BrushHandles.GetSize() ; i++)
	{
		if (vpBrush == m_BrushHandles.GetAt(i))
		{
			m_BrushHandles.RemoveAt(i);
			pb = reinterpret_cast<brush_t*>(vpBrush);
			pe = reinterpret_cast<entity_t *>(vpEntity);
			Entity_LinkBrush (pe, pb);
		}
	}
	Sys_UpdateWindows(W_ALL);
}

// the vpEntity must be in m_EntityHandles
void WINAPI QERApp_CommitEntityHandleToMap(LPVOID vpEntity)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	g_pParentWnd->GetPlugInMgr().CommitEntityHandleToMap(vpEntity);
	return;
}

int WINAPI QERApp_LoadFile( const char *pLocation, void ** buffer )
{
	char cPath[1024];
	sprintf( cPath, "%s/%s", ValueForKey(g_qeglobals.d_project_entity, "basepath"), pLocation);
  int nSize = LoadFile( cPath, buffer);
  if (nSize == -1)
  {
    nSize = PakLoadAnyFile(cPath, buffer);
  }
	return nSize;
}

char * WINAPI QERApp_ExpandReletivePath (char *p)
{
	return ExpandReletivePath(p);
}

qtexture_t* WINAPI QERApp_Texture_ForName (const char *name)
{
	// if the texture is not loaded yet, this call will get it loaded
	// but: when we assign a GL bind number, we need to be in the g_qeglobals.d_hdcBase , g_qeglobals.d_hglrcBase GL context
	// the plugin may set the GL context to whatever he likes, but then load would fail
	// NOTE: is context switching time-consuming? then maybe the plugin could handle the context switch and only add a 
	// sanity check in debug mode here
	// read current context
	HDC pluginHDC = qwglGetCurrentDC();
	HGLRC pluginHGLRC = qwglGetCurrentContext();
	qwglMakeCurrent( g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase );
	//++timo debugging
	Sys_Printf("WARNING: QERApp_Texture_ForName ... don't call that!!\n");
	qtexture_t* qtex = QERApp_Texture_ForName2( name );
	return qtex;
}

void WINAPI QERApp_RadiantFree( void * buf )
{
	free( buf );
}

char* WINAPI QERApp_Token()
{
	return token;
}

char* WINAPI QERApp_GetMapName()
{
	return currentmap;
}

void CPlugInManager::CommitEntityHandleToMap(LPVOID vpEntity)
{
	entity_t *pe;
	eclass_t *e;
	brush_t		*b;
	vec3_t mins,maxs;
	bool has_brushes;
	for (int i=0 ; i < m_EntityHandles.GetSize() ; i++ )
	{
		if (vpEntity == m_EntityHandles.GetAt(i))
		{
			m_EntityHandles.RemoveAt(i);
			pe = reinterpret_cast<entity_t*>(vpEntity);
			// fill additional fields
			// straight copy from Entity_Parse
			// entity_t::origin
			GetVectorForKey (pe, "origin", pe->origin);
			// entity_t::eclass
			if (pe->brushes.onext == &pe->brushes)
				has_brushes = false;
			else
				has_brushes = true;
			e = Eclass_ForName (ValueForKey (pe, "classname"), has_brushes);
			pe->eclass = e;
			// fixedsize
			if (e->fixedsize)
			{
				if (pe->brushes.onext != &pe->brushes)
				{
					Sys_Printf("Warning : Fixed size entity with brushes in CPlugInManager::CommitEntityHandleToMap\n");
				}
				// create a custom brush
				VectorAdd(e->mins, pe->origin, mins);
				VectorAdd(e->maxs, pe->origin, maxs);
				float a = 0;
				if (e->nShowFlags & ECLASS_MISCMODEL)
				{
					char* p = ValueForKey(pe, "model");
					if (p != NULL && strlen(p) > 0)
					{
						vec3_t vMin, vMax;
						a = FloatForKey (pe, "angle");
				        if (GetCachedModel(pe, p, vMin, vMax))
				        {
						      // create a custom brush
						      VectorAdd (pe->md3Class->mins, pe->origin, mins);
						      VectorAdd (pe->md3Class->maxs, pe->origin, maxs);
				        }
					}
			    }
		
			    b = Brush_Create (mins, maxs, &e->texdef);

			    if (a)
			    {
					vec3_t vAngle;
					vAngle[0] = vAngle[1] = 0;
					vAngle[2] = a;
					Brush_Rotate(b, vAngle, pe->origin, false);
				}

				b->owner = pe;

				b->onext = pe->brushes.onext;
				b->oprev = &pe->brushes;
				pe->brushes.onext->oprev = b;
				pe->brushes.onext = b;
			}
			else
			{	// brush entity
				if (pe->brushes.next == &pe->brushes)
					Sys_Printf ("Warning: Brush entity with no brushes in CPlugInManager::CommitEntityHandleToMap\n");
			}

			// add brushes to the active brushes list
			// and build them along the way
			for (b=pe->brushes.onext ; b != &pe->brushes ; b=b->onext)
			{
				// convert between old brushes and brush primitive
				if (g_qeglobals.m_bBrushPrimitMode)
				{
					// we only filled the shift scale rot fields, needs conversion
					Brush_Build( b, true, true, true );
				}
				else
				{
					// we are using old brushes
					Brush_Build( b );
				}
				b->next = active_brushes.next;
				active_brushes.next->prev = b;
				b->prev = &active_brushes;
				active_brushes.next = b;
			}

			// handle worldspawn entities
			// if worldspawn has no brushes, use the new one
			if (!strcmp(ValueForKey (pe, "classname"), "worldspawn"))
			{
				if ( world_entity && ( world_entity->brushes.onext != &world_entity->brushes ) )
				{
					// worldspawn already has brushes
					Sys_Printf ("Commiting worldspawn as func_group\n");
					SetKeyValue(pe, "classname", "func_group");
					// add the entity to the end of the entity list
					pe->next = &entities;
					pe->prev = entities.prev;
					entities.prev->next = pe;
					entities.prev = pe;
					g_qeglobals.d_num_entities++;
				}
				else
				{
					// there's a worldspawn with no brushes, we assume the map is empty
					if ( world_entity )
					{
						Entity_Free( world_entity );
						world_entity = pe;
					}
					else
						Sys_Printf("Warning : unexpected world_entity == NULL in CommitEntityHandleToMap\n");
				}
			}
			else
			{
				// add the entity to the end of the entity list
				pe->next = &entities;
				pe->prev = entities.prev;
				entities.prev->next = pe;
				entities.prev = pe;
				g_qeglobals.d_num_entities++;
			}
		}
	}
}

void WINAPI QERApp_SetScreenUpdate(int bScreenUpdates)
{
	g_bScreenUpdates = bScreenUpdates; 
}

texturewin_t* WINAPI QERApp_QeglobalsTexturewin()
{
	return &g_qeglobals.d_texturewin;
}

_QERTextureInfo* CPlugInManager::GetTextureInfo()
{
  if (m_pTexturePlug != NULL)
  {
    return m_pTexturePlug->getTextureInfo();
  }
  else
  {
    return NULL;
  }
}

LPVOID CPlugInManager::GetSurfaceFlags()
{
  if (m_pSurfaceListPlug != NULL)
  {
    return m_pSurfaceListPlug->getSurfaceFlags();
  }
  else
  {
    return NULL;
  }
}

void CPlugInManager::LoadTexture(const char *pFilename)
{
  if (m_pTexturePlug != NULL)
  {
    m_pTexturePlug->loadTexture(pFilename);
  }
}

patchMesh_t* WINAPI QERApp_GetSelectedPatch( )
{
	for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
	{
		if (pb->patchBrush)
		{
			return pb->pPatch;
		}
	}
#ifdef _DEBUG
	Sys_Printf("WARNING: QERApp_GetSelectedPatchTexdef called with no patch selected\n");
#endif
	return NULL;
}

char* WINAPI QERApp_GetGamePath()
{
	return g_PrefsDlg.m_strQuake2.GetBuffer(0);
}

char* WINAPI QERApp_GetQERPath()
{
	return g_strAppPath.GetBuffer(0);
}

// patches in/out -----------------------------------
int WINAPI QERApp_AllocateActivePatchHandles()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	return g_pParentWnd->GetPlugInMgr().AllocateActivePatchHandles();
}

int CPlugInManager::AllocateActivePatchHandles()
{
	int n = 0;
	for (brush_t *pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next)
	{
		if (pb->patchBrush)
		{
			n++;
			m_PatchesHandles.Add(pb);
		}
	}
	return n;
}

int WINAPI QERApp_AllocateSelectedPatchHandles()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	return g_pParentWnd->GetPlugInMgr().AllocateSelectedPatchHandles();
}
	
int CPlugInManager::AllocateSelectedPatchHandles()
{
	int n = 0;
	for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
	{
		if (pb->patchBrush)
		{
			n++;
			m_PatchesHandles.Add(pb);
		}
	}
	return n;
}

void WINAPI QERApp_ReleasePatchHandles()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	g_pParentWnd->GetPlugInMgr().ReleasePatchesHandles();
}

patchMesh_t* WINAPI QERApp_GetPatchData(int index)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	static patchMesh_t patch;
	patchMesh_t *pPatch = g_pParentWnd->GetPlugInMgr().FindPatchHandle(index);
	if (pPatch)
	{
		memcpy( &patch, pPatch, sizeof(patchMesh_t) );
		return &patch;
	}
	return NULL;
}

void WINAPI QERApp_DeletePatch(int index)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	patchMesh_t *pPatch = g_pParentWnd->GetPlugInMgr().FindPatchHandle(index);
	if (pPatch)
	{
		brush_t *pb = pPatch->pSymbiot;
		Patch_Delete( pPatch );
		if (pb)
			Brush_Free( pb );
	}
#ifdef _DEBUG
	Sys_Printf("Warning: QERApp_DeletePatch: FindPatchHandle failed\n");
#endif
}

int WINAPI QERApp_CreatePatchHandle()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	return g_pParentWnd->GetPlugInMgr().CreatePatchHandle();
}

int CPlugInManager::CreatePatchHandle()
{
	// NOTE: we can't call the AddBrushForPatch until we have filled the patchMesh_t structure
	patchMesh_t *pPatch = MakeNewPatch();
	m_PluginPatches.Add( pPatch );
	// change mode
	PatchesMode = EAllocatedPatches;
	return m_PluginPatches.GetSize()-1;
}

void WINAPI QERApp_CommitPatchHandleToMap(int index, patchMesh_t *pMesh, char *texName)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	g_pParentWnd->GetPlugInMgr().CommitPatchHandleToMap(index, pMesh, texName);
}

void CPlugInManager::CommitPatchHandleToMap(int index, patchMesh_t *pMesh, char *texName)
{
	if (PatchesMode==EAllocatedPatches)
	{
		patchMesh_t *pPatch = reinterpret_cast<patchMesh_t *>( m_PluginPatches.GetAt(index) );
		memcpy( pPatch, pMesh, sizeof( patchMesh_t ) );
		// patch texturing, if none given use current texture
		if (texName)
			pPatch->d_texture = QERApp_Shader_ForName(texName)->getTexture();
		if ( !pPatch->d_texture )
		{
			pPatch->d_texture = QERApp_Shader_ForName(g_qeglobals.d_texturewin.texdef.name)->getTexture();
			// checking .. just in case
			if (!pPatch->d_texture)
			{
#ifdef _DEBUG
				Sys_Printf("WARNING: failed to set patch to current texture in CPlugInManager::CommitPatchHandleToMap\n");
#endif
				pPatch->d_texture = notexture;
			}
		}
		g_bScreenUpdates = false;
		// the bLinkToWorld flag in AddBrushForPatch takes care of Brush_AddToList Entity_linkBrush and Brush_Build
		brush_t *pb = AddBrushForPatch( pPatch, true );
		Select_Brush( pb );
		g_bScreenUpdates = true;
		Sys_UpdateWindows(W_ALL);
	}
	else
	{
		brush_t *pBrush = reinterpret_cast<brush_t *>( m_PatchesHandles.GetAt(index) );
		patchMesh_t *pPatch = pBrush->pPatch;
		pPatch->width = pMesh->width;
		pPatch->height = pMesh->height;
		pPatch->contents = pMesh->contents;
		pPatch->flags = pMesh->flags;
		pPatch->value = pMesh->value;
		pPatch->type = pMesh->type;
		memcpy( pPatch->ctrl, pMesh->ctrl, sizeof(drawVert_t)*MAX_PATCH_HEIGHT*MAX_PATCH_WIDTH );
		pPatch->bDirty = true;
	}
}

// this gets called when the plugin needs specific services, for example the OpenGL drawing interface
//++timo plugins should be able to dynamically register their own interfaces in there
int WINAPI QERApp_RequestInterface( REFGUID refGUID, LPVOID pInterface )
{
	if (IsEqualGUID( refGUID, QERQglTable_GUID ))
	{
		_QERQglTable *pQglTable = static_cast<_QERQglTable *>(pInterface);
		if ( pQglTable->m_nSize != sizeof(_QERQglTable) )
		{
			Sys_Printf("wrong m_nSize in plugin-requested _QERQglTable interface\n");
			return 0;
		}
		pQglTable->m_pfn_qglAlphaFunc = qglAlphaFunc;
		pQglTable->m_pfn_qglBegin = qglBegin;
		pQglTable->m_pfn_qglBindTexture = qglBindTexture;
		pQglTable->m_pfn_qglBlendFunc = qglBlendFunc;
		pQglTable->m_pfn_qglCallList = qglCallList;
		pQglTable->m_pfn_qglCallLists = qglCallLists;
		pQglTable->m_pfn_qglClear = qglClear;
		pQglTable->m_pfn_qglClearColor = qglClearColor;
		pQglTable->m_pfn_qglClearDepth = qglClearDepth;
		pQglTable->m_pfn_qglColor3f = qglColor3f;
		pQglTable->m_pfn_qglColor3fv = qglColor3fv;
		pQglTable->m_pfn_qglColor4f = qglColor4f;
		pQglTable->m_pfn_qglColor4fv = qglColor4fv;
		pQglTable->m_pfn_qglCullFace = qglCullFace;
		pQglTable->m_pfn_qglDepthFunc = qglDepthFunc;
		pQglTable->m_pfn_qglDepthMask = qglDepthMask;
		pQglTable->m_pfn_qglDisable = qglDisable;
		pQglTable->m_pfn_qglDeleteLists = qglDeleteLists;
		pQglTable->m_pfn_qglEnable = qglEnable;
		pQglTable->m_pfn_qglEnd = qglEnd;
		pQglTable->m_pfn_qglEndList = qglEndList;
		pQglTable->m_pfn_qglFogf = qglFogf;
		pQglTable->m_pfn_qglFogfv = qglFogfv;
		pQglTable->m_pfn_qglFogi = qglFogi;
		pQglTable->m_pfn_qglGetDoublev = qglGetDoublev; // GDD: added line
		pQglTable->m_pfn_qglGetIntegerv = qglGetIntegerv; // GDD: added line
		pQglTable->m_pfn_qglGenLists = qglGenLists;
		pQglTable->m_pfn_qglHint = qglHint;
		pQglTable->m_pfn_qglLightfv = qglLightfv;
		pQglTable->m_pfn_qglLineWidth = qglLineWidth;
		pQglTable->m_pfn_qglListBase = qglListBase;
		pQglTable->m_pfn_qglLoadIdentity = qglLoadIdentity;
		pQglTable->m_pfn_qglMaterialf = qglMaterialf;
		pQglTable->m_pfn_qglMaterialfv = qglMaterialfv;
		pQglTable->m_pfn_qglMatrixMode = qglMatrixMode;
		pQglTable->m_pfn_qglNewList = qglNewList;
		pQglTable->m_pfn_qglNormal3f = qglNormal3f;
		pQglTable->m_pfn_qglOrtho = qglOrtho;
		pQglTable->m_pfn_qglPointSize = qglPointSize;
		pQglTable->m_pfn_qglPolygonMode = qglPolygonMode;
		pQglTable->m_pfn_qglPopAttrib = qglPopAttrib; // GDD: added line
		pQglTable->m_pfn_qglPopMatrix = qglPopMatrix;
		pQglTable->m_pfn_qglPushAttrib = qglPushAttrib; // GDD: added line
		pQglTable->m_pfn_qglPushMatrix = qglPushMatrix;
		pQglTable->m_pfn_qglRotated = qglRotated;
		pQglTable->m_pfn_qglRotatef = qglRotatef;
		pQglTable->m_pfn_qglScalef = qglScalef;
		pQglTable->m_pfn_qglShadeModel = qglShadeModel; // GDD: added line
		pQglTable->m_pfn_qglTexCoord2f = qglTexCoord2f;
		pQglTable->m_pfn_qglTexCoord2fv = qglTexCoord2fv;
		pQglTable->m_pfn_qglTexEnvf = qglTexEnvf;
		pQglTable->m_pfn_qglTexGenf = qglTexGenf;
		pQglTable->m_pfn_qglTexParameterf = qglTexParameterf;
		pQglTable->m_pfn_qglTranslated = qglTranslated;
		pQglTable->m_pfn_qglTranslatef = qglTranslatef;
		pQglTable->m_pfn_qglVertex2f = qglVertex2f;
		pQglTable->m_pfn_qglVertex3f = qglVertex3f;
		pQglTable->m_pfn_qglVertex3fv = qglVertex3fv;
		pQglTable->m_pfn_qglViewport = qglViewport;
		pQglTable->m_pfn_QE_CheckOpenGLForErrors = &QE_CheckOpenGLForErrors;
		pQglTable->m_pfn_QEW_SetupPixelFormat = &QEW_SetupPixelFormat;
		pQglTable->m_pfn_qwglCreateContext = qwglCreateContext;
		pQglTable->m_pfn_qwglDeleteContext = qwglDeleteContext;
		pQglTable->m_pfn_qwglMakeCurrent = qwglMakeCurrent;
		pQglTable->m_pfn_qwglShareLists = qwglShareLists;
		pQglTable->m_pfn_qwglSwapBuffers = qwglSwapBuffers;
		pQglTable->m_pfn_qwglUseFontBitmaps = qwglUseFontBitmaps;
		pQglTable->m_pfnGetQeglobalsHGLRC = &QERApp_GetQeglobalsHGLRC;
		pQglTable->m_pfnGetCameraInfo = &QERApp_GetCameraInfo;
		pQglTable->m_pfn_qgluPerspective = qgluPerspective;
		pQglTable->m_pfn_qgluLookAt = qgluLookAt;
		pQglTable->m_pfnHookGL2DWindow = QERApp_HookGL2DWindow;
		pQglTable->m_pfnUnHookGL2DWindow = QERApp_UnHookGL2DWindow;
    pQglTable->m_pfnHookGL3DWindow = QERApp_HookGL3DWindow;
    pQglTable->m_pfnUnHookGL3DWindow = QERApp_UnHookGL3DWindow;

		return 1;
	}
	else if (IsEqualGUID( refGUID, QERSelectedFaceTable_GUID ))
	{
		_QERSelectedFaceTable *pSelectedFaceTable = static_cast<_QERSelectedFaceTable *>(pInterface);
		if ( pSelectedFaceTable->m_nSize != sizeof(_QERSelectedFaceTable) )
		{
			Sys_Printf("wrong m_nSize in plugin-requested _QERSelectedFaceTable interface\n");
			return 0;
		}
		pSelectedFaceTable->m_pfnGetTextureNumber = &QERApp_ISelectedFace_GetTextureNumber;
		pSelectedFaceTable->m_pfnGetFaceInfo = &QERApp_GetFaceInfo;
		pSelectedFaceTable->m_pfnSetFaceInfo = &QERApp_SetFaceInfo;
		pSelectedFaceTable->m_pfnGetTextureSize = &QERApp_GetTextureSize;
		pSelectedFaceTable->m_pfnTextureForName = &QERApp_Texture_ForName;
		pSelectedFaceTable->m_pfnSelect_SetTexture = &Select_SetTexture;
		return 1;
	}
	else if (IsEqualGUID( refGUID, QERPluginEntitiesTable_GUID ))
	{
		_QERPluginEntitiesTable *pPluginEntitiesTable = static_cast<_QERPluginEntitiesTable *>(pInterface);
		if ( pPluginEntitiesTable->m_nSize != sizeof(_QERPluginEntitiesTable) )
		{
			Sys_Printf("wrong m_nSize in plugin-requested _QERPluginEntitiesTable interface\n");
			return 0;
		}
		pPluginEntitiesTable->m_pfnEClassScanDir = &QERApp_EClassScanDir;
		return 1;
	}
	else if (IsEqualGUID( refGUID, QERScripLibTable_GUID ))
	{
		_QERScripLibTable *pScripLibTable = static_cast<_QERScripLibTable *>(pInterface);
		if ( pScripLibTable->m_nSize != sizeof( _QERScripLibTable ) )
		{
			Sys_Printf("wrong m_nSize in plugin-requested _QERScripLibTable\n");
			return 0;
		}
		pScripLibTable->m_pfnGetToken = &GetToken;
		pScripLibTable->m_pfnToken = &QERApp_Token;
		pScripLibTable->m_pfnUnGetToken = &UngetToken;
		return 1;
	}
	else if (IsEqualGUID( refGUID, QERAppSurfaceTable_GUID ))
	{
		_QERAppSurfaceTable *pSurfaceTable = static_cast<_QERAppSurfaceTable *>(pInterface);
		if ( pSurfaceTable->m_nSize != sizeof( _QERAppSurfaceTable ) )
		{
			Sys_Printf("wrong m_nSize in plugin-requested _QERAppSurfaceTable\n");
			return 0;
		}
		pSurfaceTable->m_pfnAnyPatchesSelected = &AnyPatchesSelected;
		pSurfaceTable->m_pfnOnlyPatchesSelected = &OnlyPatchesSelected;
		pSurfaceTable->m_pfnQeglobalsTexturewin = &QERApp_QeglobalsTexturewin;
		pSurfaceTable->m_pfnGetSelectedPatch = &QERApp_GetSelectedPatch;
		pSurfaceTable->m_pfnPatchRebuild = &Patch_Rebuild;
		pSurfaceTable->m_pfnGetTwoSelectedPatch = &QERApp_GetTwoSelectedPatch;
		return 1;
	}
	else if (IsEqualGUID( refGUID, QERAppBSPFrontendTable_GUID ))
	{
		_QERAppBSPFrontendTable *pBSPFrontendTable = static_cast<_QERAppBSPFrontendTable *>(pInterface);
		if ( pBSPFrontendTable->m_nSize != sizeof( _QERAppBSPFrontendTable ) )
		{
			Sys_Printf("wrong m_nSize in plugin-requested _QERAppBSPFrontendTable\n");
			return 0;
		}
		pBSPFrontendTable->m_pfnGetMapName = &QERApp_GetMapName;
		pBSPFrontendTable->m_pfnLoadPointFile = &Pointfile_Check;
		return 1;
	}
	else if (IsEqualGUID( refGUID, QERMessaging_GUID ))
	{
		_QERMessagingTable *pMessagingTable = static_cast<_QERMessagingTable *>(pInterface);
		if ( pMessagingTable->m_nSize != sizeof( _QERMessagingTable ) )
		{
			Sys_Printf("wrong m_nSize in plugin-requested _QERMessagingTable\n");
			return 0;
		}
		pMessagingTable->m_pfnHookWindow = QERApp_HookWindow;
		pMessagingTable->m_pfnUnHookWindow = QERApp_UnHookWindow;
		pMessagingTable->m_pfnGetXYWndWrapper = QERApp_GetXYWndWrapper;
		pMessagingTable->m_pfnHookListener = QERApp_HookListener;
		pMessagingTable->m_pfnUnHookListener = QERApp_UnHookListener;
		return 1;
	}
	else if (IsEqualGUID( refGUID, QERShadersTable_GUID ))
	{
		_QERShadersTable *pShadersTable = static_cast<_QERShadersTable *>(pInterface);
		if ( pShadersTable->m_nSize != sizeof( _QERShadersTable ) )
		{
			Sys_Printf("wring m_nSize in plugin-requested _QERShadersTable\n");
			return 0;
		}
		pShadersTable->m_pfnFreeShaders = QERApp_FreeShaders;
		pShadersTable->m_pfnReloadShaders = QERApp_ReloadShaders;
		pShadersTable->m_pfnReloadShaderFile = QERApp_ReloadShaderFile;
		pShadersTable->m_pfnLoadShaderFile = QERApp_LoadShaderFile;
		pShadersTable->m_pfnHasShader = QERApp_HasShader;
		pShadersTable->m_pfnTry_Shader_ForName = QERApp_Try_Shader_ForName;
		pShadersTable->m_pfnShader_ForName = QERApp_Shader_ForName;
		pShadersTable->m_pfnTry_Texture_ForName = QERApp_Try_Texture_ForName;
		pShadersTable->m_pfnTexture_ForName = QERApp_Texture_ForName2;
		pShadersTable->m_pfnGetActiveShaderCount = QERApp_GetActiveShaderCount;
		pShadersTable->m_pfnColorShader_ForName = QERApp_ColorShader_ForName;
		pShadersTable->m_pfnShader_ForName_NoLoad = QERApp_Shader_ForName_NoLoad;
		return 1;
	}
	return 0;
}

int CPlugInManager::FillFuncTable(CPlugIn *pPlug)
{
  _QERFuncTable_1 *pTable = reinterpret_cast<_QERFuncTable_1*>(pPlug->getFuncTable());
  if (pTable != NULL)
  {
    if (pTable->m_fVersion != QER_PLUG_VERSION)
    {
      Sys_Printf("Radiant plugin manager was built with version %.2f, Plugin %s is version %.2f\n", QER_PLUG_VERSION, pPlug->getVersionStr() , pTable->m_fVersion);
    }
    if (pTable->m_fVersion >= QER_PLUG_VERSION_1)
    {
      pTable->m_pfnCreateBrush = &QERApp_CreateBrush;
      pTable->m_pfnCreateBrushHandle = &QERApp_CreateBrushHandle;
      pTable->m_pfnDeleteBrushHandle = &QERApp_DeleteBrushHandle;
      pTable->m_pfnCommitBrushHandle = &QERApp_CommitBrushHandleToMap;
      pTable->m_pfnAddFace = &QERApp_AddFace;
      pTable->m_pfnAddFaceData = &QERApp_AddFaceData;
      pTable->m_pfnGetFaceData = &QERApp_GetFaceData;
      pTable->m_pfnGetFaceCount = &QERApp_GetFaceCount;
      pTable->m_pfnSetFaceData = &QERApp_SetFaceData;
      pTable->m_pfnDeleteFace = &QERApp_DeleteFace;
      pTable->m_pfnTextureBrush = &QERApp_TextureBrush;
			pTable->m_pfnBuildBrush = &QERApp_BuildBrush;						// PGM
  	  pTable->m_pfnSelectBrush = &QERApp_SelectBrush;					// PGM
			pTable->m_pfnDeselectBrush = &QERApp_DeselectBrush;				// PGM
			pTable->m_pfnDeselectAllBrushes = &QERApp_DeselectAllBrushes;		// PGM
      pTable->m_pfnDeleteSelection = &QERApp_DeleteSelection;
      pTable->m_pfnGetPoints = &QERApp_GetPoints;
      pTable->m_pfnSysMsg = &QERApp_SysMsg;
      pTable->m_pfnInfoMsg = &QERApp_InfoMsg;
      pTable->m_pfnHideInfoMsg = &QERApp_HideInfoMsg;
      pTable->m_pfnPositionView = &QERApp_PositionView;					// PGM
      pTable->m_pfnSelectedBrushCount = &QERApp_SelectedBrushCount;
      pTable->m_pfnAllocateSelectedBrushHandles  = &QERApp_AllocateSelectedBrushHandles;
      pTable->m_pfnReleaseSelectedBrushHandles  = &QERApp_ReleaseSelectedBrushHandles;
      pTable->m_pfnGetSelectedBrushHandle = &QERApp_GetSelectedBrushHandle;
      pTable->m_pfnActiveBrushCount = &QERApp_ActiveBrushCount;
      pTable->m_pfnAllocateActiveBrushHandles = &QERApp_AllocateActiveBrushHandles;
      pTable->m_pfnReleaseActiveBrushHandles = &QERApp_ReleaseActiveBrushHandles;
      pTable->m_pfnGetActiveBrushHandle = &QERApp_GetActiveBrushHandle;
      pTable->m_pfnTextureCount = &QERApp_TextureCount;
      pTable->m_pfnGetTexture = &QERApp_GetTexture;
      pTable->m_pfnGetCurrentTexture = &QERApp_GetCurrentTexture;
      pTable->m_pfnSetCurrentTexture = &QERApp_SetCurrentTexture;
      pTable->m_pfnGetEClassCount = &QERApp_GetEClassCount;
      pTable->m_pfnGetEClass = &QERApp_GetEClass;
      pTable->m_pfnResetPlugins = &QERApp_ResetPlugins;
    }
    // end of v1.00
    if (pTable->m_fVersion >= 1.5f)
    {
      // v1.50 starts
      pTable->m_pfnLoadTextureRGBA = &QERApp_LoadTextureRGBA;
      // end of v1.50
    }
	if (pTable->m_fVersion >= 1.7f)
	{
		pTable->m_pfnGetEntityCount = &QERApp_GetEntityCount;
		pTable->m_pfnGetEntityHandle = &QERApp_GetEntityHandle;
		pTable->m_pfnGetEntityKeyValList = &QERApp_GetEntityKeyValList;
		pTable->m_pfnAllocateEpair = &QERApp_AllocateEpair;
		pTable->m_pfnSetEntityKeyValList = &QERApp_SetEntityKeyValList;
		pTable->m_pfnAllocateEntityBrushHandles = &QERApp_AllocateEntityBrushHandles;
		pTable->m_pfnReleaseEntityBrushHandles = &QERApp_ReleaseEntityBrushHandles;
		pTable->m_pfnGetEntityBrushHandle = &QERApp_GetEntityBrushHandle;
		pTable->m_pfnCreateEntityHandle = &QERApp_CreateEntityHandle;
		pTable->m_pfnCommitBrushHandleToEntity = &QERApp_CommitBrushHandleToEntity;
		pTable->m_pfnCommitEntityHandleToMap = &QERApp_CommitEntityHandleToMap;
		pTable->m_pfnSetScreenUpdate = &QERApp_SetScreenUpdate;
		pTable->m_pfnSysUpdateWindows = &Sys_UpdateWindows;
		pTable->m_pfnBuildBrush2 = &QERApp_BuildBrush2;
		pTable->m_pfnReadProjectKey = &QERApp_ReadProjectKey;
		pTable->m_pfnScanFileForEClass = &QERApp_ScanFileForEClass;
		pTable->m_pfnRequestInterface = &QERApp_RequestInterface;
		pTable->m_pfnErrorMsg = &QERApp_ErrorMsg;
		pTable->m_pfnLoadFile = &QERApp_LoadFile;
		pTable->m_pfnExpandReletivePath = &QERApp_ExpandReletivePath;
		pTable->m_pfnQE_ConvertDOSToUnixName = &QE_ConvertDOSToUnixName;
		pTable->m_pfnHasShader = &QERApp_HasShader;
		pTable->m_pfnTexture_LoadSkin = &Texture_LoadSkin;
		pTable->m_pfnRadiantFree = &QERApp_RadiantFree;
		pTable->m_pfnGetGamePath = &QERApp_GetGamePath;
		pTable->m_pfnGetQERPath = &QERApp_GetQERPath;
		// patches
		pTable->m_pfnAllocateActivePatchHandles = &QERApp_AllocateActivePatchHandles;
		pTable->m_pfnAllocateSelectedPatchHandles = &QERApp_AllocateSelectedPatchHandles;
		pTable->m_pfnReleasePatchHandles = &QERApp_ReleasePatchHandles;
		pTable->m_pfnGetPatchData = &QERApp_GetPatchData;
		pTable->m_pfnDeletePatch = &QERApp_DeletePatch;
		pTable->m_pfnCreatePatchHandle = &QERApp_CreatePatchHandle;
		pTable->m_pfnCommitPatchHandleToMap = &QERApp_CommitPatchHandleToMap;
	}
	return true;
  }
  else
  {
    Sys_Printf("Unable to load %s because the function tables are not the same size\n", pPlug->getVersionStr());
  }
  return false;
}

CPlugIn * CPlugInManager::PluginForModule(HMODULE hPlug)
{
	int i;
	for( i=0; i!=m_PlugIns.GetSize(); i++ )
	{
		if ( reinterpret_cast<CPlugIn *>(m_PlugIns[i])->GetDLLModule() == hPlug )
			return reinterpret_cast<CPlugIn *>(m_PlugIns[i]);
	}
	return NULL;
}

