/*  outfile.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include "cgidbg.h"
#include "ramio.h"
#include "sysconst.h"
#include <stdio.h>
#include <unistd.h>
#ifndef __NT_VC__
#define O_BINARY 0
#else
#include <io.h>
#endif
#include "outfile.h"
#include "sysintfc.h"
#include "sysspec.h"
#include "portable.h"
#include "shared.h"
#include "cgidbg.h"
#include "netlnk.h"
#include "newaloc.h"
#include "usercom.h"
#include "yacintfc.h"
#include "outnode.h"
#include "datfile.h"
#include "cgidbg.h"
#include "dskchnhd.h"
#include "mpacket.h"
#include "mkstr.h"


OutputFile::OutputFile(OutputNode * node, const char * file_name)
{
	// LogOut << "OutputFile::OutputFile(" << file_name << ")\n" ;
	CreatedOk = 0 ;
	delete_file_name = 0 ;
	exp_file_name = 0 ;
	Channels = node->GetIn();
	if (Channels < 1) DbgError("OutputFile::OutputFile","no outputs");
	TheFile = -1;
	FileName = file_name ;
	TheNode = node ;
	BlockSizeInWords = ((OutputNode *)TheNode)->GetFileBlockSize() ;
	ErrCode open_code = open();

	FirstDataSample = 0 ;
	
	BlockSizeInBytes = BlockSizeInWords * sizeof(MachWord) ;

/*
 *	LogOut << "OutputFile::OutputFile dec, Channels = "
 *		<< Channels << ", BlockSize = " << BlockSizeInBytes <<
 *		"\n" ;
 */
	// LogOut << "Start allocate for `" << GetFileName() << "'\n" ;
	Buffers = new MachWord * [Channels];
	LastWriteBlock = new long[Channels] ;
	LastWriteWord = new int16[Channels] ;
	ElementSize = new int32[Channels];
	SeekLastWordUpdate = new long[Channels];
	for (int i = 0 ; i < Channels ; i++)  {
		SeekLastWordUpdate[i] = 0 ;
		Buffers[i] = (MachWord *) new MachWordCast[BlockSizeInWords];
	}
	// LogOut << "End allocate for `" << GetFileName() << "'\n" ;
	FileInit = 0 ;
	initial_reset = 1 ;
	CreatedOk = open_code != FatalError ;
	if (Reset() == FatalError) CreatedOk = 0 ;
	initial_reset = 0 ;
	// LogOut << "OutputFile::OutputFile(\"" << file_name << "\") exit" ;
}

int32 OutputFile::LastWordIndex(int Channel)
{
	return LastWriteBlock[Channel] * BlockSizeInWords +
		LastWriteWord[Channel];
}


int OutputFile::WriteChannelHeader(int Channel)
{
	// LogOut << "WriteChannelHead(" << Channel << ")\n" ;
	NodeOutChannelHeader * ChannelHead = 
		TheNode->GetDiskState(Channel);
/*
 *		*(TheNode->GetInLink(Channel)->GetTiming()),
 *			ElementSize[Channel],LastWordIndex(Channel)) ;
 */
	// ChannelHead->Dump(LogOut);
	if (!PacketWrite(FileEltsChannelHeader, ChannelHead,
		sizeof(*ChannelHead))) return 0 ;
	int32 LastWordInChannel = 0 ;
	int Siz = sizeof(LastWordInChannel) ;
	SeekLastWordUpdate[Channel] = lseek(TheFile,0,SEEK_CUR);
		// does not change file pointer
	if (!IncrementalWrite(&LastWordInChannel,Siz,1)) return 0;
	delete ChannelHead ;
	// ChannelHead.Dump(LogOut);
	return 1;
}

int OutputFile::CheckSeek(long DidSeek)
{
	if (DidSeek >= 0) return 0 ;
	HelpOut << "Seek error in flushing file.\n" ;
	IntOut(DidSeek);
	WriteError();
	return -1 ;
}

