/*  travparm.C   */
/*  Copyright 1991 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <stream.h>
#include <fstream.h>
#include <math.h>
#include <stdlib.h>
#include "cgidbg.h"
#include "netlnk.h"
#include "strmstr.h"
#include "yacintfc.h"
#include "tarparm.h"
#include "travparm.h"
#include "network.h"
#include "cmpseq.h"
#include "buffer.h"
#include "adjust.h"
#include "execseq.h"
#include "tarnodpar.h"
#include "bufstat.h"
#include "mktarget.h"
#include "shrdary.h"
#include "netdescd.h"
#include "noderep.h"


ReasonForFailure::ReasonForFailure(const char * reason, const char *node,
	int32 needed, int chan, ChannelType type, int fatal):
	Reason(reason),
	NeededSpace(needed),
	Node(node),
	Channel(chan),
	Type(type),
	FatalFlag(fatal)
{
	// LogOut << "ReasonForFailure fatal = " << fatal << "\n" ;
}

void ReasonForFailure::Explain(ostream& Out, const char * Attempting) const
{
	Out << "Cannot " << Attempting << "\nbecause " << Reason << ".\n" ;
	if (NeededSpace >0) Out << "Buffer space needed is " <<
		NeededSpace << ".\n" ;
	if (Channel > -1) Out << "The problem occured in node `" << Node
		<< "' at " << (Type==Input ? "input":"output") << " channel "
		<< Channel << ".\n" ;
	if (IsFatal()) Out <<
	"Target code cannot be constructed with these buffer size limits.\n" ;
}




TargetBufferParameters::TargetBufferParameters(int32 size):
	Size(size),
	LeastCommonMultiple(1)
{
}

void TraverseParameters::init()
{
	TheType = UndefinedType ;
	TheParameters = 0 ;
	TheTargetCode = 0 ;
	the_flag = 0 ;
}

void TraverseParameters::SetTheParameters(BufferStatus* status)
{
	TheType = BufferStatusType;
	TheParameters = status ;
}

TraverseParameters::TraverseParameters(BufferStatus& buffer_status)
{
	init();
	TheType = BufferStatusType ;
	TheParameters = &buffer_status ;
}

TraverseParameters::TraverseParameters(EmitTargetCode& emit)
{
	init();
	TheTargetCode= &emit ;
}

TraverseParameters::TraverseParameters(TransmitNetworkDescription& desc)
{
	init();
	TheParameters = &desc ;
	TheType = TransmitDescriptionType ;
}

TraverseParameters::TraverseParameters(NodeReplacementList& list)
{
	init();
	TheParameters = &list ;
	TheType = ReplacementListType ;
}

TraverseParameters::TraverseParameters(DfNode & node)
{
	init();
	TheParameters = &node ;
	TheType = DfNodeType ;
}

DfNode *  TraverseParameters::node()
{
	if (TheType != DfNodeType || !TheParameters)
		DbgError("TraverseParameters::node",
		"bad state");
	return  (DfNode *) TheParameters ;
}

BufferStatus* TraverseParameters::GetBufferStatus() 
{
	if (TheType != BufferStatusType || !TheParameters)
		DbgError("TraverseParameters::GetBufferStatus",
		"bad state");
	return  (BufferStatus *) TheParameters ;
}

TransmitNetworkDescription& TraverseParameters::network_description()
{
	if (TheType != TransmitDescriptionType || !TheParameters)
        DbgError("TraverseParameters::network_description",
        "bad state");
	return * (TransmitNetworkDescription *) TheParameters ;
}

NodeReplacementList& TraverseParameters::node_replacement_list()
{
    if (TheType !=  ReplacementListType || !TheParameters)
        DbgError("TraverseParameters::node_replacement_list",
        "bad state");
    return * (NodeReplacementList *) TheParameters ;
}


EmitTargetCode * TraverseParameters::GetEmitTargetCode() 
{
	if (!TheTargetCode) DbgError("TraverseParameters::GetEmitTargetCode",
		"bad state");
	return  TheTargetCode ;
}

SharedArray * TraverseParameters::GetSharedArray()
{
	if (!TheTargetCode) return 0 ;
	return TheTargetCode->GetSharedArray();
}

double RecLog2 = 1.44269504088896340737 ;

enum CompareType{CompareExact,CompareLess,CompareEnd};

static int DivOK(int Bits, int32 Factor, double rec, int32 BufSize,
	CompareType cmp)
{
/*
 * LogOut << Bits << ", " << Factor << ", " << rec << ", "
 *	<< BufSize << "\n" ;
 */
	double AppRec = Factor / (double)(1 <<Bits);
	double diff = AppRec -rec ;
	if (diff < 0.) {
/*
 *		LogOut << "App < Exact: " << Bits << ", " << Factor << ", " << rec 
 *			<< BufSize << "\n" ;
 */
		if (cmp != CompareExact) return 1 ;
	}
/*
 *	LogOut << "diff(" << diff << ") * BufSize(" << BufSize << ") = " <<
 *		diff * BufSize << "\n" ;
 */
	for (int i = 0 ; i < BufSize ; i++) {
		int Exact = (int)(rec * i) ;
		int App = (Factor * i) >> Bits ;
		if (cmp == CompareExact) {if (App != Exact) return 0 ;}
		else if (App > Exact) {
/*
 *			LogOut << "App = " << App << ", Exact = " << Exact << ", i = "
 *				<< i << "\n" ;
 */
			return 0 ;
		}
	}
	// LogOut << "returned 1\n" ;
	return 1 ;
 
	// if (diff * BufSize > .999999) return 0 ;
	// return 1 ;
}

