#include <complex.h>
#include <math.h>
#include "ObjProDSP/portable.h"
#include "ObjProGen/newaloc.h"
#include "ObjProComGui/cgidbg.h"
#include "ObjProGen/dspconst.h"
#include "ObjProArith/hrdarth.h"
#include "ObjProArithGen/arthfnc.h"
#include "ObjProDSPcom/firc.h"
#include "ObjProArith/hrdcmp.h"
#include "ObjProNet/dfnode.h"

#include "ObjProUsr/cxfir.h"
#include "ObjProGen/outtok.h"
#include "ObjProGui/intfc.h"
#include "ObjProGui/remmen.h"
#include "ObjProGui/user.h"
#include "ObjProGui/dynmnu.h"
#include "ObjProArith/typout.h"
#include "ObjProGui/array.h"
#include "ObjProGui/interinit.h"
#include "ObjProGen/stattyp.h"
static EntityList * CxFirNodeList = 0 ;
static InteractiveEntity * IntEntCxFir ;
void CxFirNodesInit();
CxFir::CxFir (const char * Name, int16 Resample, int16 ZeroPad, double DemodFreq, 
		int16 Odd, MachWord * Coeff, int32 Coeff_Length):
	ProcessNode(Name, 1, 1, 2, 0, DeltaIn(Resample , ZeroPad), 
	DeltaOut(Resample , ZeroPad), 
	(1 + ((Coeff_Length << 1) - Odd - Resample - 1) / (ZeroPad ? ZeroPad : 1)), 0, 0, 0, TimingTypeLinear, 
	(ArithType::ArithCapabilities)ArithType::ArithTypeUndefined, (ArithType::ArithCapabilities)ArithType::ArithTypeUndefined)
,
	Resample_1(Resample),
	ZeroPad_2(ZeroPad),
	DemodFreq_3(DemodFreq),
	Odd_4(Odd),
	Coeff_5(Coeff),
	Coeff_Length_6(Coeff_Length)
{
	if (!CxFirNodeList) CxFirNodesInit() ;
	CxFirNodeList->Append(MakeDeclaredEntity(this, IntEntCxFir)) ;
	InitArithType(TheArithType);
	NewMenuItem("CxFir",GetName());
#line 134 "../cxfir.usr"
 
	clear_buffers();
	// LogOut << "Length = " << GetCoeff_Length() << "\n" ;
	FilterLength = (GetCoeff_Length() << 1) - Odd ;
	DataBufSize = FilterLength  ;
	DataBuf = NEW(CxMachWord,DataBufSize);
	Demod = GetDemodFreq() != 0.0 ;
#ifndef __NT_VC__
	DemodDeltaPhase = complex(cos(-DemodFreq),sin(-DemodFreq));
#else
	DemodDeltaPhase.x = cos(-DemodFreq);
	DemodDeltaPhase.y = sin(-DemodFreq);
#endif
#line 59 "../cxfir.cxx"
} // end constructor

CxFir::~CxFir()
{
	TheMenuServer->DeleteMenuItem("CxFir",GetName());
	CxFirNodeList->Delete(GetName()) ;
#line 154 "../cxfir.usr"
 
	delete DataBuf ;
	DataBuf = 0;
#line 70 "../cxfir.cxx"
} // end destructor

int CxFir::CheckSafeDelete()
{
	int Safe_Check_Return = DfNode::CheckSafeDelete();
	if (!Safe_Check_Return) return 0;
	return 1;
} // end check safe delete

CxFir * CxFirDef;

