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

#include "ObjProUsr/cxfft.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 * CxFFTNodeList = 0 ;
static InteractiveEntity * IntEntCxFFT ;
void CxFFTNodesInit();
CxFFT::CxFFT (const char * Name, int16 LogSize, double Overlap, 
		double CenterFrequency, int16 InverseFlag):
	ProcessNodeStr(Name, 1, 1,  new StreamStr(2 , 1), 
	FftStreamStr(2 , LogSize , CenterFrequency , Overlap), 0, FftSize(LogSize) - FftOverlap(Overlap , LogSize), 1, 
	FftOverlap(Overlap , LogSize), 0, TimingTypeLinear)
,
	LogSize_1(LogSize),
	Overlap_2(Overlap),
	CenterFrequency_3(CenterFrequency),
	InverseFlag_4(InverseFlag)
{
	if (!CxFFTNodeList) CxFFTNodesInit() ;
	CxFFTNodeList->Append(MakeDeclaredEntity(this, IntEntCxFFT)) ;
	InitArithType(TheArithType);
	NewMenuItem("CxFFT",GetName());
#line 108 "../cxfft.usr"
 
	clear_buffers();
	double Pi = atan(1.) * 4. ;
	Size = FftSize(GetLogSize());
	FftBufferSize= (int32)(Size + Size*GetOverlap() +.005) ;
	FftBuffer = NEW ( CxMachWord,FftBufferSize) ;
	CxCoeff = NEW ( CxMachWord, Size) ;
	double Isi = GetInverseFlag() ? 1. : -1. ;
	for (int32 i = 0 ; i < Size; i++) {
		CxCoeff[i] = CxMachWord( FullScale( cos( Isi * Pi * i / Size)),
			FullScale( sin( Isi * Pi * i / Size))) ;
#ifdef DEBUG4
		LogOut << "FFTCoeff[" << i << "] = (" << MachReal(CxCoeff[i]) <<
			", " << MachImag(CxCoeff[i]) << ")\n" ;
#endif
 
	}
#line 59 "../cxfft.cxx"
} // end constructor

CxFFT::~CxFFT()
{
	TheMenuServer->DeleteMenuItem("CxFFT",GetName());
	CxFFTNodeList->Delete(GetName()) ;
#line 128 "../cxfft.usr"
 
	delete FftBuffer;
	FftBuffer = 0;
	delete CxCoeff;
	CxCoeff = 0;
#line 72 "../cxfft.cxx"
} // end destructor

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

CxFFT * CxFFTDef;

