/*
 * Wrap the getQChi function so that it can be called from python
 */
static PyObject * DiffractionAnalysisWrap_getQChi(
        PyObject *self, PyObject *args) {

    double centerX, centerY,distance,energy,alpha,beta,rotation;
    double xPixel,yPixel;
    double pixelLength,pixelHeight;
    double cos_beta,sin_beta;
    double cos_alpha,sin_alpha;
    double cos_rotation,sin_rotation;

    double q,chi;

    cos_beta = sin_beta=-10;
    cos_alpha=sin_alpha = -10;
    cos_rotation=sin_rotation= -10;

    PyArg_ParseTuple(args,"ddddddddddd|dddddd",&centerX,
            &centerY,&distance,&energy,&alpha,&beta,&rotation,&xPixel,
            &yPixel,&pixelLength,&pixelHeight,&cos_beta,
            &sin_beta,&cos_alpha,&sin_alpha,&cos_rotation,&sin_rotation);

    if (cos_beta == -10) cos_beta  = cos(beta*PI/180.0);
    if (sin_beta == -10) sin_beta  = sin(beta*PI/180.0);
    if (cos_alpha == -10) cos_alpha = cos(alpha*PI/180.0);
    if (sin_alpha == -10) sin_alpha = sin(alpha*PI/180.0);
    if (cos_rotation == -10) cos_rotation = cos(rotation*PI/180.0);
    if (sin_rotation == -10) sin_rotation = sin(rotation*PI/180.0);

    getQChi(centerX,centerY,distance,energy,
        xPixel,yPixel,pixelLength,pixelHeight,rotation,
        cos_beta,sin_beta,cos_alpha,sin_alpha,
        cos_rotation,sin_rotation,&q,&chi);

    return Py_BuildValue("dd",q,chi);
}
void residual(double *p, double *qFit, int m, int n, void *data) {
    register int i;
    struct everything * useful;
    double q,chi;
    double rotation;
    double cos_beta,sin_beta;
    double cos_alpha,sin_alpha;
    double cos_rotation,sin_rotation;

    useful = (struct everything *)(data);

    // calculate sin & cos for later use.
    cos_alpha = cos(p[4]*PI/180.0);
    sin_alpha = sin(p[4]*PI/180.0);
    cos_beta  = cos(p[5]*PI/180.0);
    sin_beta  = sin(p[5]*PI/180.0);
    rotation = p[6];
    cos_rotation = cos(p[6]*PI/180.0);
    sin_rotation = sin(p[6]*PI/180.0); 

    for (i=0;i<n;i++) {
        getQChi(p[0],p[1],p[2],p[3],useful->x[i],useful->y[i],
            useful->pixelLength,useful->pixelHeight,rotation,cos_beta,
            sin_beta,cos_alpha,sin_alpha,cos_rotation,
            sin_rotation,&q,&chi);
        qFit[i] = q;
    }

}
double QxrdCenterFinder::getChi(double x, double y) const
{
  double q,chi;
  double beta = get_DetectorTilt()*M_PI/180.0;
  double rot  = get_TiltPlaneRotation()*M_PI/180.0;

  if (get_ImplementTilt()) {
    getQChi(get_CenterX(), get_CenterY(), get_DetectorDistance(),
            get_Energy(),
            x, y, get_DetectorXPixelSize(), get_DetectorYPixelSize(),
            rot, cos(beta), sin(beta), 1.0, 0.0, cos(rot), sin(rot),
            &q, &chi);
  } else {
    getQChi(get_CenterX(), get_CenterY(), get_DetectorDistance(),
            get_Energy(),
            x, y, get_DetectorXPixelSize(), get_DetectorYPixelSize(),
            0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0,
            &q, &chi);
  }

  return chi;
}
static PyObject * DiffractionAnalysisWrap_integrate(PyObject *self, PyObject *args) {
    PyArrayObject *diffractionData;
    PyArrayObject *values;
    PyArrayObject *integratedIntensity;

    int dimensions[1]; // the dimensions of the integrated intensity

    double centerX,centerY,distance,energy,alpha,beta,rotation;
    double lower,upper;
    int num;

    double constraintLower,constraintUpper;

    double pixelLength,pixelHeight;
    double cos_beta,sin_beta,cos_alpha,sin_alpha,cos_rotation,sin_rotation;
    unsigned int * total;
    int i; 
    double x,y; 
    double q,twoTheta,chi;
    int qBin,twoThetaBin,chiBin;
    double step;
    double intensity;
    char *type;
    char *constraintType;
    int doPolarizationCorrection;
    int doConstraint;
    double P;
    int verbose;

    int doGreaterThanMask;
    double greaterThanMask;
    int doLessThanMask;
    double lessThanMask;
   
    int doPolygonMask;
    PyArrayObject * polygonsX;
    PyArrayObject * polygonsY;
    PyArrayObject * polygonBeginningsIndex;
    PyArrayObject * polygonNumberOfItems;

    verbose = 0;

    // get the parameters out of the python call
    PyArg_ParseTuple(args,"O!dddddddddiddiidididiO!O!O!O!ddss",
            &PyArray_Type,&diffractionData,
            &centerX,&centerY,
            &distance,
            &energy,
            &alpha,&beta,&rotation,
            &lower,&upper,
            &num,
            &constraintLower,&constraintUpper,
            &doConstraint,
            &doPolarizationCorrection,&P,
            &doGreaterThanMask,&greaterThanMask,
            &doLessThanMask,&lessThanMask,
            &doPolygonMask,
            &PyArray_Type,&polygonsX,
            &PyArray_Type,&polygonsY,
            &PyArray_Type,&polygonBeginningsIndex,
            &PyArray_Type,&polygonNumberOfItems,
            &pixelLength,
            &pixelHeight,
            &type,
            &constraintType);

    // the types of integration
    // Remeber, strcmp returns 0 when they equal
    if (strcmp(type,"Q") != 0 && strcmp(type,"2theta") != 0 && strcmp(type,"chi") != 0) {
        PyErr_SetString( PyExc_Exception, "The type of integration must be Q, 2theta, or chi");
        return 0;
    }

    if (verbose) {
        printf("Doing Intensity Integration");
        printf("  The integration is over '%s'\n",type);
        printf("  Constrain the integration? %i\n",doConstraint);
        if (doConstraint) {
            printf("  The Type of constraint is '%s'\n",constraintType);
        }
    }

    if (doConstraint) {
        if (doConstraint && strcmp(type,"Q")==0 && strcmp(constraintType,"chi")!=0) {
            PyErr_SetString( PyExc_Exception, 
                "Cannot perform the desired integration. If the integration type is Q, the constrain integration type must be chi");
            return 0;
        }
        if (doConstraint && strcmp(type,"2theta")==0 && strcmp(constraintType,"chi")!=0) {
            PyErr_SetString( PyExc_Exception, 
                "Cannot perform the desired integration. If the integration type is 2theta, the constrain integration type must be chi");
            return 0;
        }
        if (doConstraint && strcmp(type,"chi")==0 && strcmp(constraintType,"Q")!=0 && strcmp(constraintType,"2theta")!=0) {
            PyErr_SetString( PyExc_Exception, 
                "Cannot perform the desired integration. If the integration type is chi, the constrain integration type must be either Q or 2theta");
            return 0;
        }
    }

    // passed array has to be 2 dimensional ints
    if (diffractionData->nd != 2 || diffractionData->descr->type_num != PyArray_INT) {
        PyErr_SetString(PyExc_Exception,"diffractionData must be two-dimensional and of type integer");
        return 0;
    }

    // create a new Numeric data structure to hold the integrated intensity
    dimensions[0]=num;
    integratedIntensity=(PyArrayObject *)PyArray_FromDims(1,dimensions,PyArray_DOUBLE);
    values = (PyArrayObject *)PyArray_FromDims(1,dimensions,PyArray_DOUBLE);


    // store totals also in a regular c data structure.
    total = malloc(num*sizeof(unsigned int) );
    if (total == NULL) {
        PyErr_SetString(PyExc_MemoryError,"No Memeory to create temporary data structure");
        return 0;
    }

    // initialize the data to 0
    for (i=0;i<num;i++) {
            total[i]=0;
            *(double *)(integratedIntensity->data + i*integratedIntensity->strides[0])=0;
    }

    // calculate sin & cos for later use.
    cos_beta  = cos(beta*PI/180.0);
    sin_beta  = sin(beta*PI/180.0);
    cos_alpha = cos(alpha*PI/180.0);
    sin_alpha = sin(alpha*PI/180.0);
    cos_rotation = cos(rotation*PI/180.0);
    sin_rotation = sin(rotation*PI/180.0); 

    // calulate the step size between different bins
    step = (upper-lower)*1.0/(num-1.0);

    if (verbose) {
        printf("  lower = %f, upper = %f\n",lower,upper);
        printf("  num = %d,  step = %f\n",num,step);
    }

    for (x=0.0;x<diffractionData->dimensions[0];x++) {
        for (y=0.0;y<diffractionData->dimensions[1];y++) {
            // treat each type of integration differently
            if (strcmp(type,"Q")==0) {
                // calculate the q chi cordiante for each pixel
                getQChi(centerX,centerY,distance,energy,x,y,pixelLength,
                        pixelHeight,rotation,cos_beta,sin_beta,cos_alpha,
                        sin_alpha,cos_rotation,sin_rotation,&q,&chi);

                // find the right bin to put it in
                qBin = (int)((q-lower)*1.0/step);

                // if the bin exists, put it into the bin
                if (qBin >=0 && qBin < num) {

                    // constraint must be of the chi value
                    // if chi is out of the constraint range, then ignore this value
                    if (doConstraint && (chi < constraintLower || chi > constraintUpper)) 
                        continue;

                    intensity = (double)*(int *)(diffractionData->data + 
                            ((int)x)*diffractionData->strides[0] + 
                            ((int)y)*diffractionData->strides[1]); 

                    if (doPolarizationCorrection)
                        intensity = polarizationCorrection(intensity,P,twoTheta,chi);

                    // if we are doing maskinging, make sure the intensity has the 
                    // right bounds and is not in a polygon mask. Otherwise, ignore 
                    // the current pixel
                    if ( (doGreaterThanMask && intensity > greaterThanMask) ||
                            (doLessThanMask && intensity < lessThanMask) || 
                            (doPolygonMask && isInPolygons(polygonsX,polygonsY,
                            polygonBeginningsIndex,polygonNumberOfItems,x,y)))
                        continue;

                    total[qBin]+=1;
                    *(double *)(integratedIntensity->data + 
                            qBin*integratedIntensity->strides[0]) += intensity;
                }
            } else if (strcmp(type,"2theta")==0) {
                getTwoThetaChi(centerX,centerY,distance,x,y,pixelLength,
                        pixelHeight,rotation,cos_beta,sin_beta,cos_alpha,
                        sin_alpha,cos_rotation,sin_rotation,&twoTheta,&chi);

                // find the right bin to put it in
                twoThetaBin = (int)((twoTheta-lower)*1.0/step);

                // if the bin exists, put it into the bin
                if (twoThetaBin >=0 && twoThetaBin < num) {

                    // constraint must be of the chi value
                    // if chi is out of the constraint range, then ignore this value
                    if (doConstraint && (chi < constraintLower || chi > constraintUpper))
                        continue;

                    intensity = (double)*(int *)(diffractionData->data + 
                            ((int)x)*diffractionData->strides[0] + 
                            ((int)y)*diffractionData->strides[1]); 

                    if (doPolarizationCorrection) 
                        intensity = polarizationCorrection(intensity,P,twoTheta,chi);

                    // if we are doing maskinging, make sure the intensity has the 
                    // right bounds and is not in a polygon mask. Otherwise, ignore 
                    // the current pixel
                    if ( (doGreaterThanMask && intensity > greaterThanMask) ||
                            (doLessThanMask && intensity < lessThanMask) || 
                            (doPolygonMask && isInPolygons(polygonsX,polygonsY,
                            polygonBeginningsIndex,polygonNumberOfItems,x,y)))
                        continue;

                    total[twoThetaBin]+=1;
                    *(double *)(integratedIntensity->data + 
                            twoThetaBin*integratedIntensity->strides[0]) += (double)intensity;

                }
            } else if (strcmp(type,"chi")==0) {
                getTwoThetaChi(centerX,centerY,distance,x,y,pixelLength,
                        pixelHeight,rotation,cos_beta,sin_beta,cos_alpha,
                        sin_alpha,cos_rotation,sin_rotation,&twoTheta,&chi);

                q = convertTwoThetaToQ(twoTheta,convertEnergyToWavelength(energy));

                // find the right bin to put it in
                chiBin = (int)((chi-lower)*1.0/step);

                // chi values can vary from -360 to 360. If our value is not in a bin, check
                // if 360-chi fall in a bin. Since the chi range is at most 360 degrees,
                // we will never have to bin something in two places.
                if (chiBin <0 || chiBin >= num) {
                    chi-=360;
                    chiBin = (int)((chi-lower)*1.0/step);
                }

                // if the bin exists, put it into the bin
                if (chiBin >=0 && chiBin < num) {

                    // if chi is out of the constraint range, then ignore this value
                    if (doConstraint && strcmp(constraintType,"Q")==0 && 
                            (q < constraintLower || q > constraintUpper))
                        continue;
                    if (doConstraint && strcmp(constraintType,"2theta")==0 && 
                            (twoTheta < constraintLower || twoTheta > constraintUpper))
                        continue;

                    intensity = (double)*(int *)(diffractionData->data + 
                            (int)x*diffractionData->strides[0] + 
                            ((int)y)*diffractionData->strides[1]); 

                    if (doPolarizationCorrection) 
                        intensity = polarizationCorrection(intensity,P,twoTheta,chi);

                    // if we are doing masking, make sure the intensity has the 
                    // right bounds and is not in a polygon mask. Otherwise, ignore 
                    // the current pixel
                    if ( (doGreaterThanMask && intensity > greaterThanMask) ||
                            (doLessThanMask && intensity < lessThanMask) || 
                            (doPolygonMask && isInPolygons(polygonsX,polygonsY,
                            polygonBeginningsIndex,polygonNumberOfItems,x,y)))
                        continue;

                    total[chiBin]+=1;
                    *(double *)(integratedIntensity->data + 
                            chiBin*integratedIntensity->strides[0]) += intensity;
                }
            }
        }
    }

    for (i=0;i<num;i++) {
        // set the values values
        *(double *)(values->data + i*values->strides[0]) = (lower + (i+0.5)*step);

        if (total[i]>=1) {
            *(double *)(integratedIntensity->data + i*integratedIntensity->strides[0]) /= total[i];

            // clip all intensity values to be at least 0.
            if (*(double *)(integratedIntensity->data + i*integratedIntensity->strides[0]) < 0)
                *(double *)(integratedIntensity->data + i*integratedIntensity->strides[0]) = 0;
                
        } else {
            // -1 means nothing was put into the bin
            *(double *)(integratedIntensity->data + i*integratedIntensity->strides[0]) = -1;
        }
    }

    // free the array we created so that we do not cause a memory leak
    free(total);

    // Why I have to do this is described http://mail.python.org/pipermail/python-list/2002-October/167549.html
    return Py_BuildValue("NN",values,integratedIntensity);
}
/* 
 * Finds one particular peak and returns the x,y cordinates of it 
 * by modifying peakX and peakY
 */
