/*  strmstr.C   */
/*  Copyright 1990 Mountain Math Software  */
/*  All Rights Reserved                    */
#include "dfnode.h"
#include "strmstr.h"
#include "usercom.h"
#include "yacintfc.h"
#include "cgidbg.h"
#include "dskchnhd.h"

const StreamStr StreamNotInitialized(SizeNotInitialized,SizeNotInitialized) ;
const StreamStr StreamNotSet(SizeVariable,SizeVariable) ;
const StreamStr StreamReal(1,1) ;
const StreamStr StreamComplex(2,1) ;

/*
 *	Sample rate adjustment rules:
 *
 *	1. All internal representations are in absolute units of one sample
 *	   per time period.
 *	2. Converion to user sample rate units is only made when displayed
 *	   to the user.
 *	3. The Y axis labeling is unaffected by sample rate changes.
 *	4. The X axis labeling is divided by the sample rate.
 *	5. For two dimensional data the X axis is either unaffected -
 *	   (SampleRateFactor = 0) or divided by sample rate factor.
 *	6. Overlap for two dimensional data is a complicating factor.
 *	   It does not affect the X axis labeling but does affect the
 *	   time of each individual plot (which is also displayed).
 *	7. SampleRateFactor in class PlotChannel is this adjustment.
 *
 *	Thus SampleRateFactor has the following values:
 *
 *		One dimensional data: 1.0
 *
 *		Two dimensional data: 
 *
 *			0.0 - X axis not affected by sample rate
 *
 *			(1. - OverlapFactor) - X axis
 *			 	is multiplied by this and
 *				by the sampling rate before being displayed. 
 *	   
 */

StreamStr::StreamStr(ArithType::ArithCapabilities type,int32 EltSz,
	int32 BlkSz, double MinX, double MaxX,
    double sample_rate_factor):
	StreamArithType(type),
    ElementSize(EltSz),
    BlockSize(BlkSz),
    MinimumX(MinX),
    MaximumX(MaxX),
    SampleRateFactor(sample_rate_factor)
{
}

StreamStr::StreamStr(int32 EltSz, int32 BlkSz, double MinX, double MaxX,
	double sample_rate_factor,ArithType::ArithCapabilities type) :
	StreamArithType(type),
	ElementSize(EltSz),
	BlockSize(BlkSz),
	MinimumX(MinX),
	MaximumX(MaxX),
	SampleRateFactor(sample_rate_factor)
{
}

StreamStr::StreamStr(int32 EltSz, int32 BlkSz, ArithType::ArithTypes type):
	StreamArithType((ArithType::ArithCapabilities)type),
	ElementSize(EltSz),
	BlockSize(BlkSz),
	MinimumX(0.0),
	MaximumX(0.0),
	SampleRateFactor(1.0)
{
}
		
StreamStr::StreamStr(int32 EltSz,int32 BlkSz,ArithType::ArithCapabilities type):
	StreamArithType(type),
	ElementSize(EltSz),
	BlockSize(BlkSz),
	MinimumX(0.0),
	MaximumX(0.0),
	SampleRateFactor(1.0)
{
}
		
StreamStr::StreamStr(const StreamStr& Str)
{
	StreamArithType = Str. GetArithType();
	ElementSize = Str.GetElementSize();
	BlockSize = Str.GetBlockSize();
	MinimumX = Str.GetMinimumX();
	MaximumX = Str.GetMaximumX();
	SampleRateFactor = Str.GetSampleRateFactor();
}

static const char * GetMistake(int EltErr, int BlkErr)
{
	if (!EltErr && !BlkErr) return 0 ;
	const char * Mistake = " element size" ;
	if (BlkErr) if (EltErr) Mistake = "s block size and element size" ;
		else Mistake = " block size" ;
	return Mistake ;
}

int StreamStr::Set(int32 EltSz, int32 BlkSz, double MinX, double MaxX,
	double sample_rate_factor,ArithType::ArithCapabilities type)
{
	// LogOut << "StreamStr::Set(" << EltSz << ", " << BlkSz << ")\n" ;
	int EltErr = 0 ;
	if (EltSz != SizeNotInitialized) if (ElementSize == SizeNotInitialized)
		ElementSize = EltSz ; else EltErr = 1 ;
	int BlkErr = 0 ;
	if (BlkSz != SizeNotInitialized) if (BlockSize == SizeNotInitialized)
		BlockSize = BlkSz ; else BlkErr = 1 ;
	const char * Mistake = GetMistake(EltErr,BlkErr) ;
	if (MinX == MaxX) if (MinX == 0.0) if (BlockSize) MaxX = BlockSize ;
	SampleRateFactor = sample_rate_factor ;
	StreamArithType = type ;
	if (!Mistake) return 0 ;
	State.Error("attempt to initialize value", Mistake, " twice.");
	return 1 ;
}

int StreamStr::Set(StreamStr& Str)
{
	return Set(Str.GetElementSize(), Str.GetBlockSize(),
		Str.GetMinimumX(),Str.GetMaximumX());
}

StreamStr::~StreamStr()
{
}

int StreamStr::CheckSet() const
{
	int EltErr = ElementSize <= SizeVariable ;
	int BlkErr = BlockSize <= SizeVariable ;
	const char * Mistake = GetMistake(EltErr,BlkErr);
	if (Mistake) State.Error("driver node value",Mistake, " not set");
	else return 0 ;
	return 1 ;
}

int StreamStr::CheckInitialized() const
{
	int EltErr = ElementSize <= SizeNotInitialized ;
	int BlkErr = BlockSize <= SizeNotInitialized ;
	const char * Mistake = GetMistake(EltErr,BlkErr);
	if (Mistake) State.Error("node value", Mistake, " not initialized");
	else return 0 ;
	return 1 ;
}

