#include "cluster_reader.h"
#include <assert.h>

size_t read_clusters(FILE *fp, size_t n_clusters, Cluster *buf,
                     uint32_t *n_left) {
    int32_t iframe = 0;     // frame number needs to be 4 bytes!
    uint32_t nph = *n_left; // number of clusters in frame needs to be 4 bytes!

    size_t nph_read = 0;
    uint32_t nn = *n_left;

    // if there are photons left from previous frame read them first
    if (nph) {
        if (nph > n_clusters) {
            // if we have more photons left in the frame then photons to read we
            // read directly the requested number
            nn = n_clusters;
        } else {
            nn = nph;
        }
        nph_read += fread((void *)(buf + nph_read), sizeof(Cluster), nn, fp);
        *n_left = nph - nn; // write back the number of photons left
    }
    if (nph_read < n_clusters) {
        // keep on reading frames and photons until reaching n_clusters
        while (fread(&iframe, sizeof(iframe), 1, fp)) {
            // read number of clusters in frame
            if (fread(&nph, sizeof(nph), 1, fp)) {
                if (nph > (n_clusters - nph_read))
                    nn = n_clusters - nph_read;
                else
                    nn = nph;

                nph_read +=
                    fread((void *)(buf + nph_read), sizeof(Cluster), nn, fp);
                *n_left = nph - nn;
            }
            if (nph_read >= n_clusters)
                break;
        }
    }
    assert(nph_read <= n_clusters); // sanity check in debug mode
    return nph_read;
}

size_t read_clusters_with_cut(FILE *fp, size_t n_clusters, Cluster *buf,
                              uint32_t *n_left, double *noise_map, int nx,
                              int ny) {
    int iframe = 0;
    uint32_t nph = *n_left;

    uint32_t nn = *n_left;

    size_t nph_read = 0;

    int32_t t2max, tot1;
    int32_t tot3;
    Cluster *ptr = buf;
    int good = 1;
    double noise;
    // read photons left from previous frame
    // if (noise_map) 
    //  printf("Using moise map\n");

    if (nph) {
        if (nph > n_clusters) {
            // if we have more photons left in the frame then photons to read we
            // read directly the requested number
            nn = n_clusters;
        } else {
            nn = nph;
        }
        for (size_t iph = 0; iph < nn; iph++) {
            // read photons 1 by 1
            size_t n_read = fread((void *)(ptr), sizeof(Cluster), 1, fp);
            if (n_read != 1) {
                return nph_read;
            }
            // TODO! error handling on read
            good = 1;
            if (noise_map) {
                if (ptr->x >= 0 && ptr->x < nx && ptr->y >= 0 && ptr->y < ny) {
                    tot1 = ptr->data[4];
                    analyze_cluster(*ptr, &t2max, &tot3, NULL, NULL, NULL, NULL,
                                    NULL);
                    noise = noise_map[ptr->y * nx + ptr->x];
                    if (tot1 > noise || t2max > 2 * noise || tot3 > 3 * noise) {
                        ;
                    } else {
                        good = 0;
			//printf("%d %d %f %d %d %d\n",ptr->x, ptr->y, noise, tot1, t2max, tot3);
		    }
                } else {
                    printf("Bad pixel number %d %d\n", ptr->x, ptr->y);
                    good = 0;
                }
            }
            if (good) {
                ptr++;
		nph_read++;
            }
	    (*n_left)--;
            if (nph_read >= n_clusters)
                break;
        }
    }
    if (nph_read < n_clusters) {
        // keep on reading frames and photons until reaching n_clusters
        while (fread(&iframe, sizeof(iframe), 1, fp)) {
            // printf("%d\n",nph_read);

            if (fread(&nph, sizeof(nph), 1, fp)) {
                // printf("** %d\n",nph);
                *n_left = nph;
                for (size_t iph = 0; iph < nph; iph++) {
                    // read photons 1 by 1
                    size_t n_read =
                        fread((void *)(ptr), sizeof(Cluster), 1, fp);
                    if (n_read != 1) {
                        return nph_read;
                    }
                    good = 1;
                    if (noise_map) {
                        if (ptr->x >= 0 && ptr->x < nx && ptr->y >= 0 &&
                            ptr->y < ny) {
                            tot1 = ptr->data[4];
                            analyze_cluster(*ptr, &t2max, &tot3, NULL, NULL,
                                            NULL, NULL, NULL);
                            //noise = noise_map[ptr->y * nx + ptr->x];
                            noise = noise_map[ptr->y + ny * ptr->x];
			    if (tot1 > noise || t2max > 2 * noise ||
                                tot3 > 3 * noise) {
                                ;
                            } else
                                good = 0;
                        } else {
                            printf("Bad pixel number %d %d\n", ptr->x, ptr->y);
                            good = 0;
                        }
                    }
                    if (good) {
                        ptr++;
			nph_read++;
                    }
		    (*n_left)--;
                    if (nph_read >= n_clusters)
                        break;
                }
            }
            if (nph_read >= n_clusters)
                break;
        }
    }
    // printf("%d\n",nph_read);
    assert(nph_read <= n_clusters); // sanity check in debug mode
    return nph_read;
}