ErrCode CxFFT::DoNode(int32 k)
{
#line 151 "../cxfft.usr"
 
	
#ifdef DEBUG4
	LogForm("CxFFT::DoNode(%d)", k);
#endif
	int32 j ;
	for (int32 i = 0 ; i < k ; i++ ) {
		// move data to front of buffer if needed
		int32 ii = FftBufferBase;
		if (FftBufferBase) for (j = 0 ; j < DataInBuffer ; j++)
			FftBuffer[j] = FftBuffer[ii++];
		// read in needed data
		for (j = DataInBuffer; j < Size; j++) {
			FftBuffer[j] = ReadCxWord();
#ifdef DEBUG4
			LogOut << "FftBuffer[" << j << "] = " <<
				FftBuffer[j] << "\n" ;
#endif
		}
#ifdef DEBUG4
		LogForm("Read %d words, i = %d", Size - DataInBuffer,i);
#endif
		// copy overlapped data to preserve it for next iteration
		int32 IncIn = GetIncrementIn(0);
		int32 Over = Size - IncIn ;
		ii = Size ;
		if (Over) for (j = IncIn; j < Size; j++)
			FftBuffer[ii++] = FftBuffer[j];
		// LogForm("Moved %d words from %d to %d",
		//	Size-IncIn, IncIn , Size);
		FftBufferBase = Size ;
		DataInBuffer = Over ;
		// bit reverse current data
		j = 0;
		for (ii = 0 ; ii < Size ; ii++) {
			if (ii < j) {
				CxMachWord temp = FftBuffer[j];
				FftBuffer[j] = FftBuffer[ii] ;
				FftBuffer[ii] = temp ;
			}
			int32 M = Size >> 1 ;
			while (j >= M) {
				j = j - M ;
				M = (M+1) >> 1 ;
			}
			j = j + M ;
		}
		// LogMsg("Data bit reversed");
		// do fft
		// from IEEE Fourea page 1.1-5
		int32 Mmax = 1 ;
		while (Size > Mmax) {
			Normalize(FftBuffer,Size,NormPower2FFTPass);
			// LogMsg("Data after normalize");
#ifdef DEBUG4
			for (j = DataInBuffer; j < Size; j++) {
				LogOut <<  "FftBuffer[" << j << "] = (" <<
					(MachWordCast) MachReal(FftBuffer[j]) << ", " <<
					(MachWordCast) MachImag(FftBuffer[j]) << ")\n" ;
			}
			LogForm("Main FFT loop Mmax = %d", Mmax);
#endif
			int32 Istep = Mmax << 1 ;
			for (int32 M = 0 ; M < Mmax; M++) {
				int32 Index = M*Size/Mmax ;
				CxAccMachWord W = CxCoeff[Index] ;
				for (ii = M; ii < Size ; ii+=Istep) {
					j = ii + Mmax ;
					CxAccMachWord Temp = W * 
						(CxAccMachWord) FftBuffer[j];
					CxMachWord OffTemp = AccScale(Temp);
					FftBuffer[j] = FftBuffer[ii]-OffTemp;
					FftBuffer[ii] = FftBuffer[ii]+OffTemp;
#ifdef DEBUG4
					LogOut << "FftBuffer[" << ii << "] = (" <<
						(MachWordCast) MachReal(FftBuffer[ii]) << ", " <<
						(MachWordCast) MachImag(FftBuffer[ii]) << ")\n" ;
					LogOut << "OffTemp = " << OffTemp << "\n";
#endif
				}
			}
			Mmax = Istep ;
#ifdef DEBUG4
			for (ii = 0 ; ii < Size ; ii++) {
				LogOut <<  "FftBuffer[" << ii << "] = (" <<
					(MachWordCast) MachReal(FftBuffer[ii]) << ", " <<
					(MachWordCast) MachImag(FftBuffer[ii]) << ")\n" ;
 			}
#endif
		}
#ifndef TI_C30
	if (TheArithType == ArithType::ArithDouble ||
		TheArithType == ArithType::ArithFloat) 
#endif
		{
			if (GetInverseFlag()) 
			for (ii = 0 ; ii < Size ; ii++) {
				FftBuffer[ii] = FftBuffer[ii] / (MachWord) Size;
			}
		// LogMsg("Inverse FFT divide complete");
		}
		// output results
		Normalize(FftBuffer,Size,NormPower2Full);
		// Rotate data to display center frequency correctly
		int32 IndexStart = index_start(Size, GetCenterFrequency());
		
		for (ii = 0 ; ii < Size ; ii ++ ) {
			// Remove artifact from coefficients starting at
			// beginning rather than middle of data
			// (this produces 180 degree phase shift in
			// succesive bin outputs.
			int32 iii = ii + IndexStart ;
			if (iii >= Size) iii -=Size ;
			if (iii&1) WriteCxWord(-FftBuffer[iii],0);
			else WriteCxWord(FftBuffer[iii],0);
#ifdef DEBUG4
			LogOut <<  "FftBuffer[" << ii << "] = (" <<
				(MachWordCast) MachReal(FftBuffer[ii]) << ", " <<
				(MachWordCast) MachImag(FftBuffer[ii]) << ")\n" ;
#endif
		}
#ifdef DEBUG4
		LogForm("Wrote %d words, i = %d", Size,i);
#endif
	}
	return OK ;
#line 213 "../cxfft.cxx"
} // end kernel code

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

