/*  dsp.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include "ObjProGen/cpyrght_exe.h"
#include <stream.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#ifdef __NT_VC__
#include <process.h>
#endif

#include "sysintfc.h"
#include "dspcom.h"
#include "remmen.h"
#include "cgidbg.h"
#include "syssig.h"
#include "baseio.h"
#include "grphio.h"
#include "hrdarth.h"

#include "shared.h"
#include "usercom.h"
#include "plotdatd.h"
// #include "menucmd.h"

#include "dynmnu.h"
#include "help.h"
#include "dyntextd.h"
#include "menucmdd.h"
#include "meninit.h"
#include "intfccomd.h"
#include "intfc.h"
#include "yacintfc.h"
#include "dspsig.h"
#include "dsp.h"
#include "mkstr.h"

#include "remcom.h"
#include "prognm.h"
#include "dspe_app.h"

const char * DebugOutFile = "dsp.messages";

DspCurrentState * DspExecState = 0 ;

char * GraphicsString[InputEndOfInputTypes];

const char * ProgramName = "DSP++" ;
const char * ThisProc = "DSP" ;

class SharedSegment * ReadSeg ;
class SharedSegment * WriteSeg ;

static void Test(int action,PacketHeader * Head = 0, const char * data = 0) ;

static LockProcessing = 1;

int ComSetup = 0;

FILE * error_file = 0 ;

void main(int argc,char * argv[])
{
	char * Check = getenv("arg_val");
	if (Check) {
		// cerr << "Check = `" << Check << "'\n" ;
		int Count = 3 ;
		for (char * pt = Check; *pt; pt++) if (*pt == ' ') Count++;
		// LogOut << "Count = " << Count << "\n" ;
		// cerr << "Count = " << Count << "\n" ;
		char ** List = new char * [Count+1];
		char * prev = Check ;
		int Index = 0 ;
		for (pt = Check; 1 ; pt++) if (!*pt || *pt == ' ') {
			int Length = pt - prev ;
			List[Index] = new char[Length+1];
			strncpy(List[Index],prev,Length);
			List[Index++][Length] = '\0' ;
			// cerr << "List[" << Index-1 << "] = `" << List[Index-1] << "'\n" ;
			// LogOut<<"List[" << Index-1 << "] = `" << List[Index-1] << "'\n" ;
			if (!*pt) break ;
			prev = pt+1 ;
		}
		List[Index] = 0 ;
		argv = List ;
	}  // else LogOut<< "arg_val not found\n" ;

	// error_file = freopen("error_file","w",stderr);
	SetNewHandler();
	for (int i = 0 ; i < InputEndOfInputTypes; i++)
		GraphicsString[i]= new char[BufSize];

	int32 ReadBufId ;
	int32 WriteBufId ;
	int32 ParentId ;
	int32 ChildId ;
	int write_file_desc = 0 ;
	int read_file_desc = 0 ;

	sscanf(Concatenate(argv[1]),"%x",&ReadBufId);
	sscanf(Concatenate(argv[2]),"%x",&WriteBufId);
	sscanf(Concatenate(argv[3]),"%x",&ParentId);
	sscanf(Concatenate(argv[4]),"%x",&ChildId);
	sscanf(Concatenate(argv[7]),"%x",&write_file_desc);
	sscanf(Concatenate(argv[8]),"%x",&read_file_desc);
	
	// cerr << "argv[7] = `" << argv[7] << "'\n" ;
	// cerr << "argv[8] = `" << argv[8] << "'\n" ;
	State.log_file(Concatenate(argv[11]));
	// LogOut << "DSP::Read ID = 0x"  << hex(ReadBufId) << "\n" ;
	// LogOut << "DSP::Write ID = 0x"  << hex(WriteBufId) << "\n" ;
	// LogOut << "DSP::Parent ID = 0x"  << hex(ParentId) << "\n" ;

	// LogOut << "Before WriteSeg\n" ;
#ifdef OPD_PIPES
	WriteSeg = new PipeCommunication(write_file_desc,1);
	ReadSeg = new PipeCommunication(read_file_desc);
	// cerr << "Created segments dsp.\n" ;
#else
	WriteSeg = new SharedSegment(SharedWriteOnly ,WriteBufId,write_file_desc);
	// LogOut << "Created write segment\n" ;
	ReadSeg = new SharedSegment(SharedReadOnly ,ReadBufId);
	// LogOut << "Created read segment\n" ;
	while (ReadSeg->IsNotOpen()) sleep(1);
#endif
	// LogOut << "ReadSeg open\n" ;
	Test(0) ;

	while (LockProcessing) {
		ReadSeg->ReadPacket();
		sleep(1);
	}
	DspExecState = new DspCurrentState(ParentId,ChildId);
	SetDspSignals() ;
	ComSetup = 1 ;
#ifdef OPD_PIPES
	SharedSegment::set_comm_active();
#else
	if (!TheCommControl) TheCommControl = new CommControl ;
	TheCommControl->SetCommActive();
#endif
	char Temp=TheArithType ;
    PacketHeader Head(PacketControl,PacketControlX_windows,sizeof(Temp));
	WriteSeg->WritePacket(Head,&Temp);
	while (!State.IsX()) ReadSeg->ReadPacket();
	DspMain();
}


void ExitCleanUp()
{
	// LogOut << "ExitCleanUp\n" ;
#ifndef OPD_PIPES
	delete TheCommControl;
	TheCommControl = 0;
#endif
	delete ReadSeg ;
	// LogOut << "Read deleted\n" ;
	ReadSeg = 0;
	sleep(1) ;
	delete WriteSeg ;
	// LogOut << "Write deleted\n" ;
	WriteSeg = 0;
}

static void LocalComCleanUp()
{
	// LogOut << "LocalComCleanUp\n" ;
	PacketHeader Head(PacketControl,PacketControlExit);
	WriteSeg->WritePacket(Head);
	sleep (2) ; // allow enough time for message to be sent before
		    // deleting buffers
	ExitCleanUp();
	// LogOut << "LocalComCleanUp exit\n" ;
}

void this_is_a_dummy_to_force_linkage() ;

void ExitMinimalCleanUp()
{
	LocalComCleanUp();
	exit(1);
	this_is_a_dummy_to_force_linkage() ;
}

void ExitAndCleanUp()
{
	// LogOut << "ExitAndCleanUp\n" ;
	State.set_exiting();
	State.ClearInteractive();
	AllEntityLists->DeleteAll();
		// Call the destructors for all created objects
	LocalComCleanUp();
	exit(0) ;
}

void SignalCleanUp()
{
	ExitAndCleanUp();
}
	

static void ProcessControlPacket(PacketControlOptions Opt)
{
	switch(Opt) {
case PacketControlExit:
		// LogMsg("DSP::Exit packet received");
		ExitCleanUp() ;
		// LogOut << "calling exit\n" ;
		exit(0);
		break ;
case PacketControlX_windows:
	{
		// LogOut << "set X_windows\n" ;
		State.SetX();
		break ;
	}
case PacketControlMenuCommandComplete:
case PacketControlInformationComplete:
default:
		TheLog << "***********DSP::Bad Process control Packet = " << (int) Opt << "\n";
		break;
	}
}

void DspCurrentState::MenuCommandStart()
{
	ExecutingMenuCommands++;
}


void DspCurrentState::InformationComplete()
{
	PacketHeader Head(PacketControl,PacketControlInformationComplete);
	WriteSeg->WritePacket(Head);
}

void DspCurrentState::MenuCommandComplete()
{
	PacketHeader Head(PacketControl,PacketControlMenuCommandComplete);
	WriteSeg->WritePacket(Head);
	if (ExecutingMenuCommands < 1)
		DbgError("DspCurrentState::MenuCommandComplete","bad count");
	ExecutingMenuCommands-- ;
}


void ProcessPacket(PacketHeader& Head, const char * Data,PacketHeader *)
{
/*
 *	LogOut << "ProcessPacket, type = " << (int) Head.ThePacketType <<
 *		", Id = " << (int) Head.Identifier << ", Size = " <<
 *		(int) Head.DataSize << "\n";
 */
	if (LockProcessing) if (Head.ThePacketType != PacketTestMessage)
		DbgError("DSP:ProcessPacket","Test message failure");
	static char TheBuffer[MaxPacketSize+1];
	const char * Buffer = Data ;
	if (Buffer) {
		for (int i = 0 ; i < Head.DataSize;i++) TheBuffer[i] = Data[i];
		TheBuffer[Head.DataSize] = '\0' ;
		Buffer = TheBuffer ;
		// LogOut << "ProcessPacket:: message is" << Buffer << "\n" ;
	}
	int Id ;
	switch (Head.ThePacketType) {
case PacketTestMessage:
		Test(1,&Head,Data) ;
		break ;
case PacketControl:
		ProcessControlPacket((PacketControlOptions)Head.Identifier);
		break ;
case PacketRemoteCommand: 
/*
 *		LogOut << "PacketRemoteCommand:" << Head.Identifier <<
 *			"[" << Head.DataSize << "\n" ;
 */
		DspExecState->MenuCommandStart();
		// LogOut << "Back from DspExecState\n";
		InterpretMenuCommand(Head,Buffer) ;
		// LogOut << "Back from InterpterMenuCommand\n";
		DspExecState->MenuCommandComplete();
		// LogOut << "Back from DspExecComplete\n";
		break ;
case PacketGraphicsStringSend:
		Id = Head.Identifier ;
		if (GraphicsStringAvailable[Id]) {
			TheLog << "*******Conflicting strings:\n`" << GraphicsString[Id]
				<< "'\n`" << Buffer << "'\n" ;
			break ;
		}

		// should only get an empty string when attempting to clear
		// read after an abort dsp request
		if (Head.DataSize && Buffer) {
			strcpy(GraphicsString[Id],Buffer);
			GraphicsStringAvailable[Id] = 1;
		} else if (DspExecState->IsBusy()) if (!State.IsError())
			State.Error("user termination");
		break ;
case PacketWindowPlotControl:
		ThePlotController.PlotServer(Head,Buffer);
		break ;
case PacketDynamicMenuControl:		// control dynamic menus
		// TheMenuServer.MenuServer(Head,Buffer) ;
		break ;
case PacketHelpControl:		// set help level
		HelpDo.HelpServer(Head,Buffer);
		break ;
case PacketWindowTextControl:	// set up a dynmaic text window
		if (!TheDynamicTextController) DbgError("ProcessPacket",
			"TextController void");
		TheDynamicTextController->TextServer(Head,Buffer);
		break ;
case PacketInterfaceControl:
		InterfaceControl.Server(Head,Buffer);
		break ;
case PacketNetworkEdit:
		DspApplication::network_edit(Head.Identifier,Data,Head.DataSize);
		break ;
case PacketWindowText:
case PacketWindowPlotData:
default:
		TheLog << "************DSP::Bad Packet = " << Head.ThePacketType << "\n";
		break;
	}
}

