/*  dynmnud.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <string.h>

#include "newaloc.h"
#include "slist.h"
#include "cgidbg.h"

#include "shared.h"
#include "remcom.h"
#include "intfc.h"
#include "yacintfc.h"

#include "dynmnu.h"


MenuServer * TheMenuServer = 0 ;

// void TestAlloc(const char * msg);

struct QueuedRequest {
	const char * ClassName ;
	const char * MenuItemName ;
	QueuedRequest(const char * class_name, const char * Name)
		{ClassName = class_name ; MenuItemName = Name ;}
} ;

class QueuedRequestList: public SingleList {
public:
	ErrCode Insert(QueuedRequest *nt) {return SingleList::Insert(nt);}
	ErrCode Append(QueuedRequest *nt) {return SingleList::Append(nt);}
	QueuedRequest * Get()   {return (QueuedRequest *) SingleList::Get();}
	QueuedRequest * Pop() {return (QueuedRequest *) SingleList::Pop();}
	QueuedRequest * GetNFromTop(int N) ;
	QueuedRequest * GetNthEntry(int N) ;
	QueuedRequestList(){;}
	int Size(){return SingleList::Size();}
} ;

class QueuedRequestListIterator: public SingleListIterator {
public:
	QueuedRequestListIterator(QueuedRequestList& df):
		SingleListIterator((SingleList&) df){}
	QueuedRequest * operator()()
		{return (QueuedRequest *) Next();}
};

static void SetMenuCommActive()
{
	// LogMsg("In SetMenuCommActive");
	if (!TheMenuServer) TheMenuServer = new MenuServer ;
	TheMenuServer->SetCommActive();
}

MenuServer::MenuServer()
{
#ifdef OPD_PIPES
	IsCommActive = SharedSegment::is_comm_active(SetMenuCommActive) ;
#else
	if (!TheCommControl) TheCommControl = new CommControl ;
	IsCommActive = TheCommControl->IsCommActive(SetMenuCommActive) ;
#endif
	if (!IsCommActive) QueuedRequests = new QueuedRequestList ;
	else QueuedRequests = 0;
}


MenuServer::~MenuServer()
{
	if (!QueuedRequests) return ;
	QueuedRequests->Clear();
	delete QueuedRequests ;
	QueuedRequests = 0;
}

void MenuServer::SendNewMenuItem(const char * ClassName, const char * Name)
{
/*
 *	LogOut << "MenuServer::SendNewMenuItem(" << ClassName << ", "
 *			<< Name << ")\n" ;
 */
	// TestAlloc("before send");
	SendMenuRequest(ClassName, Name, MenuServerNewMenuItem);
}

void MenuServer::SendMenuRequest(const char * ClassName, const char * Name,
	MenuServerCommand Command)
{
	if (State.is_exiting()) return ;
	// LogMsg("MenuSerever::SendMenuRequest for:",Name);
	// LogForm("Command = %d",Command);
	if (!WriteSeg)  if (Command == MenuServerDeleteMenuItem) return;
			// Needed for MenuItem destructors called at program
			// exit
		else DbgError("MenuServer::SendMenuRequest", "no WriteSeg");
	
	int Size = strlen(Name) + strlen(ClassName) + 2 ;
	if (Size > MaxPacketSize) DbgError("MenuServer::SendMenuRequest",
		"Message too large");
	PacketHeader Head(PacketDynamicMenuControl,Command,Size);
	char * Temp = new char[Size];
	strcpy(Temp,ClassName);
	int Concat = strlen(Temp) + 1 ;
	strcpy(Temp+Concat,Name);
	// LogOut << "WritePacket for dynamic menu control\n" ;
	WriteSeg->WritePacket(Head,Temp);
	delete Temp ;
	// LogOut << "Exit SendMenuRequest\n" ;
}


void MenuServer::NewMenuItem(const char * ClassName, const char * Name)
{
	// LogOut << "MenuServer::NewMenuItem" ;
	if (IsCommActive) SendNewMenuItem(ClassName,Name);
	else {
		// LogMsg("Queuing request for: ",Name);
		QueuedRequests->Append(new QueuedRequest(ClassName,Name));
	}
	// LogOut << "Exit NewMenuItem\n" ;
}

