/* 
 * Copies real data to complex in the special way that it will be passed to the
 * transform with an offset of one! 
 */
static int
PyGSL_copy_real_to_complex(PyArrayObject *dst, PyArrayObject *src, enum pygsl_transform_mode mode)
{
	int i, n, n_check, n_2, modulo;
	void *srcv, *dstv;
	double *srcd=NULL, *dstd=NULL;			
	float  *srcf=NULL, *dstf=NULL;			

	FUNC_MESS_BEGIN();
	n = src->dimensions[0];
	n_check = dst->dimensions[0];
	
	assert(src);
	assert(dst);
	assert(src->descr->type_num == PyGSL_TRANSFORM_MODE_SWITCH(mode, PyArray_DOUBLE, PyArray_FLOAT));
	assert(dst->descr->type_num == PyGSL_TRANSFORM_MODE_SWITCH(mode, PyArray_CDOUBLE, PyArray_CFLOAT));
	for(i = 0; i < n; ++i){
		srcv   = (src->data + src->strides[0] *  (i));
		n_2 = (i+1)/2;
		modulo = (i+1)%2;
		if(n_2 >= n_check)
			GSL_ERROR("Complex array too small!", GSL_ESANITY);	
		dstv  = (dst->data + dst->strides[0] *  (n_2));
		if(mode == MODE_DOUBLE){
			srcd = (double *) srcv;
			dstd = (double *) dstv;
			dstd[modulo] = *srcd;
			DEBUG_MESS(5, "R -> C [%d] srcd %e\t dstd %e + %ej", i, *srcd, dstd[0], dstd[1]);
		}else if(mode == MODE_FLOAT){
			srcf = (float *) srcv;
			dstf = (float *) dstv;
			dstf[modulo] = *srcf;			
			DEBUG_MESS(5, "R -> C [%d] srcd %e\t dstd %e + %ej", i, *srcf, dstf[0], dstf[1]);
		}
	}
	FUNC_MESS_END();
	/* XXX Sometimes the last value must be set to zero ... */
	return GSL_SUCCESS;
	
}

/* Assumes special halfcomplex arrangement to be made and used ! */
static int
PyGSL_copy_halfcomplex_to_real(PyArrayObject *dst, PyArrayObject *src, double eps, 
			       enum pygsl_transform_mode mode)
{
	int n, n_check, i, n_2;
	double *srcd=NULL, *dstd=NULL;
	float *srcf=NULL, *dstf=NULL;

	FUNC_MESS_BEGIN();
	assert(src);
	assert(dst);
	assert(src->descr->type_num == PyGSL_TRANSFORM_MODE_SWITCH(mode, PyArray_CDOUBLE, PyArray_CFLOAT));
	assert(dst->descr->type_num == PyGSL_TRANSFORM_MODE_SWITCH(mode, PyArray_DOUBLE, PyArray_FLOAT));

	n_check = src->dimensions[0];
	n = dst->dimensions[0];

	/* The first element is a bit special */
	if(mode == MODE_DOUBLE){
		srcd = (double *)(src->data);	
		dstd = (double *)(dst->data);
	} else {
		srcf = (float *)(src->data);	
		dstf = (float *)(dst->data);
	}
	/* Should be zero ... */
	if(gsl_fcmp(PyGSL_TRANSFORM_MODE_SWITCH(mode, srcd[1], srcf[1]), 0,  eps) != 0)
		GSL_ERROR("The complex part of the nyquist freqency was not" 
			  "zero as it ought to be!", GSL_EINVAL);
	*dstd = *srcd;

	for(i = 1; i < n; ++i){
		n_2 = (i+1)/2;
		if(n_2 >= n_check){
			GSL_ERROR("Sizes of the complex array too small!", GSL_ESANITY);
		}
		if(mode == MODE_DOUBLE){
			srcd = (double *)(src->data + src->strides[0] * n_2);
			dstd = (double *)(dst->data + dst->strides[0] * i);
			*dstd = srcd[(i+1)%2];
			DEBUG_MESS(5, "C -> R [%d] srcd %e + %ej\t dstd %e", i, srcd[0], srcd[1], *dstd);
		} else {
			srcf = (float *)(src->data + src->strides[0] * n_2);
			dstf = (float *)(dst->data + dst->strides[0] * i);
			*dstf = srcf[(i+1)%2];
			DEBUG_MESS(5, "C -> R [%d] srcf %e + %ej\t dstf %e", i, srcf[0], srcf[1], *dstf);
		}
	}
	FUNC_MESS_END();
	return GSL_SUCCESS;
}