ErrCode CxFir::DoNode(int32 k)
{
#line 178 "../cxfir.usr"
 
	int OutCount = 0 ;
	for(;;) {
		while (ValidData < DataBufSize) {
			CxMachWord Data ;
			if (ZeroPadIndex) Data = 0 ;
			else {
				if (!GetAvailableData()) return OK ;
				Data = ReadCxWord();
			}
			if (GetZeroPad()) if(++ZeroPadIndex > GetZeroPad())
				ZeroPadIndex=0;
			if (Demod) {
#ifndef __NT_VC__
				if (++DemodIndex % 100 == 0) {
					DemodPhase = complex(
						cos (-DemodIndex * GetDemodFreq()),
						sin (-DemodIndex * GetDemodFreq()));
				} else DemodPhase *= DemodDeltaPhase ;
#else
				if (++DemodIndex % 100 == 0) {
					DemodPhase.x = cos (-DemodIndex * GetDemodFreq());
					DemodPhase.y = sin (-DemodIndex * GetDemodFreq());
				} else DemodPhase = NT_complex_multiply(DemodPhase,DemodDeltaPhase);
#endif
#ifndef __NT_VC__
				if (Data != 0)
#else
				if ((MachWordCast) MachReal(Data) != 0 ||
					(MachWordCast) MachImag(Data) != 0)	
#endif
				{
					CxMachWord Fact = FullScale(DemodPhase) ;
					CxAccMachWord Temp = ((CxAccMachWord) Data) *
						(CxAccMachWord) Fact ;
					Data = (CxMachWord) AccScale(Temp,1);
				}
			}
			DataBuf[NextSampleToWrite++] = Data ;
			ValidData++ ;
			if (NextSampleToWrite >= DataBufSize)
				NextSampleToWrite = 0;
		}
		int32 CoeffIndex = 0 ;
		CxAccMachWord Sum = 0 ;
		int UpFlag = 1;
		for (int32 j = 0 ; j < FilterLength; j++ ) {
			int32 jj = j + NextSampleToWrite ;
			if (jj >= DataBufSize) jj -= DataBufSize ;
			MachWord Tmp = GetCoeff()[CoeffIndex] ;
			Sum+= ((CxAccMachWord) DataBuf[jj]) * (AccMachWord) Tmp;
			if (UpFlag) CoeffIndex++ ;
			else CoeffIndex-- ;
			if (CoeffIndex >= GetCoeff_Length()) {
				if (GetOdd()) CoeffIndex-=2 ;
				else CoeffIndex-- ;
				UpFlag = 0;
			}
		}
		CxMachWord Out = AccScale(Sum,2);
		WriteCxWord(Out);
		ValidData -= GetResample();
		if (++OutCount == k) return OK ;
	}
	return OK ;
#line 150 "../cxfir.cxx"
} // end kernel code

static UserEntity * MakeCxFir(OutTokens& Out, EntityReq Request,
	InteractiveEntity& IntNode,
	ArithType::ArithCapabilities arith = (ArithType::ArithCapabilities) TheArithType) ;
int CxFir::CppList(OutTokens& Out, CppListCmds Cmd)
{
	return IntEntCxFir->CppList(Out,Cmd,this);
}

