/*  infile.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <strstream.h>
#include <stream.h>
#include <string.h>
#include <fcntl.h>
#ifndef __NT_VC__
#define O_BINARY 0
#else
#include <io.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#include "newaloc.h"
#include "usriotyp.h"
#include "infile.h"
#include "sysintfc.h"
#include "sysspec.h"
#include "sysconst.h"
#include "portable.h"
#include "shared.h"
#include "cgidbg.h"
#include "hrdarth.h"
#include "yacintfc.h"
#include "remcom.h"
#include "dskchnhd.h"
#include "artherr.h"
#include "dfnode.h"
#include "mpacket.h"
#include "usercom.h"
#include "decx.h"
#include "innode.h"
#include "ramio.h"
#include "rtktest.h"
#include "mkstr.h"
#include "environ.h"

// #include "intfc.h"
#include "dfnode.h"
#define ARITH_TYPE TheArithType


int InputFile::CheckLegalChannel(int Channel)
{
	if (Channel < 0 || Channel >= Channels) {
		State.Error( "bad channel index in node `", TheNode->GetName(),
			"'");
		StateLastRead = FatalError ;
		return 0;
	}
	return 1;
}

void InputFile::NotBinaryFile(const char * Msg)
{
#ifdef INTERACTIVE
#ifdef OPD_PIPES
	if (!SharedSegment::is_comm_active()) return ;
#else
	if (!TheCommControl->IsCommActive()) return ;
#endif
#endif
	OutputCurrentChannel << "'" << FileName <<  "'does not appear to be " <<
		"a correctly formatted DSP++ binary data file.\n" ;
	FileInNode();
	if (Msg) OutputCurrentChannel << Msg << "\n" ;
}

#ifdef INTERACTIVE
TimingDescription * InputFile::GetInitTiming(int InChannel)
{
	if (!CheckLegalChannel(InChannel)) return 0 ;
	return TheChannelHeaders[InChannel].GetInitTiming();
}

double InputFile::GetTimeFirst(int Channel)
{
	if (!CheckLegalChannel(Channel)) return 0.0 ;
	return TheChannelHeaders[Channel].GetTimeFirst();
}
#endif

int InputFile::ConvertReadChannel(int Channel, int32 BytesRead)
{
	if (BytesRead <= 0) return BytesRead ;
	int DataSize = GetSizeOfDataType(TheDataType);
	int32 WordsRead = BytesRead / DataSize ;
	int32 ConvertOffset = 0 ;
	for (int32 i = 0 ; i < WordsRead ; i++) {
		int OverflowFlag ;
		MachWord Word = ConvertToMachWord(TheDataType,
			((char *) ConvertBuffer) + ConvertOffset, OverflowFlag);
		if (OverflowFlag) {
			ReportOverflows(TotalConvertOverflows,
				TotalConvertOverflows+1, TheNode->GetName());
			TotalConvertOverflows++;
		}
		*(Buffers[Channel]+i) = Word ;
		ConvertOffset+=DataSize;
	}
	return WordsRead * sizeof(MachWord);
	
}

void InputFile::EndOfFile()
{
	State.Warning("end of file `", FileName, "' in node `",
		TheNode->GetName(),"'");
	if (StateLastRead < EndOfData) StateLastRead = EndOfData ;
	for (int i = 0 ; i < Channels; i++) 
		LastReadWord[i] = ChannelBufferSize[i] ;
}

ErrCode InputFile::ReadChannel(int i,int32 BytesRead)
{
/*
 *	LogOut << "ReadChannel(" << i << ", " << BytesRead << "), ARITH = " 
 *		<< ARITH_TYPE << ", dt = " << TheDataType << "\n" ;
 */
	if (TheDataType != ARITH_TYPE) BytesRead =
		ConvertReadChannel(i,BytesRead);
	else {
		char * Temp = (char *) Buffers[i] ;
		Buffers[i] = (MachWord *) ConvertBuffer ;
		ConvertBuffer = Temp ;
	}
	
	if (BytesRead <= 0) {
		EndOfFile();
		return StateLastRead ;
	}
	const UnixBytesPerWord = sizeof(MachWord);
	int32 WordsRead = BytesRead / UnixBytesPerWord ;
	LastReadWord[i] = 0 ;
	ChannelBufferSize[i] = WordsRead ;
	return StateLastRead ;
}