static int
PyGSL_copy_array_to_array(PyArrayObject *dst, PyArrayObject *src,  enum pygsl_transform_mode mode)
{
	int n, n_check, stride1, stride2, size, flag, iscomplex=0;
	gsl_vector_view d1, d2;
	gsl_vector_float_view f1, f2;
	gsl_vector_complex_view z1, z2;
	gsl_vector_complex_float_view c1, c2;
	const enum PyArray_TYPES array_type = src->descr->type_num;

	FUNC_MESS_BEGIN();
	assert(src);
	assert(dst);
	assert(src->descr->type_num == dst->descr->type_num);

	n = dst->dimensions[0];
	n_check = src->dimensions[0];
	if(n_check != n){
		GSL_ERROR("Sizes of the arrays did not match!", GSL_ESANITY);
	}
	
	size = PyGSL_TRANSFORM_MODE_SWITCH(mode, sizeof(double), sizeof(float));

	if(array_type == PyArray_CDOUBLE || array_type == PyArray_CFLOAT){
	     size *= 2; /* Complex are two basis numbers */
	     iscomplex=1;
	}else{
	     iscomplex=0;
	}

	if((flag = PyGSL_STRIDE_RECALC(src->strides[0], size, &stride1) != GSL_SUCCESS) || 
	   (flag = PyGSL_STRIDE_RECALC(dst->strides[0], size, &stride2) != GSL_SUCCESS))
		return flag;

	if(iscomplex){
	     switch(mode){
	     case MODE_DOUBLE:  		
		  z1 = gsl_vector_complex_view_array_with_stride((double *)dst->data, stride2, n);
		  z2 = gsl_vector_complex_view_array_with_stride((double *)src->data, stride1, n);
		  return gsl_blas_zcopy(&z2.vector, &z1.vector);
		  break;
	     case MODE_FLOAT:   
		  c1 = gsl_vector_complex_float_view_array_with_stride((float *)dst->data, stride2, n);
		  c2 = gsl_vector_complex_float_view_array_with_stride((float *)src->data, stride1, n);
		  return gsl_blas_zcopy(&z2.vector, &z1.vector);
		  break;
	     }
	}else{
	     switch(mode){
	     case MODE_DOUBLE:  		
		  d1 = gsl_vector_view_array_with_stride((double *)dst->data, stride2, n);
		  d2 = gsl_vector_view_array_with_stride((double *)src->data, stride1, n);
		  return gsl_blas_dcopy(&d2.vector, &d1.vector);
		  break;
	     case MODE_FLOAT:   
		  f1 = gsl_vector_float_view_array_with_stride((float *)dst->data, stride2, n);
		  f2 = gsl_vector_float_view_array_with_stride((float *)src->data, stride1, n);
		  return gsl_blas_scopy(&f2.vector, &f1.vector);
		  break;
	     }
	}
	FUNC_MESS_END();
	return GSL_SUCCESS;
}

/*
 * Only shift the last one. Assumes that it was passed to the GSL function with
 * an offset of one. Further assumes an contingous array.
 */
static int
PyGSL_fft_halfcomplex_unpack(PyArrayObject *a, int n_orig,  enum pygsl_transform_mode mode)
{
	double *d;
	float *f;
	FUNC_MESS_BEGIN();
	assert(a);
	assert(a->descr->type_num == PyGSL_TRANSFORM_MODE_SWITCH(mode, PyArray_CDOUBLE, PyArray_CFLOAT));
	if(mode == MODE_DOUBLE){
		d = (double *) a->data;
		d[0] = d[1];
		d[1] = 0.0;
		/* Set the last imaginary to zero for even length as it ought to be */
		if(n_orig%2)
			d[n_orig] = 0.0; 
	}else{
		f = (float *) a->data;
		f[0] = f[1];
		f[1] = 0.0;
		/* Set the last imaginary to zero for even length as it ought to be */
		if(n_orig%2)
			f[n_orig] = 0.0; 

	}
	FUNC_MESS_END();
	return GSL_SUCCESS;
}

/*
 * I only need to reorder the imaginary data?
 * I have to do it inplace thus I can not use the gsl function ...
 */