struct both * constantChiSlice(PyArrayObject * diffractionData,double xCenter,
        double yCenter,double distance,double energy,double pixelLength,
        double pixelHeight,double alpha,double beta,double rotation,double qLower,
        double qUpper, double chi,int * numValues) {

    double x1,y1;
    double x2,y2;
    double cos_beta,sin_beta,cos_alpha,sin_alpha,cos_rotation,sin_rotation;
    double m;
    int xSize,ySize;
    double xUpperFirst,yUpperFirst,xLowerFirst,yLowerFirst;
    double xUpperSecond,yUpperSecond,xLowerSecond,yLowerSecond;
    double xCurrent,yCurrent;
    int dataLow,dataHigh;
    int xLow,xHigh,yLow,yHigh;
    int counter;
    double qCurrent,chiCurrent;
    double dataInterpolate;

    struct both * vals;

    cos_beta  = cos(beta*PI/180.0);
    sin_beta  = sin(beta*PI/180.0);
    cos_alpha = cos(alpha*PI/180.0);
    sin_alpha = sin(alpha*PI/180.0);
    cos_rotation = cos(rotation*PI/180.0);
    sin_rotation = sin(rotation*PI/180.0);

    // find the beginning & ending pixel values for our Q,chi range.
    getXY(xCenter,yCenter,distance,energy,qLower,chi,
        pixelLength,pixelHeight,rotation,cos_beta,sin_beta,
        cos_alpha,sin_alpha,cos_rotation,sin_rotation,&x1,&y1);
   
    getXY(xCenter,yCenter,distance,energy,qUpper,chi,
        pixelLength,pixelHeight,rotation,cos_beta,sin_beta,
        cos_alpha,sin_alpha,cos_rotation,sin_rotation,&x2,&y2);

    xSize = diffractionData->dimensions[0];
    ySize = diffractionData->dimensions[0];
    
    // If the two points to find values between are off the image, 
    // Point the answer to nothing.
    if (ceil(x1)<0 || floor(x1)>xSize || ceil(y1)<0 || 
        floor(y1)>=ySize || ceil(x2)<0 || 
        floor(x2) >= xSize || ceil(y2)<0 || floor(y2)>=ySize) {
        return NULL;
    }

    // Make sure that xLower < xUpper for our first sweep
    if (x1>x2) {
        xUpperFirst=x1;yUpperFirst=y1;
        xLowerFirst=x2;yLowerFirst=y2;
    } else {
        xLowerFirst=x1;yLowerFirst=y1;
        xUpperFirst=x2;yUpperFirst=y2;
    }

    // Make sure that yLower < yUpperfor our second sweep
    if (y1 > y2) {
        xUpperSecond=x1;yUpperSecond=y1;
        xLowerSecond=x2;yLowerSecond=y2;
    } else {
        xLowerSecond=x1;yLowerSecond=y1;
        xUpperSecond=x2;yUpperSecond=y2;
    }

    // figure out all the data points to sample 
    *numValues = ( (int)floor(xUpperFirst)-(int)ceil(xLowerFirst) +1 );
    if (yUpperSecond != yLowerSecond)
        *numValues += ( (int)floor(yUpperSecond)-(int)ceil(yLowerSecond) +1 );

    vals=malloc( sizeof(struct both) );
    vals->first = malloc( (*numValues)*sizeof(double) );
    vals->second = malloc( (*numValues)*sizeof(double) );

    m=(yUpperFirst-yLowerFirst)*1.0/(xUpperFirst-xLowerFirst);

    counter=0;

    for(xCurrent=ceil(xLowerFirst);xCurrent<=floor(xUpperFirst);xCurrent++) {
        // get all points that lie on constant integer y values in the image
        yCurrent=yLowerFirst +m*(xCurrent-xLowerFirst);

        yHigh = (int)ceil(yCurrent);
        yLow = (int)floor(yCurrent);

        dataLow=*(int *)(diffractionData->data + ((int)xCurrent)*diffractionData->strides[0] + 
                yLow*diffractionData->strides[1]);
        dataHigh=*(int *)(diffractionData->data + ((int)xCurrent)*diffractionData->strides[0] + 
                yHigh*diffractionData->strides[1]);

        dataInterpolate = dataLow + (yCurrent-yLow)*(dataHigh-dataLow);

        getQChi(xCenter,yCenter,distance,energy,xCurrent,yCurrent,
                pixelLength,pixelHeight,rotation,cos_beta,sin_beta,cos_alpha,
                sin_alpha,cos_rotation,sin_rotation,&qCurrent,&chiCurrent);

        vals->first[counter] = qCurrent;
        vals->second[counter] = dataInterpolate;
        counter++;
    }

    if (yUpperSecond - yLowerSecond > .00001 ) {
        m=(xUpperSecond-xLowerSecond)*1.0/(yUpperSecond-yLowerSecond);

        for (yCurrent=(int)ceil(yLowerSecond);yCurrent<=(int)floor(yUpperSecond);yCurrent++) {
            // get all points that lie on constant integer x values in the image
            
            xCurrent = xLowerSecond + m*(yCurrent-yLowerSecond);

            xHigh=(int)ceil(xCurrent);
            xLow=(int)floor(xCurrent);

            dataLow=*(int *)(diffractionData->data + xLow*diffractionData->strides[0] + 
                    ((int)yCurrent)*diffractionData->strides[1]);
            dataHigh=*(int *)(diffractionData->data + xHigh*diffractionData->strides[0] + 
                    ((int)yCurrent)*diffractionData->strides[1]);

            dataInterpolate = dataLow + (xCurrent-xLow)*(dataHigh-dataLow);

            getQChi(xCenter,yCenter,distance,energy,xCurrent,yCurrent,
                    pixelLength,pixelHeight,rotation,cos_beta,sin_beta,cos_alpha,
                    sin_alpha,cos_rotation,sin_rotation,&qCurrent,&chiCurrent);

            vals->first[counter] = qCurrent;
            vals->second[counter] = dataInterpolate;
            counter++;
        }
    }

    // now, counter must equal *numValues. Return success
    return vals;
}