int InputFile::ReadChannelHeader(int i)
{
	if (!CheckLegalChannel(i)) return 0 ;
	if (!PacketRead(FileEltsChannelHeader, TheChannelHeaders + i,
		 sizeof(NodeOutChannelHeader))) return 0;
	TheChannelHeaders[i].CheckTiming(Flags);
	DoRead(LastWord + i, sizeof(LastWord[i]),1) ;
	return sizeof(LastWord[i]) ;
}


void InputFile::ReadError()
{
	if (StateLastRead < FatalError) StateLastRead = FatalError ;
#ifdef INTERACTIVE
#ifdef OPD_PIPES
	if (!SharedSegment::is_comm_active()) return ;
#else
	if (!TheCommControl->IsCommActive()) return ;
#endif
#endif
	TargetNode::open_error(FileName, exp_file_name,"Cannot read `OutputNode' header in `");
}

int InputFile::DoRead(void * Data, int Size, int IntFlag)
{
	if (!Size) return 1 ;
	int BytesRead = read(TheFile, Data, Size);

	if (BytesRead < 0) {
		ReadError();
		return -1 ;
	}
	SeekNextRead += BytesRead ;
	return BytesRead ;
}

int InputFile::IncrementalRead(void * Data, int Size)
{
    int BytesRead = DoRead(Data,Size);
    if (BytesRead < Size) return 0;
    FirstDataSample += BytesRead ;
    return 1 ;
}


void InputFile::BadPacketType()
{
	NotBinaryFile("The packet type is not binary data file.");
}

int InputFile::PacketRead(DataFileElements Expected,void * Data, int Size)
{
	char PacketBuffer[MaxPacketSize+1];

	if (Size > MaxPacketSize) DbgError("InputFile::PacketRead",
		"too large");

	for (;;) {
		PacketHeader PacketHead ;

		if (!IncrementalRead(&PacketHead,sizeof(PacketHead))) return 0;

		if (PacketHead.ThePacketType != PacketBinaryDataFile) {
			BadPacketType();
			return 0 ;
		}

		if (!IncrementalRead(PacketBuffer,PacketHead.DataSize))
			return 0;

		int Diff = PacketHead.Identifier - Expected ;
		if ((Diff > 0) && (Expected == FileEltsChannelHeader)) continue;
		if (Diff) {
			NotBinaryFile("Found unexpeced packet identifier.");
			return 0;
		}
	
		if (Size != -1) {
			if (Size != PacketHead.DataSize) {
				NotBinaryFile("Found incorrect packet size.");
				return 0;
			}
		} else {
			PacketBuffer[PacketHead.DataSize] = '\0' ;
			Size = PacketHead.DataSize + 1 ;
			// string of unknown length
		}
		MoveNBytes((char *)Data,PacketBuffer,Size);
		return 1 ;
	}
}

void InputFile::FileInNode()
{
#ifdef INTERACTIVE
#ifdef OPD_PIPES
	if (!SharedSegment::is_comm_active()) return ;
#else
	if (!TheCommControl->IsCommActive()) return ;
#endif
#endif
	if (NodeName) OutputCurrentChannel << "This occurred in node  `" <<  NodeName <<
		"'.\n" ;
}