// Routine to exchange messges to insure process communicaition is working
static void Test(int action,PacketHeader * Head, const char * data)
{
	// Send and recieve test messages
	// Include current process ID to insure we are getting the message
	// we send
	// LogOut << "Dsp:Test(" << action << ",...)\n" ;

	static char BufSend[64] ;
	static int Leng ;
	const MyId = 2 ;

	switch (action) {
case 0:
		{
			int id = getpid();
			strcpy(BufSend,hex(id)) ;
			Leng = strlen(BufSend) + 1;
			PacketHeader SendHead(PacketTestMessage,MyId,Leng) ;
			WriteSeg->WritePacket(SendHead,BufSend) ;
		}
		break ;
case 1:
		if (Head->Identifier == MyId) {
			if (strcmp(BufSend,data) || Leng != Head->DataSize) {
				TheLog << "Message sent :: " << BufSend <<"\n";
				TheLog << "Message rec :: " << data <<"\n";
				DbgError("DSP",
				"Failed to recieve back correct init test");
			}
			LockProcessing = 0;
		} else WriteSeg->WritePacket(*Head,data);
		
	}
	
	
}

const LocBufSize = MaxPacketSize ;
static int OldSelector = -1;
static char buffer[MaxPacketSize+1];
static char * BufferPtr = buffer ;