ErrCode OutputFile::flush()
{
	// LogOut << "OutputFile::flush\n" ;
	FileInit = 1 ;
	InDestructor = 1 ;
	if (TheFile > 0) {
		for (int i = 0 ; i < Channels ; i++) UpdateChannel(i) ;
		for (i = 0 ; i < Channels; i++) {
			if (!SeekLastWordUpdate[i]) continue ;
			long DoSeek = SeekLastWordUpdate[i] ;
			IntOut(DoSeek);
			long DidSeek = lseek(TheFile, DoSeek, SEEK_SET);
			if (CheckSeek(DidSeek)) return FatalError ;
			long LastWord = LastWordIndex(i);
			if (!DoWrite(&LastWord, sizeof LastWord, 1)) return FatalError ;
/*
 *			LogOut << "Set last write to " << LastWord << " at byte " <<
 *				DoSeek << "\n" ;
 */
		}
	}
	FileInit = 0 ;
	InDestructor = 0 ;
	return OK ;
}

OutputFile::~OutputFile()
{
	// LogOut << "OutputFile::~OutputFile()\n" ;
	if (flush()  >= FatalError) return ;
#ifdef INTERACTIVE
	if (!TheNode) return ;
	if (!TheNode->net_complete()) return ;
#endif
	// LogOut << "OutputFile::~OutputFile() for `" << TheNode->GetName() << "'\n" ;
	FileInit = 1 ;
	InDestructor = 1 ;
	for (int i = 0 ; i < Channels ; i++) {
		delete Buffers[i] ;
		Buffers[i] = 0;
	}
		
	Buffers = 0 ;
	delete LastWriteBlock ;
	LastWriteBlock = 0 ;
	delete LastWriteWord ;
	LastWriteWord = 0 ;
	delete ZeroBuf ;
	ZeroBuf = 0 ;
	delete delete_file_name ;
	delete exp_file_name ;
}

void OutputFile::WriteError()
{
	State.Error("error writing `", FileName ,"' in `",TheNode->GetName(),"'");
	TargetNode::expand_name(FileName,exp_file_name);
	SystemErrorMessage();
}

int OutputFile::DoWrite(const void * Data, int Size, int BinaryFlag)
{
	// LogOut << "DoWrite(" << Size << ")\n" ;
	if (!Size) return 1 ;
	int Written = write(TheFile, Data, Size);

	if (Written != Size) {
		WriteError();
		return 0 ;
	}
	return Written ;
}

int OutputFile::IncrementalWrite(const void * Data, int Size, int BinaryFlag)
{
	int Written = DoWrite(Data,Size,BinaryFlag);
	// LogOut << "IncrementalWrite = " << Written << "\n" ;
	if (!Written) return 0;
	FirstDataSample += Written ;
/*
 *	LogOut << "Writing " << Size << " bytes, total = " <<
 *		FirstDataSample << " " ;
 */
	return 1 ;
}


int OutputFile::StringWrite(const char * TheString, int Length)
{
	return IncrementalWrite(TheString, Length);
}

int OutputFile::DoFileInit()
{
	int PrologueLength = strlen(DataFilePrologue);
	if (!StringWrite(DataFilePrologue, PrologueLength)) return 0 ;
	
	
	DataType arith_type = (DataType) TheArithType ;
	if (TheNode->GetFlags() &8) arith_type = (DataType) ArithType::ArithInt32;
	NodeOutFileHeader Head(arith_type,Channels=TheNode->GetIn(),
		BlockSizeInWords);
	if (!PacketWrite(FileEltsHeader,&Head,sizeof(Head))) return 0 ;

	if (!PacketWrite(FileEltsNodeName, TheNode->GetName(),
		strlen(TheNode->GetName()))) return 0;

	const char * Caption = TheNode->GetCaption();
	int WriteRet  ;
	if (Caption) WriteRet = PacketWrite(FileEltsCaption, Caption,
		strlen(Caption));
	else WriteRet = PacketWrite(FileEltsCaption, 0, 0);
	if (!WriteRet) return 0 ;
	for (int i = 0 ; i < Channels ; i++) {
		ElementSize[i] = TheNode->GetInEltSize(i);
		if (!WriteChannelHeader(i)) return 0 ;
	}
	FileInit = 1 ;
	return 1 ;
}

