#ifndef WABOR_T_DEFINITIONS_H
#define WABOR_T_DEFINITIONS_H

//MACROS WHICH ARE ALLOWED TO GET CHANGED
#define DEBUG (1) //Wenn Debug==1 wird viel mehr ueberprueft als ohne.
#define WABOR_T_NOTHREADS (0) //If this is nonzero, the program does not use threads
#define WABOR_T_MAX_THREADS (64) // Ist groesser als 4
#define WABOR_T_MAXPATHLENGTH (500)
#define WABOR_T_STEPWIDTH_F_DATA (1) //1 over step width
#define WABOR_T_STEPWIDTH_W_DATA (1) //1 over step width
#define WABOR_T_BSPLINE_ORDER (2)
#define WABOR_T_MAX_WINDOW_MEMORY (1e9) //in Bytes

#define WABOR_T_MALLOC_VALUE (0xA3) //Memory gets filled with that value when wabor_t_malloc is called
#define WABOR_T_FREE_VALUE (0xB3)   //Parts of memory gets filled with that value when wabor_t_free is called

//Zeit ist immer in s
//Frequenz ist immer in Hz (1/s)

//Konstanten die Konstant sind
#define PI  (3.14159265358979643846L)  // pi
#define PI2 (9.86960440108935861882L)  //pi^2
#define E  (2.71828182845904523536L)  // exp(1) 
#define SQRT2 (1.4142135623730950488016887242097L) //sqrt(2)
#define SQRTPI (1.7724538509055160272981674833411L) //sqrt(pi)
#define PD_BLOCKSIZE (64) //[samples]
#define PD_SAMPLING_RATE (44100) //[1/s], ebenso Anzahl der Sample pro Sekunde fuer paf

//Konstanten die nichts mit Wabor-Transformation zu tun haben, sondern mit dem Programm
#define WABOR_T_LENGTH_BIN (-5) //in pax[WABOR_T_LENGTHBIN] ist Laenge
#define WABOR_T_PLACE_HOLDER (-6) //in pax[WABOR_T_PLACE_HOLDER] ist noch Platz für einen Wert
//#define WABOR_T_PADDING (4)
#define WABOR_T_ALIGNING_BIN (-7) //hier speicher ich als t_int, wie das Array aligned ist. (Erlaubte Werte sind 0,1,2,3)
#define WABOR_T_PADDING_LEFT (-WABOR_T_ALIGNING_BIN)


//Bins for Both
#define WABOR_T_MAGIC_NUMBER_BIN   (0*sizeof(void*)) //wo in Control Block die Magic Nummer gespeichert wird

//Bins for Gabor Data
#define WABOR_T_POINTER_TO_COEFF_BIN (1*sizeof(void*)) 
#define WABOR_T_NUM_BLOCKS_BIN       (2*sizeof(void*))

//Bins for Control Data
#define WABOR_T_WATOM_PARAM        (3*sizeof(void*))

#define WABOR_T_MAGIC_NUMBER_C (0x57414243)
#define WABOR_T_MAGIC_NUMBER_G (0x57414247)

enum _eWindowType { WIN_ZERO=0, WIN_COS=2, WIN_COS_NOT_WARPED=3, WIN_SQRT_GAUSS=7}; //WIN_TOMM, WIN_TRI, WIN_HAMM, WIN_BLACKHARR, WIN_RECT
enum _eThetaType { BSPLINE=0, EXP_C1_NYQU=4, ID=10, LIN, SQUARE};
enum _eMultType { TIMES_VAL=0, TIMES_CONJ, TIMES_N, DIV_N, RECIPROCAL, CONJUGATE, NORMALIZE };
enum _eProlongType {PROLONG_START, PROLONG_MIDDLE, PROLONG_END };


#if defined(__GNUC__)
    #define UNUSED __attribute__((unused))
    #define ALIGN16 __attribute__ ((aligned (16)))
    #define PURE_FUNCTION __attribute__((pure))
    #define BREAK raise(SIGBREAK)
    #define INLINE static inline
#elif defined(_MSC_VER)
    #define UNUSED 
    #define ALIGN  __declspec( align( 16 ) )
    #define BREAK  __debugbreak()
    #define INLINE __inline
#endif

#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
    #define MUSLEEP(a) (Sleep((a)/1000)) //Wenn auf Windows Systemen
    
    #include <process.h> //Fuer Multihreading
    #include <windows.h> //Fuer Critical Sections
    typedef void thread_return_type;
    #define THREAD_RETURN_VAL
