/*  wfiles.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */

#include <unistd.h>
#include <math.h>
#include "cgidbg.h"
#include "flstatus.h"
#include "wfiles.h"
#include "arthtyp.h"
// #include "artherr.h"
#include "plotdatg.h"
#include "dsp_app.h"

static const double very_small = 1.e-200 ;
static const double sqrt_very_big =1.e100;

DataFile::DataFile()
{
	Name = DspApplication::open_file(TheFile) ;
	NextByteToRead = NextByteToWrite = 0 ;
}

DataFile::~DataFile()
{
	// LogOut << "~DataFile called\n" ;
	if (TheFile > 0) {
		// LogOut << "deleting " << Name << "\n" ;
		close(TheFile);
		unlink(Name);
	}
	// delete (void *) Name ;
}

void DataFile::SeekEnd()
{
	if (NextByteToRead == NextByteToWrite) return ;
	DataFile::Seek(NextByteToWrite);
	return ;
}

void DataFile::Seek(long Where)
{
	long Ret;
	if (Where != (Ret = lseek(TheFile, Where, SEEK_SET))) {
		TheLog << "Where = " << Where << ", Ret = " << Ret << "\n" ;
		DbgError("DataFile::Seek","bad seek");
	}
	NextByteToRead = Where ;
}
	

int DataFile::Read(int Size, char * Data, long Where)
{
/*
 *	LogOut<< "DataFile::Read(" << Size << ",, " << Where <<
 *			"), ToRead = " << NextByteToRead <<
 *			", ToWrite = " << NextByteToWrite << "\n" ;
 *	LogOut << "`" << Name << "'.\n" ;
 */
	Seek(Where);
	if (Where < 0) Where = NextByteToRead ;
	else {
		// LogOut << "Seeking to " << Where << "\n" ;
		if (Where != NextByteToRead) DataFile::Seek(Where);
		// LogOut << "after seek\n" ;
	}
	int32 ReadSize ;
	if ((ReadSize=read(TheFile,Data,Size)) != Size) {
		TheLog << "Size = " << Size << ", ReadSize = " << ReadSize <<
			", Next = " << NextByteToRead << "\n" ;
		DbgError( "DataFile::Read","bad read");
	}
	// LogOut << "Read " << ReadSize << "\n" ;
	NextByteToRead+=Size ;
	return Size ;
}

void DataFile::WriteData(int Size, const char * Data)
{
	SeekEnd();
/*
 *	LogForm("DataFile::WriteData Size = %d", Size);
 *	LogOut << "`" << Name << "'.\n" ;
 */
	// int ByteSize = Size << 1;
	if (write(TheFile,(char *)Data,Size) != Size) DbgError(
		"DataFile::WriteData", "bad write");
	NextByteToRead = NextByteToWrite += Size ;
}

DataFileClone::DataFileClone(PlotChannelPointer * this_one,
  DataPlot::Transform transform, PlotChannelPointer* base):
	DataFile(),
	base_channel(*base),
	base_file(*(base->GetDataFile())),
	base_plot(*(base->GetPlot())),
	this_channel(*this_one),
	the_transform(transform)
{
/*
 *	LogOut << "base_channel.arith_type() = " << base_channel.arith_type() 
 *		<< "\n" ;
 */
	int size = ArithType::SizeInBytes[base_channel.arith_type()] ;
	if (size < 1) DbgError("DataFileClone::ctor","bad data size");

	word_factor = sizeof(double) / size ;
/*
 *	LogOut << "DataFileClone::ctor, word_factor = " << word_factor <<
 *		"\n" ;
 */
	sample_factor = 1 ;
	if (base_channel.GetNumberElements() == 2)
			if (the_transform == DataPlot::power ||
		the_transform == DataPlot::db_power) sample_factor = 2 ;
	// LogOut << "sample_factor = " << sample_factor << "\n" ;
}
	

DataFileClone::~DataFileClone()
{
	// LogOut << "~DataFileClone called\n" ;
	// int False = 0 ;
	// if (False) ReportOverflows(10,10,"no where");
	// dummy call to force linking static functions
	// LogOut << "~DataFileClone exit\n" ;
}

void DataFileClone::update_from_clone(long Where)
{
/*
 *	LogOut << "update_from_clone(" << Where << "), next write = " <<
 *		NextByteToWrite << "\n" ;
 */
	long needed = Where - NextByteToWrite ;
	if (needed < 1) return ;
	DspApplication::disable_remote_com();
	// LogOut << "remote_com disabled for `" << Name << "'\n" ;

	long restore = base_file.GetNextByteToRead();
	long seek_byte = NextByteToWrite * sample_factor / word_factor ;
	if (NextByteToWrite != seek_byte * word_factor /sample_factor)
		DbgError("DataFileClone::update_from_clone","bad next write");
	base_file.Seek(seek_byte);
	// LogOut << "base_file.Seek(" << seek_byte << ")\n" ;

	long words_needed = needed * sample_factor / sizeof(double);
/*
 *	LogOut << "words_needed = " << words_needed << ", word_factor = " <<
 *		word_factor << ", sixeof(double = " << sizeof(double) << "\n" ;
 *	LogOut << "base_channel.size_of() = " << base_channel.size_of() << "\n"; 
 */
	if (words_needed * sizeof(double) / sample_factor != needed) DbgError(
		"DataFileClone::update_from_clone","bad size");
	// words_needed = needed  / sizeof(double);
	// LogOut << "words_needed = " << words_needed << "\n";
	const buf_size = 1024 ;
	double buf[buf_size*sizeof(double)] ;
	while (words_needed) {
		int to_read = buf_size  ;
		if (to_read > words_needed) to_read = (int) words_needed ;
		int read = base_channel.convert_read(to_read,buf);
		convert_write(to_read,buf);
		words_needed -= read ;
		// LogOut << "words_needed = " << words_needed << "\n" ;
	}
	base_file.Seek(restore);
	DspApplication::enable_remote_com();
}