StreamStr::CheckStream StreamStr::CheckInput(const StreamStr* Input)
{
	if (Input->CheckSet()) return CheckStreamError ;
	if (CheckInitialized()) return CheckStreamError  ;
	int Change = 0 ;

	int EltErr = 0 ;
	if (ElementSize == SizeVariable) {
		ElementSize = Input->GetElementSize();
		Change = 1 ;
	} else if (ElementSize != Input->GetElementSize()) {
		EltErr = 1 ;
/*
 *		LogOut << "StreamStr::CheckInput ElementSize: " 
 *			<< ElementSize << " != " << Input->GetElementSize()
 *			<< "\n" ;
 */
	}
	int BlkErr = 0 ;
	if (BlockSize == SizeVariable) {
		Change = 1 ;
		BlockSize = Input->GetBlockSize();
		MinimumX = Input->GetMinimumX();
		MaximumX = Input->GetMaximumX();
	} else if (BlockSize != Input->GetBlockSize()) BlkErr = 1 ;
	
	const char * Mistake = GetMistake(EltErr,BlkErr);
	if (Mistake) {
		State.Error("node value", Mistake,
			" not consistent with those of the driver node");
		return CheckStreamError ;
	}
	if (Change) return CheckStreamOk ;
	return CheckStreamNoChange ;
}

StreamStr::CheckStream StreamStr::AdjustInput (const StreamStr * Input)
{
	if (Input->CheckSet()) return CheckStreamError ;
	if (CheckInitialized()) return CheckStreamError  ;
	int Change = 0 ;

	if (ElementSize == SizeVariable) {
		ElementSize = Input->GetElementSize();
		// LogOut << "SetElementSize to " << ElementSize << "\n" ;
		Change = 1 ;
	} 

	if (BlockSize == SizeVariable) {
		Change = 1 ;
		// LogOut << "SetBlockSize to " << BlockSize << "\n" ;
		BlockSize = Input->GetBlockSize();
		MinimumX = Input->GetMinimumX();
		MaximumX = Input->GetMaximumX();
	}
	SampleRateFactor *= Input->GetSampleRateFactor();
	if (Change) return CheckStreamOk ;
	return CheckStreamNoChange ;
}


void StreamStr::SetFromDisk(const NodeOutChannelHeader& DiskState)
{
#define GET(A) A = DiskState.A
	GET(ElementSize) ;
	GET(BlockSize) ;
	GET(MinimumX) ;
	GET(MaximumX) ;
	GET(SampleRateFactor) ;
	// GET(StreamArithType);
#undef GET
	if (!ElementSize) DbgError("StreamStr::SetFromDisk",
		"bad ElementSize");
	if (SampleRateFactor < 1.e-100) SampleRateFactor = 1. ;
}

int32 StreamStr::GetChunkSize() const 
{
	if (CheckInitialized()) {
		State.Error("node not initialized");
		return 1;
	}
	return BlockSize * ElementSize ;
}

static int is_float(int type)
{
	if (type == ArithType::ArithDouble) return 1 ;
	if (type == ArithType::ArithFloat) return 1 ;
	return 0 ;
}

ErrCode StreamStr::CheckReportArithType(const StreamStr& CheckAgainst,
	DfNode * link_to, int link_to_channel) const
{
	ErrCode Err = CheckArithType(CheckAgainst);
    if (Err > OK) {
        HelpOut << "Arithmetic type error in linking to `"
            << link_to->GetName()  << "' input channel " <<
            link_to_channel << ".\n" ;
        HelpOut << "Source node arithmetic type is `" <<
            ArithType::CapabilityNames[GetArithType()]
            << "'\ndestination node type is `" <<
            ArithType::CapabilityNames[CheckAgainst.GetArithType()]
            << "'.\n" ;
/*
 *      LogOut << "Arithmetic type error in linking to `"
 *          << link_to->GetName() << "' input channel " <<
 *          link_to_channel << ".\n" ;
 *      LogOut << "DriveType = " << CheckAgainst.GetArithType() << "\n" ;
 *      LogOut << "Input type = " << GetArithType() << "\n" ;
 */
    }
    return Err ;

}

void StreamStr::dump(const char *msg) const
{
	TheLog << "StreamStr::dump" ;
	if (msg) TheLog << " for " << msg ;
	TheLog << "\nArith = " ;
	if (StreamArithType > ArithType::ArithTypeUndefined &&
		StreamArithType <=  ArithType::MaxArithCapabilities)
			TheLog << ArithType::CapabilityNames[StreamArithType] << " -- ";
	TheLog << (int) StreamArithType << "\n" ;
	TheLog << "EltSz = " << ElementSize << ", BlkSz = " << BlockSize << "\n" ;
	TheLog << "MinX = " << MinimumX << ", MaxX = " << MaximumX <<
		", RateFactor = " << SampleRateFactor << "\n" ;
}

ErrCode StreamStr::CheckArithType(const StreamStr& CheckAgainst) const
{
	int a = GetArithType() ;
	int b = CheckAgainst.GetArithType() ;
	if (a ==b) return OK ;
	if (a < ArithType::MaxArithTypes && b < ArithType::MaxArithTypes)
		return FatalError ;
	if (a == ArithType::ArithCapabilityAny ||
		b == ArithType::ArithCapabilityAny) return OK ;
	if (a == ArithType::ArithCapabilityAnyInt && !is_float(b)) return OK ;
	if (b == ArithType::ArithCapabilityAnyInt && !is_float(a)) return OK ;
	return FatalError ;
}

ElementStreamStr::ElementStreamStr( int32 EltSz, int32 BlkSz):
	StreamStr(EltSz,BlkSz)
{
}

ElementStreamStr::~ElementStreamStr()
{
}