#elif defined(__linux__)
    #include <pthread.h>
    typedef void* thread_return_type;
    #define THREAD_RETURN_VAL (NULL)
    extern int usleep (__useconds_t __useconds);
    #include <unistd.h>    
    #define MUSLEEP(a) (usleep((a))) //Wenn auf Windows Systemen
#include <time.h>
#endif

#if defined(WABOR_T_NOTHREADS)
    #define INITIALIZE_MUTEX(mutex) ((void)0)
    #define LOCK(mutex) ((void)0)
    #define UNLOCK(mutex) ((void)0)
    #define DELETE_MUTEX(mutex) ((void)0)
    #define CREATE_THREAD(thread_id,function,arg) (function((void*) (arg)))
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
    #define INITIALIZE_MUTEX(mutex) (InitializeCriticalSection( (mutex) ))        
    #define LOCK(mutex)  (EnterCriticalSection((mutex )))
    #define UNLOCK(mutex) (LeaveCriticalSection((mutex)))
    #define DELETE_MUTEX(mutex) (DeleteCriticalSection((mutex)))
    #define CREATE_THREAD(thread_id,function,arg) (_beginthread( (function), 0, (arg)))
#elif defined (__linux__)
    #define INITIALIZE_MUTEX(mutex) (pthread_mutex_init ((mutex), NULL))       
    #define LOCK(mutex)   pthread_mutex_lock ((mutex))
    #define UNLOCK(mutex) pthread_mutex_unlock(( mutex))
    #define DELETE_MUTEX(mutex) (pthread_mutex_destroy((mutex)))
    #define CREATE_THREAD(thread_id,function,arg) (pthread_create(&(thread_id), NULL, &function, (void*) (arg)))
#else 
    #define INITIALIZE_MUTEX(mutex) ((void)0)
    #define LOCK(mutex) ((void)0)
    #define UNLOCK(mutex) ((void)0)
    #define DELETE_MUTEX(mutex) ((void)0)
    #define CREATE_THREAD(thread_id,function,arg) (function((void*) (arg)))
#endif   


//General Header Files
#include <stdlib.h> //mathematical functions for int values
#include <math.h> //mathematical functions for float values
#include <string.h>
#include <complex.h>
#include <stdio.h>
#include <assert.h>