int InputFile::CheckDataType()
{
	if (TheDataType == TheArithType) return 1;
	int converting = 0 ;
	if (TheDataType > DataTypeNull && TheDataType < DataTypeUpperLim) 
		converting = 1 ;
	if (converting && !State.IsConfirm()) return 1 ;
#ifdef INTERACTIVE
	OutputType save_type = (OutputType) Output->GetOutputType();
	if (converting) Output->SetWindow(OutputCppHelp);
#endif
	OutputCurrentChannel << "`" << FileName <<  "'\n" <<
		"contains data of a different data type then that currently in use.\n" ;
	TargetNode::expand_name(FileName,exp_file_name);
	if (converting) OutputCurrentChannel << "The data will be converted from `"
		<< GetNameOfDataType(TheDataType) << "' to `"
		<< GetNameOfDataType((DataType) ARITH_TYPE) << "'.\n" ;
	else OutputCurrentChannel << "This file cannot be read by this node.\n" ;
		FileInNode() ;
#ifdef INTERACTIVE
	if (converting) Output->SetWindow(save_type);
#endif
	return converting ;
}

int InputFile::StringRead(char * Str,int Length)
{
	return IncrementalRead(Str,Length);
}


int InputFile::ReadHeader()
{
	Channels = 1 ;
	BlockSizeInWords = 0 ;
	BlockSizeInBytes = 0 ;
	InDestructor = 0 ;

	// Read master header
	int PrologueLength = strlen(DataFilePrologue);
	FirstDataSample = 0 ;
	char * Temp = new char[PrologueLength+10] ; // should be +1 
	int StringReadReturn = StringRead(Temp, PrologueLength) ;
	int Compare = 1;
	if (StringReadReturn) Compare =
		strncmp(Temp,DataFilePrologue,PrologueLength-5);
	if (!StringReadReturn) return 0 ;
	delete Temp ;
	Temp = 0 ;
	if (Compare) {
		NotBinaryFile(
 "The file prologue is incorrect or from an incompatible program version.") ;
		return 0 ;
	}

	NodeOutFileHeader Head;

	if (!PacketRead(FileEltsHeader,(char *)&Head,sizeof(Head))) return 0 ;
	// from now on any messages chould be redirected to CppHelp

	TheDataType = Head.TheDataType;
	if (!CheckDataType()) return 0;

	Channels = Head.NumberChannels ;
	BlockSizeInWords = Head.BlockSize ;
	if (BlockSizeInWords > MaxBlockSize) {
		NotBinaryFile("Block size is unreasonable.");
		return 0 ;
	}

	char PacketBuf[MaxPacketSize+1];
	{for (int i = 0 ; i < MaxPacketSize+1;i++) PacketBuf[i] = '\0' ;}
	if (!PacketRead(FileEltsNodeName, PacketBuf, -1)) return 0;
	OriginalNodeName = strcpy(new char[strlen(PacketBuf)+1],
		PacketBuf);
	if (!PacketRead(FileEltsCaption, PacketBuf, -1)) return 0;
#ifdef INTERACTIVE
	*Output + OutputCppHelp ;
#endif
	{for (int i = 0 ; i < MaxPacketSize+1;i++) PacketBuf[i] = '\0' ;}
	if (PacketBuf[0]) Caption = strcpy(new char[strlen(PacketBuf)+1],
		PacketBuf);
	else Caption = 0 ;
	
	WordSize = sizeof(MachWord) ;
	BlockSizeInBytes = BlockSizeInWords * WordSize ;

	Buffers = new MachWord * [Channels];


	LastReadBlock = new long[Channels] ;
	FirstWordInBuffer = new long[Channels] ;
	SeekByteAfterLastRead = new long[Channels]  ;
	LastReadWord = new int16[Channels] ;
	ChannelBufferSize = new int16[Channels] ;
	ElementSize = new int32[Channels] ;
	ChannelStateLastRead = new ErrCode[Channels] ;
	Buffers = new MachWord * [Channels] ;
	TheChannelHeaders = new NodeOutChannelHeader[Channels] ;
	LastWord = new long[Channels] ;
	if (TheDataType != ARITH_TYPE) ConvertBufferSizeInBytes =
		BlockSizeInWords * GetSizeOfDataType(TheDataType);
	else ConvertBufferSizeInBytes = BlockSizeInBytes ;
	
	ConvertBuffer = new char[ConvertBufferSizeInBytes] ;
	for (int i = 0 ; i < Channels; i++) {
		ChannelStateLastRead[i] = OK ;
		Buffers[i] = (MachWord *) (new char[BlockSizeInBytes]) ;
		LastReadBlock[i] =  0 ;
		FirstWordInBuffer[i] = 0;
		LastReadWord[i] =  0 ;
		ChannelBufferSize[i] = 0 ;
		ElementSize[i] =  0 ;
		ReadChannelHeader(i);
		ElementSize[i] = TheChannelHeaders[i].ElementSize ;
	}
	SeekAtReset = lseek(TheFile,0,SEEK_CUR); // does not change file pointer
	for (i = 0 ; i < Channels; i++) 
		SeekByteAfterLastRead[i] =  SeekAtReset ;
	return 1 ;
}