static double db(double power,double factor = 10.)
{
		if (power <  very_small) power = very_small ;
		double Return = factor * log10(power);
		if (Return < -200.) Return = -200. ;
		return Return ;
}

int DataFileClone::convert_write(int Size, const double  *Data)
{
	// LogOut << "DataFileClone::convert_write(" << Size << ",)\n" ;
	if (Size <  1) return 0 ;
	int count = Size / sample_factor ;
	// LogOut <<"count = " << count << "\n" ;
	if (Size != count * sample_factor) DbgError("DataFileClone::convert_write",
		"bad size");
	double * convert_buf = new double[count] ;
	double img = 0.;
	int num_elts = this_channel.GetNumberElements();
	// int base_elts = base_channel.GetNumberElements(); 
	int elt_ix = (NextByteToWrite / sizeof(double)) % num_elts ; ;
	// LogOut << "elt_ix = " << elt_ix << "\n" ;
	for (int i = 0 ; i < count; i++) {
		double r = *Data++ ;
		switch(the_transform) {
default:
case DataPlot::spectral:
case DataPlot::none:
			DbgError("DataFileClone::convert_write","bad transform");
case DataPlot::db_power:
case DataPlot::power:
			if (r > sqrt_very_big) r = sqrt_very_big ; 
			r = r*r ;
			if (sample_factor == 2) {
				img = *Data++ ;
				if (img > sqrt_very_big) img = sqrt_very_big ; 
				r+= img*img ;
			}
			convert_buf[i] = r ;
			if (the_transform == DataPlot::db_power)
				convert_buf[i] = db(convert_buf[i]);
			break ;
case DataPlot::db:
			convert_buf[i] = db(r,20.);
			break ;
case DataPlot::copy:
			convert_buf[i]=r;
			break ;
		}
		this_channel.UpdateMax(elt_ix++,convert_buf[i]);
		if (elt_ix >= num_elts) elt_ix = 0 ;
/*
 *		LogOut << i << ":" << elt_ix << ":" << convert_buf[i] << " = conv("
 *			<< r << "," << img << "\n" ;
 *		LogOut << "Extrema(" << this_channel.GetPlot()->GetMinYVal() <<
 *			"," << this_channel.GetPlot()->GetMaxYVal() << ")\n" ;
 */
	}
	//	LogOut << "conversion done\n" ;
	DataFile::WriteData(sizeof(double) * count,(char *) convert_buf);
	// LogOut << "WriteData done\n";
	// delete convert_buf ;
	// LogOut << "exit convert_write\n" ;
	return count ;
}

int DataFileClone::Read(int Size, char * Data, long Where)
{
/*
 *	LogOut<< "DataFileClone::Read(" << Size << ",, " << Where << 
 *			"), NextByteToRead = " << NextByteToRead << "\n" ;
 */
	if (Where < 0) Where = NextByteToRead ;
	update_from_clone(Where+Size);
	// LogOut << "calling DataFile::Read(" << Size << ",, " << Where << ")\n" ;
	int Return = DataFile::Read(Size,Data,Where);
/*
 *	for (double * ptr = (double * ) Data ; ptr < (double *) (Data+Return);
 *		ptr++) LogOut << *ptr << "\n" ;
 */
	return Return ;
}

void DataFileClone::WriteData(int , const char * )
{
	DbgError("DataFileClone::WriteData","bad call");
}

long DataFileClone::GetNextByteToWrite()
{
/*
 *	LogOut << "DataFileClone::GetNextByteToWrite()\n" ;
 *	LogOut << "base_file = " << (void *) &base_file << "\n" ;
 *	LogOut << "word_factor = " << word_factor << ", Next = "
 *		<< base_file.GetNextByteToWrite() << "\n" ;
 *	LogOut << "sample_factor = " << sample_factor << "\n" ;
 */
	long ret = word_factor*base_file.GetNextByteToWrite()/sample_factor;
	// LogOut << "DataFileClone::GetNextByteToWrite returning " << ret << "\n" ;
	return ret ;
}

long DataFileClone::GetNextByteToRead()
{
	return DataFile::GetNextByteToRead();
}

void DataFileClone::Seek(long Where)
{
	// LogOut << "DataFileClone::Seek(" << Where << ")\n" ;
	update_from_clone(Where);
	DataFile::Seek(Where);
}