#include "../dependencies/fftw/fftw3.h" //Header file for fftw
    #define PAC_TYPE 1
    #if PAC_TYPE==3

	#include <quadmath.h>
	#define PAC_TYPEDEF __float128
	typedef __complex128 pac_type;
	typedef __float128 pac_type_real; //Type des Realteils (und damit des Imaginaerteils)
	#define fftwPAC_plan fftwq_plan
	#define fftwPAC_execute fftwq_execute 
	#define fftwPAC_plan fftwq_plan
	#define fftwPAC_plan_dft_1d fftwq_plan_dft_1d
	#define fftwPAC_destroy_plan fftwq_destroy_plan
    #elif PAC_TYPE==2
	typedef complex long double pac_type;
	typedef long double pac_type_real; //Type des Realteils (und damit des Imaginaerteils)
	#define fftwPAC_plan fftwl_plan
	#define fftwPAC_execute fftwl_execute 
	#define fftwPAC_plan fftwl_plan
	#define fftwPAC_plan_dft_1d fftwl_plan_dft_1d
	#define fftwPAC_destroy_plan fftwl_destroy_plan
	#define clogPAC  clogl
	#define logPAC   logl
	#define crealPAC creall
	#define cimagPAC cimagl
	#define cosPAC   cosl
	#define ccosPAC  ccosl
	#define sinPAC   sinl
	#define csinPAC  csinl
	#define cabsPAC  cabsl
	#define absPAC   absl
	#define fabsPAC  fabsl
	#define expPAC   expl
	#define cexpPAC  cexpl
	#define sqrtPAC  sqrtl
	#define csqrtPAC csqrtl
	#define sincPAC  sincl
	#define sinc_nPAC sinc_nl
	#define powPAC   powl
	#define cpowPAC  cpowl
    #elif PAC_TYPE==1
	typedef complex  double pac_type;
	typedef double pac_type_real; //Type des Realteils (und damit des Imaginaerteils)
	#define fftwPAC_plan fftw_plan
	#define fftwPAC_execute fftw_execute
	#define fftwPAC_plan fftw_plan
	#define fftwPAC_plan_dft_1d fftw_plan_dft_1d
	#define fftwPAC_destroy_plan fftw_destroy_plan
	#define clogPAC  clog
	#define logPAC   log
	#define crealPAC creal
	#define cimagPAC cimag
	#define cosPAC   cos
	#define ccosPAC  ccos
	#define sinPAC   sin
	#define csinPAC  csin
	#define cabsPAC  cabs
	#define absPAC   abs
	#define fabsPAC  fabs
	#define expPAC   exp
	#define cexpPAC  cexp
	#define sqrtPAC  sqrt
	#define csqrtPAC csqrt
	#define sincPAC  sinc
	#define sinc_nPAC sinc_n
	#define powPAC   pow
	#define cpowPAC  cpow
	#define floorPAC floor
	#define cargPAC	 carg
    #elif PAC_TYPE==0
	typedef complex  float pac_type;
	typedef float pac_type_real; //Type des Realteils (und damit des Imaginaerteils)
	#define fftwPAC_plan fftwf_plan
	#define fftwPAC_execute fftwf_execute
	#define fftwPAC_plan fftwf_plan
	#define fftwPAC_plan_dft_1d fftwf_plan_dft_1d
	#define fftwPAC_destroy_plan fftwf_destroy_plan
	#define clogPAC  clogf
	#define logPAC   logf
	#define crealPAC crealf
	#define cimagPAC cimagf
	#define cosPAC   cosf
	#define ccosPAC  ccosf
	#define sinPAC   sinf
	#define csinPAC  csinf
	#define cabsPAC  cabsf
	#define absPAC   absf
	#define fabsPAC  fabsf
	#define expPAC   expf
	#define cexpPAC  cexpf
	#define sqrtPAC  sqrtf
	#define csqrtPAC csqrtf
	#define sincPAC  sincf
	#define sinc_nPAC sinc_nf
	#define powPAC   powf
	#define cpowPAC  cpowf
    #endif
   
	
    typedef double tc_type; //Fuer analyse
    typedef float paf_type; //Fuer Fenster
    typedef double sound_type;   //Fuer synthese

#include "../dependencies/pd/include/m_pd.h"
//#include "../dependencies/qdbmp/qdbmp.h"
    

//Prototypen
    
    
void* wabor_t_malloc0(size_t n);
void wabor_t_free(void *p);


/*INLINE PURE_FUNCTION t_int roundup_16(t_int iNumber) { 
    assert(iNumber>0);
    t_int iRemainder = iNumber % 16;
    if (iRemainder == 0) return iNumber;
    return iNumber + 16 - iRemainder;
}*/
INLINE PURE_FUNCTION t_int roundup_x(t_int iNumber, const t_int iMultiple) { 
    //assert(iNumber>0);
    assert(iMultiple>0);
    if(DEBUG) if(iMultiple == 0) return iNumber;
    t_int iRemainder = iNumber % iMultiple;
    if (iRemainder == 0) return iNumber; 
    return iNumber + iMultiple - iRemainder;
}/*
INLINE PURE_FUNCTION t_int rounddown_16(t_int iNumber){
    assert(iNumber>0);
    t_int iRemainder = iNumber % 16;
    if(iRemainder==0) return iNumber;
    return iNumber-iRemainder;
}*/
INLINE PURE_FUNCTION t_int rounddown_x(t_int iNumber, t_int iMultiple){
    if(DEBUG) if(iMultiple==0) return iNumber;
    t_int iRemainder = iNumber % iMultiple;
    if(iRemainder==0) return iNumber;
    return iNumber-iRemainder;
}
PURE_FUNCTION t_int round_up_to_multiple_of_small_primes(t_int n, t_int maxprime);


void wabor_t_setup(void);
void invwabor_t_setup(void);
void welay_t_setup(void);
void wenerator_t_setup(void);
void winary_t_setup(void);
void watom_t_setup(void);
void woutput_t_setup(void);

void TEST(void);
t_int wabor_test_compileparam(void);




typedef void* pointer_t;
INLINE void hide_pointer_at_address(pointer_t what, void *p){
    assert(what!=NULL);
    memcpy(p,&what,sizeof(void*));
}