InputFile::InputFile(TargetNode * node, const char * file_name,
	const char * node_name,int16 flags):
	TheFile(-1),
	TheNode(node),
	FileName(file_name),
	exp_file_name(0),
	delete_file_name(0),
	NodeName(node_name),
	Channels(0),
	WordSize(0),
	BlockSizeInWords(0),
	BlockSizeInBytes(0),
	FirstDataSample(0),
	Flags(flags)
{
	Caption = OriginalNodeName = 0 ;
	IgnoreFileHeaderWordCount = 0 ;
	FirstDataSample = 0 ;
	SeekNextRead = 0 ;
	SeekByteAfterLastRead = 0 ;
	LastReadBlock =  0 ;
	FirstWordInBuffer = 0 ;
	LastReadWord = 0 ;
	ChannelBufferSize = 0 ;
	ElementSize = 0 ;
	Buffers = 0 ;
	TheChannelHeaders = 0 ;
	ConvertBuffer = 0 ;
	TotalConvertOverflows = 0 ;
	StateLastRead = OK ;
	if (!NodeName) if (TheNode) NodeName = TheNode->GetName();
	if (!FileName) {
#ifdef INTERACTIVE
		// this prvents a warning message with the default objects
		if (!State.IsInteractive()) {
			return ;
		}
#endif
		const char * name = node_name;
		if (!name && node) name = node->GetName();
		PromptOut << "Empty or nonexistant file name was given.\n" ;
		if (name) {
			PromptOut << "Node `" << name << "'\n" ;
			PromptOut <<
			"requires a file in `OutputNode' format to initialize itself.\n" ;
		}
		if (!State.IsInteractive()) return ;
		// if INTERACTIVE is defined this test should never pass but
		// is cleaner then using `ifdef'

		PromptOut <<
			"If you enter RETURN with no file name the node will not be usable.\n";
	}
	for (;;) {
		const char * new_file_name  = FileName ;
		const char * exp_name = TargetNode::open_file(&new_file_name,TheFile,
			"parmeter `FileName' for binary file (created by `OutputNode')", 1);
		if (!exp_name) return ;
		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);
    	}
		if (ReadHeader()) return ;
		const char * cannot = "Cannot read `OutputNode' header in `" ;
#ifdef INTERACTIVE
		OutputType channel = OutputPrompt ;
		if (!State.IsInteractive()) channel = OutputCppHelp ;
		*Output + channel;
		if (State.IsInteractive()) OutputCurrentChannel << cannot 
			<< new_file_name  << "'.\n" ;
		else
#endif
		if (!State.IsInteractive()) {
			State.Error(cannot,new_file_name,"'");
			StateLastRead = FatalError ;
		}
#ifdef INTERACTIVE
		else *Output + OutputPrompt << cannot << new_file_name << "'.\n" ;
#endif
		TargetNode::expand_name(new_file_name,exp_name);
		delete delete_file_name ;
		delete exp_file_name ;
		exp_file_name = 0 ;
		delete_file_name = 0 ;
		FileName = 0 ;
		if (!State.IsInteractive()) break ;
	}
}

