/*  comparec.h   */
/*  Copyright 1990 Mountain Math Software  */
/*  All Rights Reserved                    */

#include <fstream.h>
#include <fcntl.h>
#include "ObjProDSPcom/disphnd.h"
#include "ObjProComGui/usercom.h"
#include "ObjProUsr/compare.h"
#include "ObjProGui/remcom.h"
#include "ObjProComGui/cgidbg.h"
#include "ObjProDSP/sysconst.h"
#include "ObjProGui/yacintfc.h"
#include "ObjProDSPcom/infile.h"
#include "ObjProGui/dyntextd.h"

static int rel_tolerance = 1 ;
static int abs_tolerance = 1 ;
/* #define GetTheTolerance() 1.e-7  */
#define GetTheTolerance() GetTolerance()

InputFile * CompareDisk::TempInputFile = 0 ;

#ifdef INTERACTIVE
int CompareDisk::InitAfterLinked()
{
    return set_read_binary_flag(binary_read_flag) ;
}
#endif


void CompareDisk::ctor()
{
	max_abs_diff = 0.0 ;
	max_rel_diff = 0.0 ;
	out_file = 0 ;
	BadNode = 1 ;
	FirstTime = 1 ;
	WindowId = 0 ;
	ErrorCount = 0 ;
	compare_count = 0 ;
	TheInputFile = TempInputFile ;
	Channels = 0 ;
#ifdef INTERACTIVE
	if (!GetErrorFile()) 
		GetWindowHandle(DisplayHandle,GetName(),TheInputFile->GetCaption());
#endif
	if (TheInputFile->GetTheFile() < 1) return ;
	Channels = TheInputFile->GetChannels();
	// ElementSize = new int32[Channels] ;

	for (int i = 0 ; i < Channels ; i ++) {
		SetInputFromDisk(TheInputFile->GetChannelHeader(i));
#ifdef INTERACTIVE
		GetInStream(i)->SetArithType(ArithType::ArithCapabilityAny);
#endif
	}
 
	CumulativeBlocks = new double [Channels];
	for (int j = 0 ; j < Channels; j++) CumulativeBlocks[j] = 0.0 ;
	if (Channels > 1) {
		SampleRateRatio = new double[Channels-1];
		for (int j = 1 ; j < Channels ; j++) SampleRateRatio[j-1] = 
				TheInputFile->GetTimingRatio(j) /
				TheInputFile->GetTimingRatio(0) ;
	} else SampleRateRatio = 0 ;
	// LogOut << "Constructing `" << Name << "'\n" ;
	BadNode = 0 ;
}

#ifdef INTERACTIVE
void CompareDisk::DisplayHeader()
{
	if (!TheInputFile) {
		if (BadNode) NoFileError();
		return ;
	}
	TheInputFile->DisplayHeader();
}

void CompareDisk::NoFileError()
{
	State.Error( "No file open in node `", GetName(), "'");
}

void CompareDisk::IgnoreHeaderCount() 
{
	if (!TheInputFile) {
		if (BadNode) NoFileError();
		return ;
	}
	TheInputFile->SetIgnoreHeaderCount();
}

TimingDescription * CompareDisk::GetInitTiming(int InChannel)
{
	if (!TheInputFile) {
		if (BadNode) NoFileError();
		return 0 ;
	}
	return TheInputFile->GetInitTiming(InChannel);
}
#endif