INLINE void hide_int_at_address(t_int i, void *p){ //OLD
    memcpy(p,&i,sizeof(t_int));
}
INLINE pointer_t find_pointer_at_address(void *p){
    
    return *((pointer_t*)p);
}
INLINE t_int find_int_at_address(void *p){ //OLD
    return *((t_int*)p);
}

struct _sWabor_Coeff { //Array das die Wabor Koeffizienten enthaelt
    t_int start; //Zeitpunkt zu dem Frame startet (innerhalb des PD-Frames gezaehlt)
    tc_type val_r;   //Wert des Koeffizienten //muss uU komplex werden
    tc_type val_i;   //Wert des Koeffizienten //muss uU komplex werden
    t_int band;	    //Welches Frame 
    t_int numCoeff;  //Wieviel Koeffizienten da sind. Wird nur in 0-tem gespeichert
};
typedef struct _sWabor_Coeff sWabor_Coeff;

typedef  pac_type_real thetafunction(pac_type_real arg, pac_type_real* data);

struct _sWatom_Param { //Enthaelt alle Transformationsparameter

    //Constants C
    pac_type_real C_T_c; // T_c=T_h/Theta'_inf*C_T, aka WinlengthComputeFactor
    pac_type_real C_b; // iOversamplingFactor
    pac_type_real C_d; //dFactor
    pac_type_real C_q; //Extra Bands to compute 

    t_int C_SR; //iSampleRateFactor
    pac_type_real C_cut; //dCutRatio
    pac_type_real T_max; //wl_max 


    //Werte die gesetzt werden koennen mittels Temp Variablen
    enum _eWindowType eWindowType;
    enum _eThetaType eThetaType;	 	  
    
    thetafunction *Theta, *Theta_inv, *d_Theta_infimum; //Theta muss streng monoton wachsend sein, operiert mit echten Frequenzen   
    pac_type_real* thetadata; //contains the data for the thetafunctions
    char thetapath[WABOR_T_MAXPATHLENGTH];
    //thetadata[0]=dThetaF0
    //thetadata[1]=dThetaK
    //double dThetaF0, dThetaF0Temp;//F0 Wert fuer Theta
    //double dThetaK, dThetaKTemp; //K    
    
    double _K;//In welchem Bereich soll linearisiert werden 
    

    t_int iq_compute_sup; //Groesstes q das gebraucht wird
    t_int iComputeType; //Wenn iComputeType=1 werden hohe Frequenzen auf negative Frequenzen abgebildet.

    //Werte die berechnet werden
    pac_type_real _R; //R Ueberlappungsfaktor der Fenster in der Zeit, R*a = Winlength
    pac_type_real dWinlength_window; //Fensterlaenge in s
    pac_type_real dWinlength_compute; //Fensterlaenge die zum Berechnen verwendet wird
    pac_type_real _a, _aTemp; //Zeitverschiebung in s
    pac_type_real _b, _bTemp; //Frequenzverschiebung in 1/s
    pac_type_real dEssentialBandwidth;//ditto

    pac_type_real* aldDq_real; //Enthaelt alle Werte dq, Mach ich vorberechnet, weil ich es in perform Routine brauche
    pac_type_real* aldDq; //Enthaelt alle Werte dq, Mach ich vorberechnet, weil ich es in perform Routine brauche
    t_int*    aiDq; //Enthaelt die gleichen Werte wie adDq als t_int in Einheit Bins

    //Werte die von anderen Funktionen als initialize berechnet werden

    t_int iLongestFrame_paf; //Laengstes Frame 
    
    paf_type **paf_r;
    paf_type **paf_i;
};
typedef struct _sWatom_Param sWatom_Param;




INLINE PURE_FUNCTION pac_type csincPAC(pac_type x){
    //sin(x)/x
    if(cabsPAC(x)>5e-3){
        return csinPAC(x)/x;
    } else {
        pac_type y = x*x;
        y = 1 - y*( 1.0L/6.0L - 1.0L/120.0L*y );
        return y;
    }
}

INLINE PURE_FUNCTION pac_type_real sincPAC(pac_type_real x)   {return (pac_type_real) csincPAC((pac_type) x);}
INLINE PURE_FUNCTION pac_type_real sinc_nPAC(pac_type_real x) {return sincPAC(PI*x);}
INLINE PURE_FUNCTION pac_type csinc_nPAC(pac_type x)          {return csincPAC(PI*x);}


#endif //WABOR_T_DEFINITIONS_H
