// main.cpp
//
// Copyright 1997 Diehl Graphsoft, Inc.

#include "stdafx.h"
#include "Resource.h"

#include "GSESetup.h"

#include "PurgeDlg.h"
#include "GSString.h"

///////////////////////////////////////////////////////////////////////////////
// Callbacks

//-----------------------------------------------------------------------------
extern "C" void DoDeleteUnusedLayers(Handle h, CallBackPtr cbp)
	//If a layer has no objects in it, delete it
{
	GSCalledRoutineSetup(cbp);

	if (h && GS_GetObjectType(cbp, h) == layerNode && GS_GetObjectType(cbp, GS_FirstMemberObj(cbp, h)) == termNode) { 
		if (h != GS_GetCurrentLayer(cbp)) 
			GS_DeleteObject(cbp, h);
		else
			AfxMessageBox(IDS_ACTIVELAYER, MB_OK | MB_ICONEXCLAMATION);
	}
}

//-----------------------------------------------------------------------------
extern "C" void DoCreateClassTable(Handle h, CallBackPtr cbp, CWordArray *classTable)
// Fill ClassTable with the Class IDs of each object found by ForEachObject.
{
	GSCalledRoutineSetup (cbp);
	
	if (h) {
		BOOL found = FALSE;
		short classID = GS_GetObjectClass(cbp, h);
		for (short i = 0; i < classTable->GetSize(); i++) {
			if (classID == (*classTable)[i]) {
				found = TRUE;
				break;
			}
		}
		if (!found)
			classTable->Add(classID);
	}
}

//-----------------------------------------------------------------------------
extern "C" void DoCreateSymbolTable(Handle h, CallBackPtr cbp, CDWordArray *symbolTable)
// Fill SymTable with the Symbol Instances found by ForEachObject
{
	GSCalledRoutineSetup (cbp);
	
	if (h) {
		if (GS_GetObjectType(cbp, h) == symNode) {
			BOOL found = FALSE;
			Handle symbolDef = GS_GetDefinition(cbp, h);
			for (short i = 0; i < symbolTable->GetSize(); i++) {
				if (symbolDef == (Handle)( (*symbolTable)[i] ))	{
					found = TRUE;
					break;
				}
			}
			if (!found) {
				symbolTable->Add((DWORD)symbolDef);
			}
		}
	}
}

//-----------------------------------------------------------------------------
extern "C" void DoCreateRecordFormatTable(Handle h, CallBackPtr cbp, CDWordArray *recFormatTable)
// Fill RecordFormatTable with the Record Format Definitions of the
// record instances found by ForEachObject
{
	GSCalledRoutineSetup (cbp);
	
	if (h) {
		Handle recHdle = GS_FindAuxObject(cbp, h, recordNode);
		if (recHdle) {
			BOOL found = FALSE;
			Handle recFormat = GS_GetDefinition(cbp, recHdle);
			if (recFormat) {
				for (short i = 0; i < recFormatTable->GetSize(); i++) 
					if (recFormat == (Handle)( (*recFormatTable)[i] )) {
						found = TRUE;
						break;
					}
				if (!found)
					recFormatTable->Add((DWORD)recFormat);
			}
		}
	}
}


///////////////////////////////////////////////////////////////////////////////
class CPurgeData
{
public:
	CPurgeData(CallBackPtr cbp);

	BOOL GetData();
	void DoPurge();

protected:
	CallBackPtr	m_cbp;
	BOOL		m_bLayers, m_bClasses, m_bSymbols, m_bRecords;

	void DoDeleteUnusedClasses(CWordArray &classTable);
	void DeleteUnusedSymbolDefs(Handle symbolLibHdr, CDWordArray &symbolTable);
	void DeleteUnusedRecordFormats(Handle drwgHdr, CDWordArray &recFormatTable);
};

///////////////////////////////////////////////////////////////////////////////
CPurgeData::CPurgeData(CallBackPtr cbp)
{
	m_cbp = cbp;
	m_bLayers = m_bClasses = m_bSymbols = m_bRecords = FALSE;
}