void CxFFT::Describe(OutTokens& Out, ListEntity Option)
{
	switch(Option) {
case ListSingleEntity:
		Out.NewLine();
		MakeCxFFT(Out,EntityReqDescribeFull,*IntEntCxFFT, (ArithType::ArithCapabilities) TheArithType);
		Out.NewLine();
		Out.NextQuoteOut("LogSize");
		Out.NextFillOut("(");
		Out.NextFillOut(TypeToString(GetLogSize()));
		Out.NextFillOut(")");
		Out.NextFillOut("is the log base 2 of the FFT size.");
		Out.NextQuoteOut("Overlap");
		Out.NextFillOut("(");
		Out.NextFillOut(TypeToString(GetOverlap()));
		Out.NextFillOut(")");
		Out.NextFillOut("specifies the fractional overlap of successive");
		Out.NextFillOut("FFTs. For example");
		Out.NextQuoteOut("Overlap");
		Out.NextFillOut("= .5 and");
		Out.NextQuoteOut("LogSize");
		Out.NextFillOut("= 64 would");
		Out.NextFillOut("result in each FFT being 32 samples beyond the previous FFT.");
		Out.NextQuoteOut("Overlap");
		Out.NextFillOut("= 1.0 is interpreted to mean that successive FFTs");
		Out.NextFillOut("having their inputs separated by a single sample.");
		Out.NextFillOut("The true center frequency of the FFT is determined by");
		Out.NextFillOut("the data entering it.");
		Out.NextQuoteOut("CenterFrequency");
		Out.NextFillOut("(");
		Out.NextFillOut(TypeToString(GetCenterFrequency()));
		Out.NextFillOut(")");
		Out.NextFillOut("allows you to");
		Out.NextFillOut("rotate the output bins to conform to this. The default");
		Out.NextFillOut("value of .5 corresponds to a signal with center frequency");
		Out.NextFillOut("of 0 hz in the input sample stream. This value should");
		Out.NextFillOut("set to the relative position of the center");
		Out.NextFillOut("frequency of the input sample stream.");
		Out.NextQuoteOut("InverseFlag");
		Out.NextFillOut("(");
		Out.NextFillOut(TypeToString(GetInverseFlag()));
		Out.NextFillOut(")");
		Out.NextFillOut("set to 1 selects an inverse FFT.");
		Out.NewLine();
		break;
case ListEntityMembers:
		Out.NextOut(GetName());
		break;
case ListGlobalClasses:
case ListEntityClasses:
		break ;
case ListSetParameterValues:
		IntEntCxFFT->GetOneParameter("LogSize")->
			IntP->CurrentValue = LogSize_1;
		IntEntCxFFT->GetOneParameter("Overlap")->
			FloatP->CurrentValue = Overlap_2;
		IntEntCxFFT->GetOneParameter("CenterFrequency")->
			FloatP->CurrentValue = CenterFrequency_3;
		IntEntCxFFT->GetOneParameter("InverseFlag")->
			IntP->CurrentValue = InverseFlag_4;
		break;
	}
} // end  list entity switch

static ValueType * SetCenterFrequency(OutTokens&,EntityReq Request,
		UserParameters * Param,UserEntity *This) 
{
	switch (Request) {
case EntityReqDescribe:
	break ;
case EntityReqDescribeFull:
	break ;
case EntityReqCall:
		{		// Call procedure
				double CenterFrequency =
			Param->GetFloatParameterValue("CenterFrequency");
		((CxFFT *) This)->SetCenterFrequency(CenterFrequency);
		return 0 ;
	}
	}
	return 0;
}

