/*  fileios.C   */
/*  Copyright 1990 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "fileios.h"
#include "cgidbg.h"
#include "mkstr.h"
#include "copyright.h"

const BufSize = 1024 ;

static LineOfstream* MakeFileStream(const char * file_name,
	const char * input_file_name, LineOfstream::HeaderOptions options)
{
	if (!file_name) return new LineOfstream ;
	return new LineOfstream(file_name, input_file_name,options);
}

MofStream::MofStream(const char * FirstName, const char * SecondName,
	const char * InputFileName, LineOfstream::HeaderOptions options,
	const char * NodeName):
/*********************
	ostream(base_stream = (new BaseStreamBuf(FirstName,SecondName,
		InputFileName,options,NodeName)))
 ****************/
	base_stream(new BaseStreamBuf(FirstName,SecondName,
        InputFileName,options,NodeName))
{
	rdbuf(base_stream);
	init(base_stream); 
	/* this is redundant but is needed for a bug in g++ */
}

MofStream::MofStream(ObjectsForFile ** Objs, const char * node_name,
	const char * SourceFileName):
		// ostream(base_stream = (new BaseStreamBuf(Objs,node_name,SourceFileName)))
		base_stream(new BaseStreamBuf(Objs,node_name,SourceFileName))
{
	rdbuf(base_stream);
	init(base_stream);
}

MofStream::~MofStream()
{
	delete base_stream ;
}


MofStream& MofStream::flush()
{
	if (ostream::good()) ostream::flush();
	base_stream->Flush();
	return *this ;
}

void MofStream::PushFirstFileSelect(BaseStreamBuf::OnOff on_off)
{
/*
 *	cerr << "Push first " << base_stream->first_file_out()
 *		 << " : " << on_off << "\n" ;
 */
	FirstOnOffStack.Push((int) base_stream->first_file_out());
	FirstFileSelect(on_off) ;
}
	
void MofStream::PopFirstFileSelect()
{
	BaseStreamBuf::OnOff Temp = (BaseStreamBuf::OnOff) FirstOnOffStack.Pop() ;
	FirstFileSelect(Temp) ;
	// cerr << "Pop first " << Temp << "\n" ;
}

void MofStream::PushSecondFileSelect(BaseStreamBuf::OnOff on_off)
{
/*
 *	 cerr << "Push second " << base_stream->second_file_out() <<
 *		" : " << on_off << "\n" ;
 */
	SecondOnOffStack.Push((int) base_stream->second_file_out());
	SecondFileSelect(on_off) ;
}
	
void MofStream::PopSecondFileSelect()
{
	BaseStreamBuf::OnOff Temp = (BaseStreamBuf::OnOff) SecondOnOffStack.Pop() ;
	SecondFileSelect(Temp) ;
	// cerr << "Pop second " << Temp << "\n" ;
}

ObjectsForFile::ObjectsForFile(const char * file,
	const char * nod, const char * input_file_name,
	LineOfstream::HeaderOptions options):
		FileName(file),
		NodeName(nod),
		TheFile(MakeFileStream(file, input_file_name,options))
{
}

BaseStreamBuf::BaseStreamBuf(ObjectsForFile ** objects, const char * node_name,
                const char * input_file_name):
		Objects(objects),
		NodeName(node_name),
		InputFileName(input_file_name)
{
	InitBuffer();
}
		
	
	

BaseStreamBuf::BaseStreamBuf(const char * FirstName, const char * SecondName,
	const char * input_file_name, LineOfstream::HeaderOptions options,
	const char * node_name):
	NodeName(node_name)
{
	const Max = 3 ;
	Objects = new ObjectsForFile *[Max] ;
	int i = 0 ;
	Objects[i++] = new ObjectsForFile(FirstName, NodeName,
		input_file_name,options);
	Objects[i++] = new ObjectsForFile(SecondName, NodeName,
		input_file_name,options);
	Objects[i++]=0;
	if (i != Max) DbgError("BaseStreamBuf::BaseStreamBuf","count error");
	InitBuffer();
}