void CxFir::Describe(OutTokens& Out, ListEntity Option)
{
	switch(Option) {
case ListSingleEntity:
		Out.NewLine();
		MakeCxFir(Out,EntityReqDescribeFull,*IntEntCxFir, (ArithType::ArithCapabilities) TheArithType);
		Out.NewLine();
		Out.NextQuoteOut("Resample");
		Out.NextFillOut("(");
		Out.NextFillOut(TypeToString(GetResample()));
		Out.NextFillOut(")");
		Out.NextFillOut("specifies the filter resampling factor.");
		Out.NextFillOut("This is the ratio of the input sampling rate to the");
		Out.NextFillOut("output sampling rate.");
		Out.NextQuoteOut("ZeroPad");
		Out.NextFillOut("(");
		Out.NextFillOut(TypeToString(GetZeroPad()));
		Out.NextFillOut(")");
		Out.NextFillOut("specifies the number of 0's that are");
		Out.NextFillOut("added after each input sample. If Zero padding is done the filter");
		Out.NextFillOut("must be designed");
		Out.NextFillOut("as an interpolation filter.");
		Out.NextQuoteOut("DemodFreq");
		Out.NextFillOut("(");
		Out.NextFillOut(TypeToString(GetDemodFreq()));
		Out.NextFillOut(")");
		Out.NextFillOut("is the demodulation frequency in radians per");
		Out.NextFillOut("sample. Input sample N is multiplied by");
		Out.NextFillOut("e^(-i * 2 * Pi *");
		Out.NextQuoteOut("DemodFreq");
		Out.NextFillOut("* N)");
		Out.NextFillOut("before the filtering");
		Out.NextFillOut("operation.  If");
		Out.NextQuoteOut("DemodFreq");
		Out.NextFillOut("is 0 then the demodulation step");
		Out.NextFillOut("is skipped.");
		Out.NextQuoteOut("Odd");
		Out.NextFillOut("(");
		Out.NextFillOut(TypeToString(GetOdd()));
		Out.NextFillOut(")");
		Out.NextFillOut("determines if the filter is odd (");
		Out.NextQuoteOut("Odd");
		Out.NextFillOutConcat("=1)");
		Out.NextFillOut("or even (");
		Out.NextQuoteOut("Odd");
		Out.NextFillOutConcat("=0).");
		Out.NextQuoteOut("Coeff");
		Out.NextFillOut("is the list of filter coefficients. The filter is");
		Out.NextFillOut("symmetric and only half (or half plus one");
		Out.NextFillOut("for odd length filters) are specified. The first");
		Out.NextFillOut("in the list is the first coefficient of the filter.");
		Out.NextFillOut("The middle coefficient is at the end of the list.");
		Out.NextFillOut("The default values for the coefficients define a low pass");
		Out.NextFillOut("FIR filter with a pass band of .125 times the sample rate");
		Out.NextFillOut("and transition band of .375 times the sample rate. The");
		Out.NextFillOut("pass band is extremely flat and the stop band is down over");
		Out.NextFillOut("100 db. This is an overdesigned filter for most practical");
		Out.NextFillOut("applications but it provides a good test case. The performance");
		Out.NextFillOut("will be degraded by 16 bit integer arithmetic.");
		Out.NewLine();
		break;
case ListEntityMembers:
		Out.NextOut(GetName());
		break;
case ListGlobalClasses:
case ListEntityClasses:
		break ;
case ListSetParameterValues:
		IntEntCxFir->GetOneParameter("Resample")->
			IntP->CurrentValue = Resample_1;
		IntEntCxFir->GetOneParameter("ZeroPad")->
			IntP->CurrentValue = ZeroPad_2;
		IntEntCxFir->GetOneParameter("DemodFreq")->
			FloatP->CurrentValue = DemodFreq_3;
		IntEntCxFir->GetOneParameter("Odd")->
			IntP->CurrentValue = Odd_4;
		IntEntCxFir->GetOneParameter("Coeff")->
			ArrayP->CurrentValue = 
		(new ArrayData)->SetArrayData(Coeff_Length_6, DecMachWord, Coeff_5);
		break;
	}
} // end  list entity switch

MachWordType TheDataForCxFirCoeffDefaultArray[] = {

		( CastMachWord ) NormToHardLimitMachWord (1.00018552e-04), 
		( CastMachWord ) NormToHardLimitMachWord (3.70747893e-04), 
		( CastMachWord ) NormToHardLimitMachWord (4.46594395e-04), 
		( CastMachWord ) NormToHardLimitMachWord (-5.36969535e-04), 
		( CastMachWord ) NormToHardLimitMachWord (-2.67492760e-03), 
		( CastMachWord ) NormToHardLimitMachWord (-3.52687595e-03), 
		( CastMachWord ) NormToHardLimitMachWord (8.25715193e-04), 
		( CastMachWord ) NormToHardLimitMachWord (1.01494638e-02), 
		( CastMachWord ) NormToHardLimitMachWord (1.50874056e-02), 
		( CastMachWord ) NormToHardLimitMachWord (2.48008436e-03), 
		( CastMachWord ) NormToHardLimitMachWord (-2.76876913e-02), 
		( CastMachWord ) NormToHardLimitMachWord (-4.93498540e-02), 
		( CastMachWord ) NormToHardLimitMachWord (-2.20594765e-02), 
		( CastMachWord ) NormToHardLimitMachWord (7.30790837e-02), 
		( CastMachWord ) NormToHardLimitMachWord (2.04010323e-01), 
		( CastMachWord ) NormToHardLimitMachWord (2.99286359e-01)
	};

	static ArrayData CxFirCoeffDefaultArray = { 16, DecMachWord,
		(void *) TheDataForCxFirCoeffDefaultArray};