ErrCode InputFile::Reset()
{
	for (int i = 0 ; i < Channels; i++) {
		LastReadBlock[i] =  0 ;
		FirstWordInBuffer[i] = 0;
		SeekByteAfterLastRead[i] =  SeekAtReset  ;
		LastReadWord[i] =  0 ;
		ChannelBufferSize[i] = 0 ;
	}
	if (TheFile < 1) return StateLastRead = FatalError ;
	if (lseek(TheFile,SeekAtReset,SEEK_SET) != SeekAtReset)
		return FatalError;
	return OK ;
	
}

InputFile::~InputFile()
{
	if (TheFile > 0) close(TheFile) ;
	delete delete_file_name ;
	delete exp_file_name ;
	delete SeekByteAfterLastRead ;
	delete LastReadBlock ;
	delete FirstWordInBuffer ;
	delete LastReadWord ;
	delete ChannelBufferSize ;
	delete ElementSize ;
	delete ChannelStateLastRead ;
	if (Buffers) for (int i = 0 ; i < Channels;i++) delete Buffers[i] ;
	delete Buffers ;
	delete TheChannelHeaders ;
	delete ConvertBuffer ;
	delete exp_file_name ;
}

ErrCode InputFile::SeekAfterLastRead(int Channel)
{
	long Where = lseek(TheFile, SeekNextRead=SeekByteAfterLastRead[Channel],
		SEEK_SET);
	// LogOut << "Seeking to " << Where << ", for chan " << Channel << "\n" ;
	if (Where != SeekNextRead) {
		TargetNode::open_error(FileName,exp_file_name,"seek error");
		FileInNode();
		return StateLastRead =  FatalError ;
	}
	return StateLastRead ;
}

ErrCode InputFile::UpdateChannel(int Channel)
{
#ifdef INTERACTIVE
	*Output + OutputCppHelp ;
#endif
	if (State.IsError()) return FatalError ;
	if (StateLastRead >= EndOfData) return StateLastRead ;
	if (SeekAfterLastRead(Channel) > Warning) return StateLastRead ;
	int32 DiskChannel = 0 ;
	int32 FirstWordIndex = 0 ; // starts at word 0
	const HeadBufSize = sizeof(DiskChannel) + sizeof(FirstWordIndex) ;
	char HeadBuf[HeadBufSize] ;
	int DifferentChannels = 0 ;
	 
	long NextWord = FirstWordInBuffer[Channel] + BlockSizeInWords ;
	if (NextWord >= LastWord[Channel]) return StateLastRead = EndOfData ;
	for (;;) {
		int32 BytesRead = DoRead(HeadBuf,HeadBufSize,1) ;
		if (BytesRead < HeadBufSize) {
			return StateLastRead = EndOfData ;
		}
		MoveNBytes((char *) &DiskChannel,HeadBuf,sizeof(DiskChannel));
		MoveNBytes((char *) &FirstWordIndex, HeadBuf+
			sizeof(DiskChannel), sizeof(FirstWordIndex));
		if (!CheckLegalChannel(DiskChannel)) return FatalError ;
		if (DiskChannel == Channel) break ;

		// skip past this data
		if ((SeekNextRead=lseek(TheFile,ConvertBufferSizeInBytes,SEEK_CUR))
			< 0) {
			State.Error("error in seek in file `", FileName, "'");
			TargetNode::expand_name(FileName, exp_file_name);
			SystemErrorMessage();
        	return StateLastRead = FatalError;
		}
	}
	int32 bytes_to_read = ConvertBufferSizeInBytes ;
	int32 bytes_left = (LastWord[Channel] - NextWord)*
		GetSizeOfDataType(TheDataType);
	if (bytes_left <= 0) return StateLastRead = EndOfData ;
	if (bytes_to_read > bytes_left) bytes_to_read = bytes_left ;
	int32 BytesRead = DoRead(ConvertBuffer,bytes_to_read);
	if (State.IsError()) return FatalError ;
	if (StateLastRead >= EndOfData) return StateLastRead ;
	if (FirstWordIndex != NextWord) {
/*
 *		LogOut << "FirstWordIndex = " << FirstWordIndex <<
 *			", NextWord = " << NextWord <<  ", DifferentCannels = "
 *			<< DifferentChannels << "\n" ;
 */
		char bufChannel[32];
		sprintf(bufChannel,"%d",Channel);
		char bufNextWordm1[32];
		sprintf(bufNextWordm1,"%d",NextWord-1);
		char bufFirstWordIndex[32];
		sprintf(bufFirstWordIndex,"%d",FirstWordIndex);
		State.Warning("data for node `", TheNode->GetName(),
			"' channel ", bufChannel, " is not in sequence.\nWord ",
			bufNextWordm1, " is followed by word ", bufFirstWordIndex);
		NextWord = FirstWordIndex ;
	}
	FirstWordInBuffer[Channel] = NextWord ;
	SeekByteAfterLastRead[Channel] = SeekNextRead ;
	return ReadChannel(Channel,BytesRead);
}