///////////////////////////////////////////////////////////////////////////////
BOOL CPurgeData::GetData()
{
	BOOL bOK;
	CWnd *parent = GS_GetCWndParent(m_cbp);
	{
		CPurgeDlg dlg(m_cbp, parent);
		dlg.m_bLayers = m_bLayers;
		dlg.m_bClasses = m_bClasses;
		dlg.m_bSymbols = m_bSymbols;
		dlg.m_bRecords = m_bRecords;

		bOK = ( dlg.DoModal() == IDOK );
		if (bOK) {
			m_bLayers = dlg.m_bLayers;
			m_bClasses = dlg.m_bClasses;
			m_bSymbols = dlg.m_bSymbols;
			m_bRecords = dlg.m_bRecords;
		}
	}
	GS_ReleaseCWndParent(m_cbp, parent);

	return bOK;
}

///////////////////////////////////////////////////////////////////////////////
void CPurgeData::DoDeleteUnusedClasses(CWordArray &classTable)
// Search the classTable for each class ID found
// here and delete those that are not in the table
{
	Str31			className;

	for (short i = 2; i <= GS_MaxClassID(m_cbp); i++) {
		BOOL found = FALSE;
		for (short j = 0; j < classTable.GetSize(); j++) 
			if (i == classTable[j]) {
				found = true;
				break;
			}
		if (!found) {
			GS_ClassIDToName(m_cbp, i, className);
			GS_DeleteClass(m_cbp, className);
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
void CPurgeData::DeleteUnusedSymbolDefs(Handle symbolLibHdr, CDWordArray &symbolTable)
// Search the symbolTable for each Symbol Definition found
// here and delete those that are not in the table
{
	Handle			nextObj;
	
	Handle firstMemObj = GS_FirstMemberObj(m_cbp, symbolLibHdr);
	if (firstMemObj) {
		do {
			nextObj = GS_NextObject(m_cbp, firstMemObj);
			
			if (GS_GetObjectType(m_cbp, firstMemObj) == symDefNode) {
				BOOL found = FALSE;
				for (short i = 0; i < symbolTable.GetSize(); i++) 
					if (firstMemObj == (Handle) symbolTable[i]) {
						found = TRUE;
						break;
					}
					
				if (!found)
					GS_DeleteObject(m_cbp, firstMemObj);
			}
			else if (firstMemObj && GS_GetObjectType(m_cbp, firstMemObj) == groupNode) {
				DeleteUnusedSymbolDefs(firstMemObj, symbolTable);
			}
			else if (firstMemObj && GS_GetObjectType(m_cbp, firstMemObj) == termNode)
				return;
			firstMemObj = nextObj;
		} while (nextObj != NULL);
		
	}
}

///////////////////////////////////////////////////////////////////////////////
void CPurgeData::DeleteUnusedRecordFormats(Handle drwgHdr, CDWordArray &recFormatTable)
// Search the recFormatTable for each Record Format found
// here and delete those that are not in the table
{
	Handle			dataHdle;
	Handle			nextObj;
	
	if (drwgHdr) {
		Handle auxHdle = GS_FirstAuxObject(m_cbp, drwgHdr);
		if (auxHdle) {
			while (auxHdle && GS_GetObjectType(m_cbp, auxHdle) != dataNode)
				auxHdle = GS_NextObject(m_cbp, auxHdle);
				
			if (auxHdle && GS_GetObjectType(m_cbp, auxHdle) == dataNode)
				dataHdle = auxHdle;
			else
				dataHdle = 0;
		}
			
		if (dataHdle) {
			Handle firstMemObj = GS_FirstMemberObj(m_cbp, dataHdle);
			
			if (firstMemObj) {			
				do {
					nextObj = GS_NextObject(m_cbp, firstMemObj);
					if (GS_GetObjectType(m_cbp, firstMemObj) == formatNode) {
						BOOL found = FALSE;
						for (short i = 0; i < recFormatTable.GetSize(); i++) 
							if (firstMemObj == (Handle) recFormatTable[i]) {
								found = TRUE;
								break;
							}
						if (!found) {
							GS_DeleteObject(m_cbp, firstMemObj);
						}
					}
					else if (firstMemObj && GS_GetObjectType(m_cbp, firstMemObj) == termNode)
						return;
					firstMemObj = nextObj;
				} while (nextObj != NULL);
			}
		}
	}
}


///////////////////////////////////////////////////////////////////////////////
void CPurgeData::DoPurge()
{
	// Remove unused layers.
	if (m_bLayers) {
		GS_ForEachLayer(m_cbp, (GS_ForEachObjectProcPtr) DoDeleteUnusedLayers, NULL);
	}

	// Delete unused classes.
	if (m_bClasses) {
		CWordArray classTable;
		
		GS_ForEachObject(m_cbp, allObjects+descendIntoAll, (GS_ForEachObjectProcPtr) DoCreateClassTable, &classTable);
		DoDeleteUnusedClasses(classTable);
	}

	// Delete unused symbols.
	if (m_bSymbols) {
		CDWordArray symbolTable;
	
		GS_ForEachObject(m_cbp, allObjects+descendIntoAll, (GS_ForEachObjectProcPtr) DoCreateSymbolTable, &symbolTable);		
		Handle symbolLibHdr = GS_GetSymbolLibraryHeader(m_cbp);
		if (symbolLibHdr)
			DeleteUnusedSymbolDefs(symbolLibHdr, symbolTable);
	}

	//Delete Unused Record Formats
	if (m_bRecords) {
		CDWordArray recFormatTable;
		
		GS_ForEachObject(m_cbp, allObjects+descendIntoAll, (GS_ForEachObjectProcPtr) DoCreateRecordFormatTable, &recFormatTable);
		Handle drwgHdr = GS_GetDrawingHeader(m_cbp);
		if (drwgHdr)
			DeleteUnusedRecordFormats(drwgHdr, recFormatTable);
	}	
}

///////////////////////////////////////////////////////////////////////////////
extern "C" long GS_EXTERNAL_ENTRY main(int action, long message1, long &/*userData*/, CallBackPtr cbpParam)
// This is the function called by MiniCad when the Purge menu is activated.
{
	GSExternalSetup(cbpParam);

	switch (action) {
		case kMenuItemDisplayContextualHelp: {
			TXString* helpIdentifier = (TXString*) message1;
			if (helpIdentifier) {
				*helpIdentifier = "net.nemetschek.help.menuitem.000000000468";
				return kMenuHandledEvent;
			}
			break;
		}

		case kMenuInitGlobals : {
				//
				//	This action is called when an workspace is activated and serves the 
				//	primary purpose of allowing the plug-in to define any 
				//	defaults and global information and to initialize data space that 
				//	may be stored in userData.
				//

				// Construct explicitly with 'placement new'. Get memory from the app because
				// memory allocated with normal 'new' disappears when the dll unloads.

			/* Example:

				void* pRawMemory = GS_NewPtr(gCBP, sizeof(FooClass));
				if (pRawMemory) {
					FooClass* pConstructedMemory = (FooClass*) new(pRawMemory) FooClass();	
					userData = (long) pConstructedMemory;
				}
				else {
					userData = nil;
				}
	
			*/

				break;
			}

		case kMenuDisposeGlobals : {
				//
				//	This action is called when a workspace is deactivated and serves the 
				//	primary purpose of allowing the external function to dispose data 
				//	space that may be stored in userData.
				//

				// Destruct explicitly when using 'placement new'

			/* Example:

				if (userData) {
					FooClass* pData = (FooClass*) userData;
					pData->~FooClass();
					GS_DisposePtr(gCBP, pData);
					userData = nil;
				}
	
			*/
				
				break;
			}

		case kMenuDoInterface: {
			/*
				MiniCad passes this action value after the user has chosen a menu 
				item and indicates to the definition function to take the appropriate 
				action(i.e. do whatever the menu item is supposed to do).  If the menu 
				function does not ÒtouchÓ the document(do something which should activate 
				the Save menu item), the definition function should return kMenuDidNotTouchDoc.
			*/
			CPurgeData purge(cbpParam);
			if (purge.GetData()) {
				GS_EnterMoveableWindow(cbpParam, NULL);
				if (::AfxMessageBox(IDS_CONFORM, MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
					purge.DoPurge();
				GS_ExitMoveableWindow(cbpParam, NULL);
			}
			break;
		}
	}

	/*
		The definition should return 0 when a specific return value is not defined.
	*/
	GSExternalSetdown(0);
	return 0;
}