void CxFirNodesInit()
{
	if (CxFirNodeList)  return ;

	static StringParam CxFirNameParam =
		{"CxFir", MakeNewEntityName, 0, LegalEntityName};
	static IntParam CxFirResampleParam = {
		 1, 0,  0,  1, 0, 32767};
	static IntParam CxFirZeroPadParam = {
		 0, 0,  0,  0, 0, 32767};
	static FloatParam CxFirDemodFreqParam = {
		 0.0, 0, 0, -1e+100, 0, 1e+100};
	static IntParam CxFirOddParam = {
		 0, 0,  0,  0,  0,  1};
	static  CastMachWord CxFirCoeffLowerArrayBound =
		{( CastMachWord ) NormToHardLimitMachWord (-1000)} ;

	static  CastMachWord CxFirCoeffUpperArrayBound =
		{( CastMachWord ) NormToHardLimitMachWord (1000)} ;

	static ArrayParam CxFirCoeffParam = {
		DecMachWord, &CxFirCoeffDefaultArray,
		0, 0, (void *) &CxFirCoeffLowerArrayBound, 0,
		(void *) &CxFirCoeffUpperArrayBound, 3, 1024};

	static OneParameter CxFirParArray[] = {
		{"Name", 0, "node name", 0, 0, &CxFirNameParam},
		{"Resample", 0, 
			"filter resampling factor (input rate/output rate)",
			&CxFirResampleParam},
		{"ZeroPad", 0, 
			"zero padding of input",
			&CxFirZeroPadParam, 0, 0, 0, 0, 1},
		{"DemodFreq", 0, 
			"demodulation frequency (0 for no demodulation)",
			0, &CxFirDemodFreqParam, 0, 0, 0, 1},
		{"Odd", 0, 
			"if set the filter length is odd",
			&CxFirOddParam, 0, 0, 0, 0, 1},
		{"Coeff", 0, 
			"list of unique coefficients",
			0, 0, 0, 0,
			&CxFirCoeffParam, 1},

		{0}
	};

	CxFirNodeList = new EntityList;
	IntEntCxFir = new InteractiveEntity("CxFir", CxFirNodeList,
		MakeCxFir, InteractiveNode, "cxfir.h",
		0, "ProcessNode");
	IntEntCxFir->SetParameters(new UserParameters(CxFirParArray));
	TheNodes->Append(IntEntCxFir);
} // end initalization

static UserEntity * MakeCxFir(OutTokens& Out, EntityReq Request,
	InteractiveEntity& IntNode,
	ArithType::ArithCapabilities arith)
{
	switch(Request) {
case EntityReqDescribe:

case EntityReqDescribeFull:
		Out.NextQuoteOut("CxFir");
		Out.NextFillOut("implements two parallel real symmetric FIR filters that");
		Out.NextFillOut("operate on a complex data stream. Optional");
		Out.NextFillOut("complex demodulation multiplies the input by a complex cosine");
		Out.NextFillOut("with phase increment -");
		Out.NextFillOutConcat("DemodFreq");
		Out.NextFillOut("radians between each pair of");
		Out.NextFillOut("samples. Input samples can have");
		Out.NextQuoteOut("ZeroPad");
		Out.NextFillOut("0's interpolated");
		Out.NextFillOut("between them. Output sample can be generated for every");
		Out.NextQuoteOut("Resample");
		Out.NextFillOut("input (or zero pad) sample. The");
		Out.NextQuoteOut("Coeff");
		Out.NextFillOut("array contains half of");
		Out.NextFillOut("the symmetric coefficients.");
		Out.NextQuoteOut("Odd");
		Out.NextFillOut("indicates if there are an even(0) or");
		Out.NextFillOut("odd(1) total number of coefficients.");
		Out.NewLine();
		break;

case EntityReqCreate:
	{
		const char * Name = IntNode.GetStringParameterValue("Name");
		int16 Resample =
			IntNode.GetIntParameterValue("Resample");
		int16 ZeroPad =
			IntNode.GetIntParameterValue("ZeroPad");
		double DemodFreq =
			IntNode.GetFloatParameterValue("DemodFreq");
		int16 Odd =
			IntNode.GetIntParameterValue("Odd");
		MachWord * Coeff =
			(MachWord *  ) IntNode.GetArrayParameterValue("Coeff");
		int32 Coeff_Length =
			IntNode.GetLengthArrayParameterValue("Coeff");
		return new CxFir(Name, Resample, ZeroPad, DemodFreq, Odd, Coeff, Coeff_Length);

	}
	}
	return 0;
}

static InitObj LocalInit(CxFirNodesInit, "CxFir", "ProcessNode");