ErrCode CompareDisk::CompareSamples(int32 k)
{
	int c ;
	// LogOut << "CompareDisk::CompareSamples(" << k << ")\n" ;
	// LogOut << "Channels = " << Channels << "\n" ;
	for (c = 0 ; c < Channels; c++)  {
		// LogOut << "BlockSize[" << c <<"] = "<< GetBlockSize(c) << "\n" ;
		// LogOut << "ElementSize = " << GetInEltSize(c) << "\n" ;
	}
	int Total = 0 ;
	for (c = 0 ; c < Channels; c++)  {
	for (int i = 0 ; i < k ; i++)
	  for (int b = 0 ; b < GetBlockSize(c); b++) 
	    for (int j = 0 ; j < GetInEltSize(c); j++) {
		Total++ ;
		MachWord InputWord ;
		MachWord FileWord = TheInputFile->Read(c) ;
		if (binary_read_flag) InputWord = (SignedBinMachWord) ReadInteger(c);
		else InputWord = ReadWord(c);
		if (TheInputFile->GetReadState() >= EndOfData)
			return TheInputFile->GetReadState();
		compare_count++ ;
		if (CompareValue(FileWord,InputWord)) {
			if (++ErrorCount > GetMaxReport()) continue ;
			if (State.IsError()) return FatalError ;
			if (GetErrorFile()) {
			    if (!out_file) {
 	    	     	out_file = new ofstream(GetErrorFile());
    	    	    int bad = 0 ;
        	    	if (!out_file) bad = 1 ;
            		else bad = out_file->bad();
            		if (bad) {
                		State.Error("Output file `", GetErrorFile(), "' in node `",
                    		GetName(), "' cannot be created.");
                		return FatalError ;
            		}
				}
				*out_file << "File word " <<  (MachWordCast) FileWord << " != "
					<< (MachWordCast) InputWord << "( " << Diff <<
					", " << RelDiff << ") at" ;
				if (Channels > 1)
					*out_file << " channel " << c << "," ;
				if (GetBlockSize(c) > 1) {
					*out_file << " block " << i + CumulativeBlocks[c]  ;
					*out_file << " sample " << b ;
				} else *out_file << " sample " << i + CumulativeBlocks[c] ;
				if (GetInEltSize(c) > 1) *out_file << ", element " << j ;
				*out_file << ".\n" ;
			} else {
				DISPLAY_OUT <<  (MachWordCast) FileWord << " != "
					<< (MachWordCast) InputWord << " at" ;
				if (Channels > 1)
					DISPLAY_OUT << " channel " << c << "," ;
				if (GetBlockSize(c) > 1) {
					DISPLAY_OUT << " block " << i + CumulativeBlocks[c]  ;
                    DISPLAY_OUT << " sample " << b ;
                } else DISPLAY_OUT << " sample " << i + CumulativeBlocks[c]
                    << "," ;
				if (GetInEltSize(c) > 1) DISPLAY_OUT << ", element " << j; 
				DISPLAY_OUT << ".\n" ;
			}
		}
	}
	CumulativeBlocks[c]+=k ;
	}
	// LogOut << "Total = " << Total << "\n" ;
	if (State.IsError()) return FatalError ;
	return OK ;
}

ErrCode CompareDisk::kernel(int32 k)
{
#ifdef INTERACTIVE
	if (FirstTime) {
		if (BadNode) {
			State.Error("Node `",GetName(),"'",
			"is not initialized.  It may be a dummy default");
			return FatalError ;
		}
		if (!GetErrorFile()) {
			while (!DisplayHandle) TheDynamicTextController->CheckForPacket();
			TheDynamicTextController->OpenWindowAndWait(DisplayHandle,
				&WindowId);
		}
		FirstTime = 0;
	}
#endif
	if (!TheInputFile) {
		State.Error("File `",  GetFileName(), "' in node `",
			GetName(),
		"' has not been opened properly.\nThis node may be a dummy default.");
		return FatalError ;
	}
	ErrCode Return = CompareSamples(k);
	// if (TheInputFile->IsReadError()) return FatalError ;
	return Return ;
}

void CompareDisk::dtor()
{
	if (GetFileName()) if (State.log_file()) {
		ofstream val_log (State.log_file(),ios::ate  | ios::app | ios::out);
		if (!val_log.good()) DbgError("Compare::~Compare",
			"cannot write validation log file");
		if (ErrorCount || !compare_count) val_log << "*** " ;
		val_log << GetName() << " compared `" << GetFileName() <<
			"', " << compare_count << " comparisons with " ;
		if (ErrorCount) val_log << ErrorCount ;
		else val_log << "no" ;
		val_log << " errors at tolerance " << GetTheTolerance() ;
		if (!ErrorCount) val_log << ".\n" ;
		else val_log << ", max abs = " << max_abs_diff << ", max rel = " <<
			max_rel_diff << ".\n" << "Errors are recorded in `" <<
			GetErrorFile() << "'.\n" ;
	}
	delete TheInputFile ;
	delete out_file ;
}

int CompareDisk::CompareValue(MachWord FileWord, MachWord InputWord) 
{
	int ret = 0 ;
	Diff = fabs(FileWord.val() - InputWord.val()) ;
	if (abs_tolerance) if (Diff > GetTheTolerance()) ret = 1 ;
	double abs_sum = fabs(FileWord.val()) + fabs(InputWord.val());
	if (Diff > 0.0) if ( Diff < 1.e50  && abs_sum > 1.e-50) {
		RelDiff = Diff/abs_sum ;
		if (rel_tolerance) if (RelDiff > GetTheTolerance()) ret = 1 ;
		if (ret) if (RelDiff > max_rel_diff) max_rel_diff = RelDiff ;
	}
	if (ret) if (Diff > max_abs_diff) max_abs_diff = Diff ;
	return ret;
}