static PyObject *
_PyGSL_fft_halfcomplex_radix2_unpack(PyObject *self, PyObject *args, enum pygsl_transform_mode mode)
{

	PyObject *a_o=NULL;
	PyArrayObject *a=NULL, *r=NULL;
	int i, n, rn;       
	double *ddata, *dreal, *dimag;
        float *fdata, *freal, *fimag;
	void *adata, *rdata;

	FUNC_MESS_BEGIN();
	if(!PyArg_ParseTuple(args, "O",&a_o))
		return NULL;

	a = PyGSL_PyArray_PREPARE_gsl_vector_view(a_o, PyGSL_TRANSFORM_MODE_SWITCH(mode, PyArray_DOUBLE, PyArray_FLOAT), 
						  PyGSL_NON_CONTIGUOUS | PyGSL_INPUT_ARRAY, -1, 1, NULL);
	if(a == NULL)
		goto fail;
	n = a->dimensions[0];
	if(n%2 != 0){
		gsl_error("The length of the vector must be a multiple of two!",
			  __FILE__, __LINE__, GSL_EDOM);		goto fail;
	}
	rn = n / 2 + 1;
	if((r = (PyArrayObject *) PyGSL_New_Array(1, &rn, PyGSL_TRANSFORM_MODE_SWITCH(mode, PyArray_CDOUBLE, PyArray_CFLOAT))) == NULL)
		goto fail;
	assert(r->dimensions[0] == rn);


	/* first one special */
	rdata = r->data;
	adata = a->data;
	switch(mode){
	  case MODE_DOUBLE: 
		  ddata = (double *) rdata;
		  ddata[0] = ((double *) adata)[0];
		  ddata[1] = 0.0;
		  break;
	  case MODE_FLOAT: 
		  fdata = (float *) rdata;
		  fdata[0] = ((float *) adata)[0];
		  fdata[1] = 0.0;
		  break;
	}
	for(i = 1; i < rn - 1; ++i){
		assert(i>0 && i < n);
		switch(mode){
		case MODE_DOUBLE: 
			ddata    = (double *)(r->data + r->strides[0] * i);
			dreal    = (double *)(a->data + a->strides[0] * i);
			dimag    = (double *)(a->data + a->strides[0] * (n-i));
			ddata[0] = *dreal;
			ddata[1] = *dimag;
			break;
		case MODE_FLOAT: 
			fdata    = (float *)(r->data + r->strides[0] * i);
			freal    = (float *)(a->data + a->strides[0] * i);
			fimag    = (float *)(a->data + a->strides[0] * (n-i));
			fdata[0] = *freal;
			fdata[1] = *fimag;

			break;
		}
	}
	switch(mode){
	case MODE_DOUBLE: 
		ddata    =   (double *)(r->data + r->strides[0] * (rn-1));
		ddata[0] = *((double *)(a->data + a->strides[0] * (n/2)));
		ddata[1] = 0.0;
		break;
	case MODE_FLOAT: 
		fdata    =   (float *)(r->data + r->strides[0] * (rn-1));
		fdata[0] = *((float *)(a->data + a->strides[0] * (n/2)));
		fdata[1] = 0.0;
		break;
	}
	/* data[n_orig] = 0.0; */
	Py_DECREF(a); a = NULL;
        FUNC_MESS_END();
	return (PyObject *) r;
  fail:
	Py_XDECREF(a);
	Py_XDECREF(r);
	return NULL;
}

static PyObject *
PyGSL_fft_halfcomplex_radix2_unpack(PyObject *self, PyObject *args)
{
	PyObject *tmp;
	FUNC_MESS_BEGIN();
	tmp = _PyGSL_fft_halfcomplex_radix2_unpack(self, args, MODE_DOUBLE);
	FUNC_MESS_END();
	return tmp;
}

static PyObject *
PyGSL_fft_halfcomplex_radix2_unpack_float(PyObject *self, PyObject *args)
{
	PyObject *tmp;
	FUNC_MESS_BEGIN();
	tmp = _PyGSL_fft_halfcomplex_radix2_unpack(self, args, MODE_FLOAT);
	FUNC_MESS_END();
	return tmp;
}

/*
 * Copy an input array in an output array if necessary
 */