static void LogBuffer(const char * buffer)
{
	for (const char * ptr = buffer ; *ptr ; ptr++) {
		if (*ptr == '\n' || *ptr == '\t' || *ptr == '\r' || *ptr ==
			'\b') continue ;
		if (!isprint(*ptr)) {
			TheLog << "First non-printable is 0x" << hex <<  (int) *ptr <<
				dec << "\n" ;
			TheLog << "Sending non printable buffer\n" ;
			return ;
		}
	}
	// LogMsg("Writing buffer:",buffer);
}

static void WriteBuffer()
{
#ifndef OPD_PIPES
	if (!TheCommControl) {
		// LogMsg("WriteBuffer = no Comm- buffer dropped");
		return ;
	}
#endif
	int GuiWindowIndex = OldSelector ;
	// if (GuiWindowIndex >= OutputEndOfOutputTypes) GuiWindowIndex =
	//	TheDynamicTextController->GetWindowId(GuiWindowIndex);
/*
 *	 LogOut << "GuiWindowIndex = " << GuiWindowIndex << ", OldSlector = "
 *		<< OldSelector << "\n" ;
 */
	PacketHeader Head(PacketWindowText,GuiWindowIndex,BufferPtr-buffer);
	if (buffer[0]) WriteSeg->WritePacket(Head,buffer);
	*BufferPtr++ = '\0' ;
	// LogBuffer(buffer);
	// LogMsg("DSP::WriteBuffer - Wrote packet::",buffer);
	BufferPtr = buffer ;
	OldSelector = -1 ;
}