void BaseStreamBuf::InitBuffer()
{
	// Buffer = new char[BufSize] ;
	// DumpPointers("Before setbuf");
	// setbuf(Buffer,BufSize);
	// setbuf((char *) 0,0);
	ObjectsSize=0;
	for (ObjectsForFile ** Ck = Objects ; *Ck; Ck++) ObjectsSize++;
	const char * FirstName = 0 ;
	const char * SecondName = 0 ;
	if (ObjectsSize) {
		FirstName = Objects[0]->FileName ;
		SecondName = Objects[1]->FileName ;
	}
	FirstFileOut = FirstName ?  On : Off ;
	SecondFileOut = SecondName ? On : Off ;
}

BaseStreamBuf::~BaseStreamBuf()
{
	Flush();
	for (ObjectsForFile ** obj = Objects ; *obj; obj++) 
		delete  (*obj)->TheFile ;
}

ostream& BaseStreamBuf::GetFirstFile()
{
	if (!ObjectsSize)
		DbgError("BaseStreamBuf::GetFirstFile","bad index");
	return *(Objects[0]->TheFile);
}

ostream& BaseStreamBuf::GetSecondFile()
{
	if (ObjectsSize < 2)
		DbgError("BaseStreamBuf::GetSecondFile","bad index");
	return *(Objects[1]->TheFile);
}

ostream& BaseStreamBuf::GetFile(int i)
{
	if (ObjectsSize < i)
		DbgError("BaseStreamBuf::GetFile","bad index");
	return *(Objects[i]->TheFile);
}


void BaseStreamBuf::FirstSelect(BaseStreamBuf::OnOff on_off)
{
	if (!ObjectsSize) return ;
	if (!Objects[0]->TheFile->GetFileName()) return ;
	Flush();
	FirstFileOut = on_off;
}

void BaseStreamBuf::SecondSelect(BaseStreamBuf::OnOff on_off)
{
	if (ObjectsSize < 2) return ;
	if (!Objects[1]->TheFile->GetFileName()) return ;
	Flush();
	SecondFileOut = on_off;
}

void BaseStreamBuf::DumpPointers(const char * Where)
{
	cerr << "\nStreamBuf pointers at: " << Where << "\n" ;
	cerr << "base() = " << (long) base() << "\n" ;
	cerr << "eback() = " << (long) eback() << "\n" ;
	cerr << "ebuf() = " << (long) ebuf() << "\n" ;
	cerr << "egptr() = " << (long) egptr() << "\n" ;
	cerr << "epptr() = " << (long) epptr() << "\n" ;
	cerr << "gptr() = " << (long) gptr() << "\n" ;
	cerr << "pbase() = " << (long) pbase() << "\n" ;
	cerr << "pptr() = " << (long) pptr() << "\n" ;
	cerr << "*******************************\n\n" ;
}

int BaseStreamBuf::Write()
{
	char Buf[BufSize] ;	
	int Size = sgetn(Buf,BufSize);
	// cerr << "Size = " << Size << "\n" ;
	if (!Size) return 0 ;

	if (FirstFileOut==On) (*Objects)->TheFile->Write(Buf,Size);
	if (SecondFileOut==On) {
		for (ObjectsForFile ** obj = Objects+1; *obj ; obj++) {
			(*obj)->TheFile->Write(Buf,Size);
			if ((*obj)->TheFile->fail()) return EOF ;
		}
	}
	setp(base(),ebuf());
	setg(base(),base(),base());
	return 0 ;
}


#undef zapeof
#define      zapeof(c) ((c)&0377)

int BaseStreamBuf::underflow()
{
	// cerr << "MofStream::underflow\n" ;
	return EOF ;
}

int BaseStreamBuf::overflow(int c)
{

	// cerr << "BaseStreamBuff::overflow(" << c << ")\n" ;
	if (Write() == EOF) return EOF ;
	int Return = zapeof(c) ;
	if (c == EOF) return Return ;
	if (FirstFileOut==On) (*Objects)->TheFile->Put(c) ;
	if (SecondFileOut==On) {
		for (ObjectsForFile ** obj = Objects+1; *obj ; obj++) {
			(*obj)->TheFile->Put(c) ;
			if ((*obj)->TheFile->fail()) return EOF ;
		}
	}
	return Return ;
}

void BaseStreamBuf::Flush()
{
	Write();
	for (ObjectsForFile ** obj = Objects; *obj ; obj++)
		if ((*obj)->TheFile->GetFileName()) (*obj)->TheFile->flush();
}