int32 InputFile::Read(int16 Channel, int32 Size, MachWord * DataBuffer)
{
/*
 *	LogOut << "Read(" << Channel << "," <<  Size  << ",), StateLastRead = "
 *		<< StateLastRead << ", TheFile =  " << TheFile << "\n" ;
 */
	if ((StateLastRead = ChannelStateLastRead[Channel]) >= EndOfData) return 0 ;
	for (int i = 0; i < Size ; i++) {
		DataBuffer[i] = Read(Channel);
		if (StateLastRead >= EndOfData) {
			ChannelStateLastRead[Channel] = StateLastRead ;
			return i;
		}
	}
	ChannelStateLastRead[Channel] = StateLastRead ;
	return i ;
		
}

MachWord InputFile::Read(int16 Channel)
{
	if (LastReadWord[Channel] >= ChannelBufferSize[Channel])
		if (UpdateChannel(Channel)>=EndOfData) return 0 ;
	MachWord Return = *(Buffers[Channel]+LastReadWord[Channel]++) ;
	return Return ;
}

void InputFile::DisplayHeader()
{
	HelpOut << "The header for file `" << FileName << 
		"' contains caption:\n`" << Caption <<
		"'\nThis file was created by node `" << OriginalNodeName <<
		"'.\n" ;
	HelpOut << "It contains " << Channels << " channel" ;
	if (Channels > 1) HelpOut << "s" ;
	HelpOut << " of data of type `" <<
		GetNameOfDataType(TheDataType) << "'.\n\n" ;
	HelpOut << "BlockSize = " << BlockSizeInWords << ".\n" ;
	for (int i = 0 ; i < Channels; i++) {
		HelpOut << "Channel " << i << " parameters:\n" ;
		TheChannelHeaders[i].DisplayHeader();
		int32 ElementSize = TheChannelHeaders[i].ElementSize ;
		if (!LastWord[i]) *Output<< "There are no words ";
		else if (LastWord[i] == 1)
		*Output << "There is 1 word" ;
		else *Output << "There are " << LastWord[i] << " words" ;
		if (ElementSize && LastWord[i])
			*Output << " (" << LastWord[i]/ElementSize <<
			" samples) " ;
		*Output << "in this channel.\n" ;

	}
}

int32 InputFile::GetElementSize(int Channel)
{
	if (!CheckLegalChannel(Channel)) return -1 ;
	return ElementSize[Channel] ;
}

double InputFile::GetTimingRatio(int Channel)
{
	if (!CheckLegalChannel(Channel)) return 0.0 ;
	return TheChannelHeaders[Channel].GetTimingRatio();
}
	
const NodeOutChannelHeader& InputFile::GetChannelHeader(int i) const
{
	if (i < 0 || i >= Channels) DbgError("InputFile::GetChannelHeader",
		"bad channel");
	return TheChannelHeaders[i];
}

int InputFile::CheckIntegrity()
{
	if (TheFile > 10000) return 0 ;
	if (StateLastRead > FatalError) return 0;
	if (TheFile  < 0) return 0 ;
	if (StateLastRead < 0) return 0;
	return 1 ;
}