int WindowWrite(OutputType WindowSelector,char c)
{
/*
 * 	LogOut << "OutputType = " << WindowSelector << "(" << OldSelector <<
 *		"):" << c << "\n" ;
 */

    switch (WindowSelector) {
case OutputMenu:
case OutputPlot:
case OutputAcknowledge:
		WindowSelector = OutputPrompt ;
		break ;
case OutputHelp:
case OutputPrompt:
case OutputCppHelp:
case OutputCppEntry:
		break ;
case OutputUndefined:
case OutputEndOfOutputTypes:
case OutputNotInitialized:
/// default: Using default here will not work
	{
		static int FirstTime = 1 ;
		if (FirstTime) {
			TheLog << "::WindowWrite Bad type = " << (int) WindowSelector << "\n" ;
			FirstTime = 0 ;
		}
		if (c == '\n') FirstTime = 1 ;
		static char * Buf= "X";
		Buf[0] = '*' ;
		if (isascii(c)) if (isprint(c)) Buf[0] = c;
		TheLog << Buf ;
        TheLog << "WindowWrite bad type\n";
	}
    }


/*
 *	if (WindowSelector < OutputEndOfOutputTypes)
 *		if (WindowSelector != OutputHelp && WindowSelector !=
 *		OutputCppEntry) WindowSelector = OutputPrompt ;
 */
	if (OldSelector > -1) if (OldSelector != WindowSelector) WriteBuffer() ;
	OldSelector = WindowSelector ;
	if (BufferPtr > buffer + LocBufSize - 3) WriteBuffer() ;
	*BufferPtr++ = c ;
	if (c == '\n' || c == '\b' ) WriteBuffer() ; 
	return 0;
}

static int CheckLocal(InputType type, char * Result)
{
	const char * Line ;
	if (Line = State.ReadLocalFile(type)) {
		strcpy(Result,Line);
		return 1 ;
	}
	return 0;
}

char * GetGraphicsBuf(enum InputType type, const char * Prompt,
	const char * P1, const char * P2, const char * P3, const char * P4)
{
	char Buf[MaxPacketSize+1];
	const NumberStringsPlus = 6 ;
	const char * PromptArray[NumberStringsPlus];
	int i = 0 ;
	PromptArray[i++] = Prompt ;
	PromptArray[i++] = P1 ;
	PromptArray[i++] = P2 ;
	PromptArray[i++] = P3 ;
	PromptArray[i++] = P4 ;
	PromptArray[i++] = 0 ;
	
	if (i != NumberStringsPlus) DbgError("GetGraphicsBuf","bad size");
	Buf[0] = '\0' ;
	for (const char ** Pt = PromptArray; *Pt; Pt++) strcat(Buf,*Pt); 
	// LogOut << "GetGrBuf - Buf is `" << Buf << "'\n." ;
	return GetGraphicsRawBufLine(type,Buf);
}

char * GetGraphicsRawBufLine(enum InputType type,const char * Prompt)
{
	// LogOut<<"GetGraphicsRawBufLine(" << type << ", \"" << Prompt << "\")\n" ;
	static char * RawBuf[InputEndOfInputTypes];
	static int init = 1;
	static int PromptRequestNeeded = 1 ;
	if (init) {
		init = 0;
		for (int i = 0 ; i < InputEndOfInputTypes; i++) 
			RawBuf[i] = new char[BufSize] ;
	}
	if (CheckLocal(type,RawBuf[type])) {
		// LogOut << "Did local read\n" ;
		PromptRequestNeeded = 0 ;
		return RawBuf[type];
	}

	if (PromptRequestNeeded || type != InputCppEntry) {
		int Length = 0;
		if (Prompt) Length = strlen(Prompt);
		if (Length > MaxPacketSize) {
			Length = MaxPacketSize ;
			TheLog << "******** Prompt TRUNCATED - was:\n" <<
				Prompt <<"\n";
		}
		PacketHeader Head(PacketGetString,type,Length);
		// LogOut << "PacketGetString, " << type << " `" << Prompt << "'\n" ; 
		WriteSeg->WritePacket(Head,Prompt);
		PromptRequestNeeded = 0;
		// LogForm("Wrote read request = %d",type);
	}

	for (;;) {
		// if (CheckLocal(type,RawBuf[type])) return RawBuf[type];
		if (State.IsError()) {
			RawBuf[type][0] = 0 ;
			return RawBuf[type] ;
		}
		CheckReadAndSleep();
		if (State.IsError()) {
			RawBuf[type][0] = 0 ;
			return RawBuf[type] ;
		}
		if (GraphicsStringAvailable[type]) {
		//	LogMsg("GetGraphicsRawBufLine::",GraphicsString[type]);
			strcpy(RawBuf[type],GraphicsString[type]);
			GraphicsStringAvailable[type] = 0 ;
		//	LogMsg("GetGraphicsRawBufLine:returning:",RawBuf[type]);
			PromptRequestNeeded = 1 ;
			return RawBuf[type];
		}
	}
}

int GraphicsReturnOrQuit(OutputType)
{
	// LogForm("ReturnOrQuit(%d) called", typ);
	return 1 ;
}

void GraphicsReturnToContinue(OutputType)
{
	// LogForm("ReturnToContinue(%d) called", typ);
}