void NewMenuItem(const char * ClassName, const char * Name)
{
	if (!TheMenuServer) TheMenuServer = new MenuServer ;
	TheMenuServer->NewMenuItem(ClassName,Name);
}

void MenuServer::DeleteMenuItem(const char * ClassName, const char * Name)
{
/*
 *	LogOut << "Delete = SendMenuRequest(" << ClassName << ", "
 *		<< Name << ", " << MenuServerDeleteMenuItem << ")\n" ;
 */
	SendMenuRequest(ClassName, Name, MenuServerDeleteMenuItem);
}


void MenuServer::SetCommActive()
{
	// LogMsg("In MenuServer::SetCommActive");
	IsCommActive = 1;
	QueuedRequestListIterator Next(*QueuedRequests);
	QueuedRequest * Request ;
	while (Request = Next()) {
		// LogMsg("MenuServer::SetCommActive","sending queued request");
		SendNewMenuItem(Request->ClassName,Request->MenuItemName);
		delete Request ;
	}
}

void MenuServer::DynamicPacketData(PacketHeader&, const char *)
{
	// int Id = Head.Identifier ;
	// InterpretDynamicMenuCommand(Id,Data);
	DbgError("MenuServer::DynamicPacketData",
		"should not be called: not used");
}

static InteractiveEntityList BaseEntities("BaseEntities");

static void MakeListOfPrimitiveEntities()
{
	// Make a list of all interactive entities that are
	// derived from a lower interactive entity
	for (InteractiveEntityList ** Ptr = AllEntityLists->GetAllEntities();
		*Ptr; Ptr++) {
		InteractiveEntity * Ent ;
		InteractiveEntityIterator Next(**Ptr);
		while (Ent = Next()) if (!Ent->GetBaseEntity()) {
/*
 *			LogOut << "Adding Base : " << Ent->GetClassName() <<
 *				"\n" ;
 */
			BaseEntities.Append(Ent);
		}
	}
}

static void SendPairs(InteractiveEntity * Ent, int DoSend=0,
	const char * BaseName=0)
{
	InteractiveEntity * Brother = Ent->GetDerivedEntity();
/*
 *	if (Brother) LogOut << "Derived : " << Brother->GetClassName()
 *		<< "\n" ;
 */
	DoSend = DoSend || Ent->GetMembers();
	while (Brother) {
		const char * UseBaseName = BaseName ;
		if (DoSend) if (!Brother->IsInMenus()) {
			if (!BaseName) UseBaseName = Ent->GetClassName();
		} else {
			// LogOut << "SendMenuRequest for BaseName\n" ;
			TheMenuServer->SendMenuRequest(BaseName?
				BaseName:Ent->GetClassName(),
				Brother->GetClassName(),MenuServerAttachOrphan);
			UseBaseName = 0 ;
		}
		SendPairs(Brother, DoSend, UseBaseName);
/*
 *		LogOut << "DoSend = " << DoSend << ", Brother is : " <<
 *			Brother->GetClassName() << ", Use is : " <<
 *			UseBaseName << "\n" ;
 */
		Brother = Brother->GetBrotherEntity();
	}
}

static void SendAllDerivedPairs()
{
	InteractiveEntityIterator Next(BaseEntities);
	InteractiveEntity * Ent ;
	while (Ent = Next()) {
/*
 *		LogOut << "Checking for pairs with : " << Ent->GetClassName()
 *			<< "\n" ;
 */
		SendPairs(Ent);
	}
}

void FindHomesForOrphanMenus()
{
	// Loop through all interactive entities
	// for each one that has member functions
	// send a message to attach the associated menus to all
	// class objects that directly or indirectly have
	// this interactive entity as a base class.
	// LogOut << "InFindHomesForOrphanMenus\n" ;
	MakeListOfPrimitiveEntities() ;
	SendAllDerivedPairs();
}