ErrCode OutputFile::open(int increment_flag)
{
	// LogOut << "OutputFile::open(" << increment_flag << ")\n" ;
	if (TheFile > 0) {
		flush();
		close(TheFile);
	}
	if (increment_flag) {
		int Length = 0 ;
		if (FileName) Length = strlen(FileName);
		if (Length < 1) {
			State.Error("null file name to increment in `OutputFile::Reset'");
			return FatalError ;
		}
		char X = FileName[Length-1] ;
		char Y = X+1 ;
		int AlNumY = isalnum(Y);
		if (!AlNumY) if (isdigit(X)) Y = 'a' ;
		else Y = '0' ;
		char * NextName = Concatenate(FileName);
		NextName[Length-1] = Y ;
		FileName = NextName ;
		delete delete_file_name ;
		delete_file_name = NextName ;
	}
	const char * new_file_name = FileName ;
	int flags = 0 ;
	if (!(TheNode->GetFlags()&1)) flags |= 2 ;
	const char * exp_name = TargetNode::create_file(
		&new_file_name,TheFile,(const char *) 0,flags);
	// LogOut << "TheFile = " << TheFile << " `" << exp_name << "'\n" ;
	if (!exp_name) return FatalError;
	delete exp_file_name ;
	exp_file_name = 0 ;
	if (exp_name != new_file_name) exp_file_name = Concatenate(exp_name);
	if (FileName != new_file_name) {
		delete delete_file_name ;
		FileName = delete_file_name = Concatenate(new_file_name);
	}
	return OK ; 
}

ErrCode OutputFile::Reset()
{
	// LogOut << "OutputFile::Reset()\n" ;
	if (FileInit) return OK ;
	if (State.is_exiting()) return OK ; 
	ErrCode ret = OK ;
	if (!initial_reset) ret = open(1);
	FileInit = 0 ;
	ZeroBuf = 0 ;
	NextChannelToWrite = 0 ;
	HighestChannelToWrite = 0 ;
	InDestructor = 0 ;
	for (int i = 0 ; i < Channels ; i++) {
		LastWriteBlock[i] = 0;
		LastWriteWord[i] = 0 ;
	}
	// LogOut << "OutputFile::Reset() exit\n" ;
	return ret ;
}

int OutputFile::UpdateChannel(int32 Channel)
{
	if (!FileInit) if (!DoFileInit()) return 0;
	int32 Location = LastWordIndex(Channel);
	const HeadBufSize = sizeof(Location) + sizeof(Channel) ;
	char HeadBuf[HeadBufSize];

	if (LastWriteWord[Channel] != BlockSizeInWords)
		if (!IsInDestructor()) {
		//LogOut << "Channel = " << Channel << "\n" ;
		DbgError("OutputFile::UpdateChannel","bad index");
	}
	MoveNBytes(HeadBuf,(char *) &Channel,sizeof(Channel));
	MoveNBytes(HeadBuf+sizeof(Channel),(char *) &Location,
		sizeof(Location)) ;
	if (!DoWrite(HeadBuf,HeadBufSize,1)) return 0 ;
	if (!DoWrite(Buffers[Channel],BlockSizeInBytes,0)) return 0;
	if (IsInDestructor()) return 1 ;
	LastWriteBlock[Channel]++;
	LastWriteWord[Channel] = 0 ;
	return 1 ;
}

int OutputFile::PacketWrite(DataFileElements type, const void * Data, int Size)
{
	if (Size > MaxPacketSize) DbgError("OutputFile::PacketWrite",
		"too large");
	PacketHeader PacketHead(PacketBinaryDataFile, type, Size);

	if (!IncrementalWrite( &PacketHead, sizeof(PacketHead))) return  0;
	if (Size) if (!IncrementalWrite(Data, Size)) return 0;
	return 1;
}

int OutputFile::Write(BinMachWord Val, int Channel)
{
	if (LastWriteWord[Channel] >= BlockSizeInWords)
		if (!UpdateChannel(Channel)) return 0;
	*(BinMachWord *) (Buffers[Channel]+LastWriteWord[Channel]++) = Val ;
	return 1 ;
}

int OutputFile::Write(MachWord Val, int Channel)
{
	if (LastWriteWord[Channel] >= BlockSizeInWords)
		if (!UpdateChannel(Channel)) return 0;
	*(Buffers[Channel]+LastWriteWord[Channel]++) = Val ;
	return 1 ;
}

int OutputFile::Write(BinMachWord * Ptr, int Size, int Channel)
{
	for (int i = 0 ; i < Size; i++) {
		if(!Write(Ptr[i],Channel)) break ;
	}
	return i ;
}

int OutputFile::Write(MachWord * Ptr, int Size, int Channel)
{
	for (int i = 0 ; i < Size; i++) if(!Write(Ptr[i],Channel)) break ;
	return i ;
}