void CxFFTNodesInit()
{
	if (CxFFTNodeList)  return ;

	static StringParam CxFFTNameParam =
		{"CxFFT", MakeNewEntityName, 0, LegalEntityName};
	static IntParam CxFFTLogSizeParam = {
		 4, 0,  0,  1,  0,  512};
	static FloatParam CxFFTOverlapParam = {
		 0.0, 0,  0,  0.0,  0,  1.0};
	static FloatParam CxFFTCenterFrequencyParam = {
		 0.5, 0,  0,  -1.0,  0,  1.0};
	static IntParam CxFFTInverseFlagParam = {
		 0, 0,  0,  0,  0,  1};

	static OneParameter CxFFTParArray[] = {
		{"Name", 0, "node name", 0, 0, &CxFFTNameParam},
		{"LogSize", 0, 
			"log base 2 of FFT size",
			&CxFFTLogSizeParam},
		{"Overlap", 0, 
			"fractional overlap of successive FFTs",
			0, &CxFFTOverlapParam, 0, 0, 0, 1},
		{"CenterFrequency", 0, 
			"position of center frequency in FFT output bins",
			0, &CxFFTCenterFrequencyParam, 0, 0, 0, 1},
		{"InverseFlag", 0, 
			"inverse FFT flag",
			&CxFFTInverseFlagParam, 0, 0, 0, 0, 1},
		{0}
	};

	static OneParameter SetCenterFrequencyCxFFTList[] = {
		{"CenterFrequency", 0, "position of center frequency in FFT output bins",
			 0, &CxFFTCenterFrequencyParam, 0, 0, 0, 1},
			{0}
	};
	UserParameters * SetCenterFrequencyMemberParam = new UserParameters
		(SetCenterFrequencyCxFFTList);

	Procedure * SetMemberProcCenterFrequency = new Procedure("SetCenterFrequency", SetCenterFrequency,
		 SetCenterFrequencyMemberParam, "void");
static ValueType * CxFFTMembers[2] ;
	int ii = 0 ;
	CxFFTMembers[ii++] = new ValueType(DecProcedure, SetMemberProcCenterFrequency) ;
	CxFFTMembers[ii++] = 0 ;
	CxFFTNodeList = new EntityList;
	IntEntCxFFT = new InteractiveEntity("CxFFT", CxFFTNodeList,
		MakeCxFFT, InteractiveNode, "cxfft.h",
		CxFFTMembers, "ProcessNodeStr");
	IntEntCxFFT->SetParameters(new UserParameters(CxFFTParArray));
	TheNodes->Append(IntEntCxFFT);
} // end initalization

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

case EntityReqDescribeFull:
		Out.NextQuoteOut("CxFFT");
		Out.NextFillOut("computes a series of complex FFTs of size N = 2 ^");
		Out.NextQuoteOut("LogSize");
		Out.NextFillOut("on");
		Out.NextFillOut("a single complex input channel. A forward FFT is computed if");
		Out.NextQuoteOut("InverseFlag");
		Out.NextFillOut("is 0 and an inverse FFT if this parameter is 1.");
		Out.NextFillOut("Successive FFTs have their first samples separated by");
		Out.NextFillOut("by N *");
		Out.NextQuoteOut("Overlap");
		Out.NextFillOut("samples. If");
		Out.NextQuoteOut("Overlap");
		Out.NextFillOut("is 1.0 then successive inputs");
		Out.NextFillOut("are separated by a single sample.");
		Out.NewLine();
		break;

case EntityReqCreate:
	{
		const char * Name = IntNode.GetStringParameterValue("Name");
		int16 LogSize =
			IntNode.GetIntParameterValue("LogSize");
		double Overlap =
			IntNode.GetFloatParameterValue("Overlap");
		double CenterFrequency =
			IntNode.GetFloatParameterValue("CenterFrequency");
		int16 InverseFlag =
			IntNode.GetIntParameterValue("InverseFlag");
		return new CxFFT(Name, LogSize, Overlap, CenterFrequency, InverseFlag);

	}
	}
	return 0;
}

static InitObj LocalInit(CxFFTNodesInit, "CxFFT", "ProcessNodeStr");