static void ComputeDivSim(int32 Divisor, int32 Buf, int32& Factor,
	int& Try)
{
	// LogOut << "Divisor = " << Divisor << ", Buf = " << Buf << "\n" ;
	int BitsBuf =1 ;
	while (Buf > (1<<BitsBuf++));
	int BitLimit = 31 - BitsBuf ;
	// LogOut << "BitLimit = " << BitLimit << "\n" ;
	int Bits = (int) (0.5 + log(Divisor) * RecLog2) ;
	double Rec = 1. / Divisor ;
	int bump_fac,cmp ;
	 for (cmp = CompareExact; cmp < CompareEnd; cmp++) 
	  for (bump_fac = 0 ; bump_fac < 2; bump_fac++)
	  for (Try = Bits; Try < BitLimit ;Try++) {
		int32 Num = 1 << Try ;
		Factor = Num / Divisor ;
		if (!bump_fac) if (Factor * Divisor < Num) Factor++;
		if (!Factor) continue ;
		if (DivOK(Try,Factor,Rec,Buf,(CompareType)cmp)) goto ExitLoop ;
	}
ExitLoop:
	if (Try >= BitLimit) Try = Factor = 0 ;
/*
 *	LogOut << "Factor = " << Factor << ", Try = " << Try << ", bmp = " <<
 *		bump_fac << " "<<(cmp == CompareExact? "EXACT":"approximate") << "\n" ;
 */
}


TargetWriterParameters::TargetWriterParameters(DfNodeOutLink& Out, int32 BufSz)
{
/*
 *	LogOut << "TargetWriterParameters::TargetWriterParameters for `" <<
 *		Out.GetDriverNodeName() << "'\n" ;
 */
	// MinimumDataToExecute =  Out.GetIncrementOut() * Out.GetChunkSize();
	MinimumDataToExecute =  Out.GetMinBufferSize();
/*
 *	LogOut << "inc_out = " << Out.GetIncrementOut() << ", chunk = " <<
 *			Out.GetChunkSize() << "\n" ;
 */
	ComputeDivSim(Out.GetIncrementOut()*Out.GetChunkSize(),
		BufSz, DivisorFactor, DivisorShift);
/*
 *	LogOut << "factor = " << DivisorFactor << ", shift = " <<
 *		DivisorShift << "\n" ;
 */
	if (!DivisorFactor) State.Error("buffer size out of range for target");
}

TargetWriterParameters::TargetWriterParameters(DfNodeInLink& In, int32 BufSz)
{
	int32 Overlap = In.GetOverlap();
	MinimumDataToExecute = In.GetMinBufferSize();
	// LogOut<<"ComputeDivSim for InLink of `" << In.GetOutputNodeName()<<"'\n";
	ComputeDivSim(In.GetIncrementIn()*In.GetChunkSize(),
		BufSz - Overlap*In.GetChunkSize(), DivisorFactor, DivisorShift);

	if (!DivisorFactor) State.Error("buffer size out of range for target");
}

TargetReaderParameters::TargetReaderParameters(DfNodeInLink& In, int32 BufSz):
	TargetWriterParameters(In,BufSz)
{
	Delay = In.GetDelay();
	LeftOver = In.GetOverlap() ;
}

void TargetWriterParameters::EmitParameters(OutTokens& Out) const
{
	Out.NextOut(DivisorShift);
	Out.NextConcat(",");
	Out.NextOut(DivisorFactor);
	Out.NextConcat(",");
	Out.NextOut(MinimumDataToExecute);
}



TargetAdjustState TargetBufferParameters::AdjustSize(int32 NewSize)
{
	TargetAdjustState Return = TargetAdjustOK ;
	if (NewSize != Size) Return = TargetAdjustChange ;
	Size = NewSize ;
/*
 *	LogOut << "TargetBufferParameters::AdjustSize(" << NewSize
 *		<< ") returning " << Return << "\n" ;
 */
	return Return ;
}

TraverseResult TargetBufferParameters::CheckTargetBufferSize(
	TraverseObject& obj, int32 ExistingSize, int32 MinSize)
{
	BufferStatus * Status = obj.GetBufferStatus() ;
	switch (Status->GetGoal()) {
case BufferStatus::FixedSequence:
		break;
case BufferStatus::ExistingSize:
/*
 *		LogOut << "TargetBufferParameters setting Size(" <<
 *			Size << ") to " << ExistingSize << "\n" ;
 */
		Size = ExistingSize ;
		break ;
default:
		DbgError("Buffer::CheckTargetBufferSize","bad case");
	}
	if (Size < MinSize) {
		if (MinSize >= Status->GetMaxSize()) {
			HelpOut << "Maximum allowed buffer size (" <<
				Status->GetMaxSize() << ") is too small.\n" ;
			HelpOut << "Size of " << MinSize << " required.\n" ;
		}
		Size = MinSize ;
	}
	Status->IncrementTotalSize(Size);
	Status->UpdateMaxSize(Size);
	return TraverseOK;
}