void MofStream::LineOut(int Line)
{
	flush();
	base_stream->LineOut(Line);
}

void BaseStreamBuf::LineOut(int Line)
{
	if (FirstFileOut==On) (*Objects)->TheFile->LineOut(Line);
	if (SecondFileOut==On)
		for (ObjectsForFile ** obj= Objects+1; *obj ; obj++) 
			(*obj)->TheFile->LineOut(Line);
}

LineOfstream::LineOfstream():
	FileName(0),
	InputFileName(0),
	OutStatus(StatusOutput),
	ConvertedFileName(0),
	TheHeaderOptions(None),
	ofstream()
{
}

LineOfstream::LineOfstream(const char * file_name,
	const char * input_file_name, HeaderOptions options):
	FileName(file_name),
	InputFileName(input_file_name),
	OutStatus(StatusOutput),
	ofstream(file_name),
	TheHeaderOptions(options),
	ConvertedFileName(0),
	Line(1)
{
	if (!FileName) return ;
	if (!good()) {
		cerr << "Error creating file `" << FileName << "'.\n" ;
		exit(1);
	}
	if (!(options & NoCopyrightBit)) CopyrightOut() ;
	if (options & HeaderOnceBit) HeaderOnceOut();
}

LineOfstream::~LineOfstream()
{
	if (TheHeaderOptions & HeaderOnceBit) HeaderOnceOutEnd();
}

void LineOfstream::CopyrightOut()
{
	AddCopyright& add = AddCopyright::add_copyright() ;
	const char * fr = 0 ;
	char * delete_name = 0 ;
	if (NodeName) if (*NodeName) fr = delete_name =
		 Concatenate(" for ",NodeName," ",AddCopyright::created_by);
	if (!fr) fr = AddCopyright::created_by ;
	Line += add.write_short(
		*this,FileName,TheHeaderOptions&UserCopyrightBit,fr);
	delete delete_name ;
}


void LineOfstream::LineOut(int input_file_line)
{
	// cerr << "LineOut(" << input_file_line << ")\n" ;
	LineOutStatus ChangeStatus =
		input_file_line < 0 ? StatusOutput : StatusSource ;
	if (OutStatus == ChangeStatus) return ;

	const char * Name = FileName ;
	int LineOut = input_file_line + 1 ;
	OutStatus = ChangeStatus ;
	Line++ ;
	if (input_file_line < 0) LineOut = Line ;
	else Name = InputFileName ;
		
	*this << "#line " << LineOut << " \"" << Name << "\"\n" ;
}

LineOfstream& LineOfstream::Put(int c)
{
	if (c == '\n') Line++;
	put((char) c);
	return *this ;
}

LineOfstream& LineOfstream::Write(const char * ptr, int n)
{
	for (int i = 0 ; i < n ;i++) if (ptr[i] == '\n') Line++;
	write(ptr,n);
	return *this ;
}

char * ConvertName (const char * name)
{
	const char * Temp ;
	const char * R ;
	char c;
	Temp = R = name ;
	int DotCount = 0 ;
	while (c= *R++) {
		if (c == '/') {Temp = R; DotCount = 0 ;}
		if (c == '.') DotCount++ ;
	}
	char * RR ;
	char * Result ;
	const char * DOT = "_DOT_" ;
	int DotLength = strlen(DOT) ;
	RR = Result = new char[strlen(Temp)+DotCount*(DotLength-1)+1];
	while (c= *Temp++) {
		if (!isascii(c)) continue ;
		if (c == '.' ) {
			strcpy(RR,DOT);
			RR+=DotLength ;
		} else {
			if (!islower(c)) *RR++ = c;
			else *RR++ = toupper(c) ;
		}
	}
	*RR++ = '\0' ;
	return Result ;
}

void LineOfstream::HeaderOnceOut()
{
	ConvertedFileName = ConvertName(FileName) ;
	*this << "\n#ifndef " << ConvertedFileName << "\n#define "
		<< ConvertedFileName << "\n" ;
	Line += 3 ;
}

void LineOfstream::HeaderOnceOutEnd()
{
	*this << "\n#endif /* #ifdef " << ConvertedFileName << " */\n" ;
	Line+=2 ;
	// delete ConvertedFileName ;
}