int analyze_clusters(int64_t n_clusters, int32_t *cin, ClusterAnalysis *co,
                     int csize) {
    char quad;
    int32_t tot;
    double etax, etay;
    int nc = 0;
    // printf("csize is %d\n",csize);
    int ret;
    for (int ic = 0; ic < n_clusters; ic++) {
        switch (csize) {
        case 2:
            ret = analyze_data((cin + 9 * ic), &tot, NULL, &quad, &etax, &etay,
                               NULL, NULL);
            break;
        default:
            ret = analyze_data((cin + 9 * ic), NULL, &tot, &quad, NULL, NULL,
                               &etax, &etay);
        }
        if (ret == 0) {
            printf("%d %d %d %f %f\n", ic, tot, quad, etax, etay);
        }
        nc += ret;
        // printf("%d %d %d %d\n", ic , quad , t2max , tot3);
        (co + ic)->c = quad;
        (co + ic)->tot = tot;
        (co + ic)->etax = etax;
        (co + ic)->etay = etay;
        // printf("%g %g\n",etax, etay);
        /* if (tot<=0) */
        /* printf("%d %d %d %d %d %d\n",ic,(cin+ic)->x, (cin+ic)->y, */
        /*   (cout+ic)->c, (cout+ic)->tot2, (cout+ic)->tot3); */
    }
    return nc;
}

int analyze_cluster(Cluster cl, int32_t *t2, int32_t *t3, char *quad,
                    double *eta2x, double *eta2y, double *eta3x,
                    double *eta3y) {

    return analyze_data(cl.data, t2, t3, quad, eta2x, eta2y, eta3x, eta3y);
}

int analyze_data(int32_t *data, int32_t *t2, int32_t *t3, char *quad,
                 double *eta2x, double *eta2y, double *eta3x, double *eta3y) {

    int ok = 1;

    int32_t tot2[4];
    int32_t t2max = 0;
    char c = 0;
    int32_t val, tot3;

    tot3 = 0;
    for (int i = 0; i < 4; i++)
        tot2[i] = 0;

    for (int ix = 0; ix < 3; ix++) {
        for (int iy = 0; iy < 3; iy++) {
            val = data[iy * 3 + ix];
            //	printf ("%d ",data[iy * 3 + ix]);
            tot3 += val;
            if (ix <= 1 && iy <= 1)
                tot2[cBottomLeft] += val;
            if (ix >= 1 && iy <= 1)
                tot2[cBottomRight] += val;
            if (ix <= 1 && iy >= 1)
                tot2[cTopLeft] += val;
            if (ix >= 1 && iy >= 1)
                tot2[cTopRight] += val;
        }
        //	printf ("\n");
    }
    // printf ("\n");

    if (t2 || quad) {

        t2max = tot2[0];
        c = cBottomLeft;
        for (int i = 1; i < 4; i++) {
            if (tot2[i] > t2max) {
                t2max = tot2[i];
                c = i;
            }
        }
	//printf("*** %d %d %d %d -- %d\n",tot2[0],tot2[1],tot2[2],tot2[3],t2max);
        if (quad)
            *quad = c;
        if (t2)
            *t2 = t2max;
    }
    if (t3)
        *t3 = tot3;

    if (eta2x || eta2y) {
        if (eta2x)
            *eta2x = 0;
        if (eta2y)
            *eta2y = 0;
        switch (c) {
        case cBottomLeft:
            if (eta2x && (data[3] + data[4]) != 0)
                *eta2x = (double)(data[4]) / (data[3] + data[4]);
            if (eta2y && (data[1] + data[4]) != 0)
                *eta2y = (double)(data[4]) / (data[1] + data[4]);
            break;
        case cBottomRight:
            if (eta2x && (data[2] + data[5]) != 0)
                *eta2x = (double)(data[5]) / (data[4] + data[5]);
            if (eta2y && (data[1] + data[4]) != 0)
                *eta2y = (double)(data[4]) / (data[1] + data[4]);
            break;
        case cTopLeft:
            if (eta2x && (data[7] + data[4]) != 0)
                *eta2x = (double)(data[4]) / (data[3] + data[4]);
            if (eta2y && (data[7] + data[4]) != 0)
                *eta2y = (double)(data[7]) / (data[7] + data[4]);
            break;
        case cTopRight:
            if (eta2x && t2max != 0)
                *eta2x = (double)(data[5]) / (data[5] + data[4]);
            if (eta2y && t2max != 0)
                *eta2y = (double)(data[7]) / (data[7] + data[4]);
            break;
        default:;
        }
    }

    if (eta3x || eta3y) {
        if (eta3x && (data[3] + data[4] + data[5]) != 0)
            *eta3x = (double)(-data[3] + data[3 + 2]) /
                     (data[3] + data[4] + data[5]);
        if (eta3y && (data[1] + data[4] + data[7]) != 0)
            *eta3y = (double)(-data[1] + data[2 * 3 + 1]) /
                     (data[1] + data[4] + data[7]);
    }

    return ok;
}