static PyArrayObject *
PyGSL_Shadow_array(PyObject *shadow, PyObject *master,  enum pygsl_transform_mode mode)
{
	PyArrayObject * ret = NULL, *s=NULL, *m=NULL;
	int line = -1;
	const int type1 = PyGSL_TRANSFORM_MODE_SWITCH(mode, PyArray_CDOUBLE, PyArray_CFLOAT);
	const int type2 = PyGSL_TRANSFORM_MODE_SWITCH(mode, PyArray_DOUBLE, PyArray_FLOAT);
	FUNC_MESS_BEGIN();
	/* Check if I got a return array */
	if(!PyArray_Check(master)){
		line = __LINE__ - 1;
		goto fail;
	}

	m = (PyArrayObject *) master;
	assert(m);
	assert(m->descr->type_num == type1 || m->descr->type_num == type2);

	if(shadow == NULL){
		FUNC_MESS("Generating an output array");
		ret =  (PyArrayObject *) PyGSL_Copy_Array(m);
		if(ret == NULL){
			line = __LINE__ -2;
			goto fail;
		}
	} else {		
		if (shadow ==  master) {
			Py_INCREF(shadow);
			ret = m;
		}else{	
			FUNC_MESS("Copying input to output array");
			/* Check if it is an array of the approbriate size */
			s = (PyArrayObject *) shadow;
			if((PyArray_Check((PyObject *) s)) && (s->nd == 1) 
			   && (s->descr->type_num == m->descr->type_num) 
			   && (s->dimensions[0] == m->dimensions[0])){
				Py_INCREF(s);
				ret = (PyArrayObject *) s;
			} else {
				gsl_error("The return array must be of approbriate size and type!", 
					  filename, __LINE__, GSL_EINVAL);
				line = __LINE__ -7;
				goto fail;
			}
			if(PyGSL_ERROR_FLAG(PyGSL_copy_array_to_array(s, m, mode)) != GSL_SUCCESS){
				line = __LINE__ -1;
				goto fail;			
			}
		}
	}
	FUNC_MESS_END();
	return ret;
  fail:
	FUNC_MESS("Fail");
	PyGSL_add_traceback(module, filename, __FUNCTION__, line);
	return NULL;
}

static int
PyGSL_guess_halfcomplex_length(PyArrayObject *a, int length, double eps,  enum pygsl_transform_mode mode)
{
	int n, call_n = -1;
	void *v;

	FUNC_MESS_BEGIN();

	assert(a);
	assert(a->descr->type_num == PyGSL_TRANSFORM_MODE_SWITCH(mode, PyArray_CDOUBLE, PyArray_CFLOAT));

	n = a->dimensions[0];
	if(length == -1){
		/* length was not given, try to guess */
		v = (a->data + a->strides[0] * (n - 1));
		if(gsl_fcmp(PyGSL_TRANSFORM_MODE_SWITCH(mode, ((double *)v)[1], ((float *)v)[1]), 0,  eps) == 0){
			call_n = n*2-2;
		}else{
			/* 
			 * The last element was close to zero, thus I assume
			 *  that I got  original data of even length.
			 */
			call_n = n*2-1;
		}			
	}else if(length < -1){
		gsl_error("The given length must be a positive number!",
			  __FILE__, __LINE__, GSL_EINVAL);
		return length;
	}else{
		call_n = length;
	}
	DEBUG_MESS(5, "Using a length of %d", call_n);
	FUNC_MESS_END();
	return call_n;

}

static int
PyGSL_Check_Array_Length(PyArrayObject *a, int call_n, int datamode, int n_type)
{
	int n_check = a->dimensions[0];

	if (PyGSL_DEBUG_LEVEL()> 1)
	{
	     int i;
	     DEBUG_MESS(4, "Array nd = %d", a->nd);
	     for(i = 0; i < a->nd; ++i){
		  DEBUG_MESS(5, "Array nd = %d", a->dimensions[i]);
	     }
	}

	DEBUG_MESS(3, "Call Length %d, Array Length %d n_type %d", call_n, n_check, n_type);
	if(call_n > n_check * n_type)
		GSL_ERROR("Array size was not big enough!", GSL_ESANITY);


	switch(datamode){
	case MODE_DOUBLE:		
		if(a->descr->type_num != PyArray_CDOUBLE &&  a->descr->type_num != PyArray_DOUBLE)
			GSL_ERROR("Type not of (C)DOUBLE!", GSL_ESANITY);
		break;
	case MODE_FLOAT:
		if(a->descr->type_num != PyArray_CFLOAT &&  a->descr->type_num != PyArray_FLOAT)
			GSL_ERROR("Type not of (C)FLOAT!", GSL_ESANITY);
		break;
		GSL_ERROR("Unknown mode!", GSL_ESANITY);
	}
	return GSL_SUCCESS;
}
