/******************************************************************
 * DPUSER - The Next Generation
 *
 * File:     libfits/fits_dpl.cpp
 * Purpose:  Fits class methods to handle dead pixels
 * Author:   Thomas Ott
 *
 * Revision history
 * ================
 * 13.08.1999: file created
 * 24.08.1999: added method to DeadpixApply
 ******************************************************************/

#include <math.h>
#include <stdlib.h>

#include "fits.h"
#include "cube.h"

int Fits::dpixCreate(float threshold, int boxsize, int npass)
{
    long i, j;
    int k, c, rv;
    Fits b, d, e, dpl;

    if (naxis[0] == 3) {
        int x, y, z;
        float med, val;

        c = 0;
        dpl.create(naxis[1], naxis[2], naxis[3], I1);
        for (x = boxsize+1; x < naxis[1]-boxsize; x++) {
            for (y = boxsize+1; y < naxis[2]-boxsize; y++) {
                for (z = boxsize+1; z < naxis[3]-boxsize; z++) {
                    extractRange(b, x-boxsize, x+boxsize, y-boxsize, y+boxsize, z-boxsize, z+boxsize);
                    b.setValue(0.0, 2, 2, 2);
                    med = b.get_avg(0.0);

                    val = ValueAt(F_I(x, y, z));
                    if (fabs(val - med) > threshold) {
                        dpl.i1data[dpl.C_I(x, y, z)] = 1;
                        c++;
                    }
                }
            }
        }
        dp_output("Number of dead pixels: %i\n", c);
        rv = TRUE;
    } else {
        d.copy(*this);
        dpl.copy(*this);
        b.copy(*this);
        c = 0;
        rv = TRUE;
        if (rv) dpl = 0.0;

        // do npass passes
        dp_output("Doing %i passes...\n", npass);
        for (k = 0; k < npass; k++) {
            b.copy(d);
            if (rv)
                rv = Boxcar(b, boxsize, 3);
            if (rv) {
                e.copy(b);
                Boxcar(e, 1, 0);
                for (i = 1; i <= b.naxis[1]; i++) {
                    for (j = 1; j <= b.naxis[2]; j++) {
                        if (fabs(d.ValueAt(d.F_I(i, j)) - e.ValueAt(e.F_I(i, j))) > threshold) {
                            if (dpl.ValueAt(dpl.F_I(i, j)) == 0.0) {
                                dpl.setValue(1.0, i, j);
                                c++;
                            }
                        }
                    }
                }
                dp_output("Pass %i...number of dead pixels: %i\n", k + 1, c);
                b = dpl;
                rv = DeadpixApply(d, b, 0, 1);
            }
        }
    }
    *this = dpl;

    return(rv);
}

void Fits::dpl_apply(char *dname)
{
    int deadlist[20][NDEAD+1], imax, il, ip, i, k;
    float sum;
    FILE *fd;

    if (!setType(R4))
        return;
    fd = fopen(dname, "rb");
    fscanf(fd, "%6i", &imax);

    for (i = 1; i <= imax; i++) {
        fscanf(fd, "%4i %4i %1i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i",
        &deadlist[1][i], &deadlist[2][i], &deadlist[3][i], &deadlist[4][i], &deadlist[5][i],
        &deadlist[6][i], &deadlist[7][i], &deadlist[8][i], &deadlist[9][i], &deadlist[10][i],
        &deadlist[11][i], &deadlist[12][i], &deadlist[13][i], &deadlist[14][i], &deadlist[15][i],
        &deadlist[16][i], &deadlist[17][i], &deadlist[18][i], &deadlist[19][i]);
    }
    fclose(fd);

    for (il = 1; il <= imax; il++) {
        sum = 0.0;
        for (ip = 1; ip <= deadlist[3][il]; ip++) {
            i = deadlist[2+2*ip][il];
            k = deadlist[3+2*ip][il];
            sum += r4data[F_I(i, k)];
        }
        i = deadlist[1][il];
        k = deadlist[2][il];
        if (deadlist[3][il] > 0) r4data[F_I(i, k)] = sum / (float)(deadlist[3][il]);
    }
}

/*!
Correct for dead pixels

method can have the following values:
method == 0: boxcar average
method == 1: average along naxis1
method == 2: average along naxis2
method == 3: bezier interpolate in a cube
*/
int DeadpixApply(Fits & a, Fits & dpl, int method, int boxsize)
{
    int i, j, k, ii, jj, kk, c, rv, dc, n;
    float f;
    Fits d;

    dc = 0;
    d.copy(dpl);
    d.clip(0.0, 1.0);
    rv = TRUE;
    n = 0;

    if (method == 3) {
        Fits subcube, submask, dplsave;
        Dim dimensions;
        int numpix, numdpix;
        int counter = 0, ccounter = 1;
        int iii, jjj, kkk;

        dimensions.x = (boxsize * 2 + 1);
        dimensions.y = (boxsize * 2 + 1);
        dimensions.z = (boxsize * 2 + 1);
        subcube.create(boxsize * 2 + 1, boxsize * 2 + 1, boxsize * 2 + 1);
        submask.create(boxsize * 2 + 1, boxsize * 2 + 1, boxsize * 2 + 1, I2);
        numpix = (boxsize * 2 + 1) * (boxsize * 2 + 1) * (boxsize * 2 + 1);
        numdpix = (int)(d.get_flux());

        while (d.get_flux() > 0.0) {
            if (ccounter == 0) {
                dp_output("Bezier: %i out of %i dead pixels replaced\n", counter, numdpix);
                return rv;
            }
            dplsave.copy(d);
            ccounter = 0;
            n++;
            dp_output("Bezier interpolation: iteration #%i\n", n);
            for (i = 1; i <= d.Naxis(1); i++) {
                for (j = 1; j <= d.Naxis(2); j++) {
                    for (k = 1; k <= d.Naxis(3); k++) {
                        if (dplsave.ValueAt(dplsave.F_I(i, j, k)) > 0.0) {
                            for (ii = i - boxsize, iii = 1; ii <= i + boxsize; ii++, iii++) {
                                for (jj = j - boxsize, jjj = 1; jj <= j + boxsize; jj++, jjj++) {
                                    for (kk = k - boxsize, kkk = 1; kk <= k + boxsize; kk++, kkk++) {
                                        if ((ii > 0) && (ii <= a.Naxis(1)) && (jj > 0) && (jj <= a.Naxis(2)) && (kk > 0) && (kk <= a.Naxis(3))) {
                                            subcube.r4data[subcube.F_I(iii, jjj, kkk)] = a.ValueAt(a.F_I(ii, jj, kk));
                                            if ((dplsave.ValueAt(dplsave.F_I(ii, jj, kk)) > 0.0) || (subcube.r4data[subcube.F_I(iii, jjj, kkk)] == 0.0)) {
                                                submask.i2data[submask.F_I(iii, jjj, kkk)] = -1;
                                            } else {
                                                submask.i2data[submask.F_I(iii, jjj, kkk)] = 0;
                                            }
                                        } else {
                                            submask.i2data[submask.F_I(iii, jjj, kkk)] = -1;
                                        }
                                    }
                                }
                            }
                            submask.i2data[submask.C_I(boxsize, boxsize, boxsize)] = 1;
                            if (-submask.get_flux() < numpix / 2) {
                                a.setValue(interpol(subcube.r4data, submask.i2data, dimensions), i, j, k);
                                d.setValue(0.0, i, j, k);
                                counter++;
                                ccounter++;
                            }
                            if ( !(counter % 100 ) )
                                dp_output("%i\n", counter);
                        }
                    }
                }
            }
        }
        dp_output("%i out of %i dead pixels replaced\n", counter, (int)(d.get_flux()));
    } else if (rv) while (d.get_flux() > 0.0) {
        if (n > boxsize * 2)
            method = 0;
        if (n > 50)
            return rv;
        if (method == 0) {
            if (d.Naxis(0) == 2) {
                for (i = 1; i <= d.Naxis(1); i++) {
                    for (j = 1; j <= d.Naxis(2); j++) {
                        if (d.ValueAt(d.F_I(i, j)) != 0.0) {
                            c = 0;
                            f = 0.0;
                            dc++;
                            for (ii = i - boxsize; ii <= i + boxsize; ii++) {
                                if ((ii > 0) && (ii <= a.Naxis(1))) {
                                    for (jj = j - boxsize; jj <= j + boxsize; jj++) {
                                        if ((jj > 0) && (jj <= a.Naxis(2))) {
                                            if (d.ValueAt(d.F_I(ii, jj)) == 0.0) {
                                                f += a.ValueAt(a.F_I(ii, jj));
                                                c++;
                                            }
                                        }
                                    }
                                }
                            }
                            if (c > 0) {
                                a.setValue(f / (double)c, i, j);
                                d.setValue(0.0, i, j);
                            }
                        }
                    }
                }
            } else if (d.Naxis(0) == 3) {
                for (i = 1; i <= d.Naxis(1); i++) {
                    for (j = 1; j <= d.Naxis(2); j++) {
                        for (k = 1; k <= d.Naxis(3); k++) {
                            if (d.ValueAt(d.F_I(i, j, k)) != 0.0) {
                                c = 0;
                                f = 0.0;
                                dc++;
                                for (ii = i - boxsize; ii <= i + boxsize; ii++) {
                                    if ((ii > 0) && (ii <= a.Naxis(1))) {
                                        for (jj = j - boxsize; jj <= j + boxsize; jj++) {
                                            if ((jj > 0) && (jj <= a.Naxis(2))) {
                                                for (kk = k - boxsize; kk <= k + boxsize; kk++) {
                                                    if ((kk > 0) && (kk <= a.Naxis(3))) {
                                                        if (d.ValueAt(d.F_I(ii, jj, kk)) == 0.0) {
                                                            if (a.ValueAt(a.F_I(ii, jj, kk)) != 0.0) {
                                                                f += a.ValueAt(a.F_I(ii, jj, kk));
                                                                c++;
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                if (c > 0) {
                                    a.setValue(f / (double)c, i, j, k);
                                    d.setValue(0.0, i, j, k);
                                }
                            }
                        }
                    }
                }
            }
        } else if (method == 1) {
            for (i = 1; i <= d.Naxis(1); i++) {
                for (j = 1; j <= d.Naxis(2); j++) {
                    if (d.ValueAt(d.F_I(i, j)) != 0.0) {
                        c = 0;
                        f = 0.0;
                        dc++;
                        for (ii = i - boxsize; ii <= i + boxsize; ii++) {
                            if ((ii > 0) && (ii <= a.Naxis(1))) {
                                if (d.ValueAt(d.F_I(ii, j)) == 0.0) {
                                    f += a.ValueAt(a.F_I(ii, j));
                                    c++;
                                }
                            }
                        }
                        if (c > 0) {
                            a.setValue(f / (double)c, i, j);
                            d.setValue(0.0, i, j);
                        }
                    }
                }
            }
        } else if (method == 2) {
            for (i = 1; i <= d.Naxis(1); i++) {
                for (j = 1; j <= d.Naxis(2); j++) {
                    if (d.ValueAt(d.F_I(i, j)) != 0.0) {
                        c = 0;
                        f = 0.0;
                        dc++;
                        for (jj = j - boxsize; jj <= j + boxsize; jj++) {
                            if ((jj > 0) && (jj <= a.Naxis(2))) {
                                if (d.ValueAt(d.F_I(i, jj)) == 0.0) {
                                    f += a.ValueAt(a.F_I(i, jj));
                                    c++;
                                }
                        }
                        }
                        if (c > 0) {
                            a.setValue(f / (double)c, i, j);
                            d.setValue(0.0, i, j);
                        }
                    }
                }
            }
        }
        n++;
    }

    return(rv);
}

bool Fits::BezierInterpolate(Fits &result, int boxsize) {
    int i, j, k, ii, jj, kk, iii, jjj, kkk, counter = 0;
    Dim dimensions;
    Fits subcube, submask, a;

    dimensions.x = (boxsize * 2 + 1);
    dimensions.y = (boxsize * 2 + 1);
    dimensions.z = (boxsize * 2 + 1);
    if (!subcube.create(boxsize * 2 + 1, boxsize * 2 + 1, boxsize * 2 + 1))
        return FALSE;
    if (!submask.create(boxsize * 2 + 1, boxsize * 2 + 1, boxsize * 2 + 1, I2))
        return FALSE;
    if (!result.copy(*this))
        return FALSE;
    if (!result.setType(R4))
        return FALSE;

    for (i = 1; i <= Naxis(1); i++) {
        for (j = 1; j <= Naxis(2); j++) {
            for (k = 1; k <= Naxis(3); k++) {
                counter++;
                if (!(counter % 1000)) dp_output("%i\n", counter);
                for (ii = i - boxsize, iii = 1; ii <= i + boxsize; ii++, iii++) {
                    for (jj = j - boxsize, jjj = 1; jj <= j + boxsize; jj++, jjj++) {
                        for (kk = k - boxsize, kkk = 1; kk <= k + boxsize; kk++, kkk++) {
                            if ((ii > 0) && (ii <= Naxis(1)) && (jj > 0) && (jj <= Naxis(2)) && (kk > 0) && (kk <= Naxis(3))) {
                                subcube.r4data[subcube.F_I(iii, jjj, kkk)] = ValueAt(F_I(ii, jj, kk));
                                submask.i2data[submask.F_I(iii, jjj, kkk)] = 0;
                            } else {
                                submask.i2data[submask.F_I(iii, jjj, kkk)] = -1;
                            }
                        }
                    }
                }
                submask.i2data[submask.C_I(boxsize, boxsize, boxsize)] = 1;
                result.setValue(interpol(subcube.r4data, submask.i2data, dimensions), i, j, k);
            }
        }
    }

    return TRUE;
}

/*
void Fits::dpl_create(float fact, int speed, char *dname)
{
        float xmax, xmin, xmedian, xmederr, xmedflux, delta, checksum, check, comp, sum;
        int ii, iii, k, icount, nstep, ik, ikl, nflag1, nflag2, nflag3, irun, n;
        int ikk, ih, kkk;
        int i;
        float xikl, *vec1, *vec2, *vec3;
        int iik, ix=0, kx=0, ixh, kxh, icheck=0, ip, nv[9], nw[9], nbdead = 0;
        int deadlist[20][NDEAD+1];
        FILE *fd;
        char dummy[1000];

        if (!setType(R4)) return;
        vec1 = (float *)malloc((n_elements+1) * sizeof(float));
        vec2 = (float *)malloc((n_elements+1) * sizeof(float));
        vec3 = (float *)malloc((n_elements+1) * sizeof(float));
        xmax = get_max();
        xmin = get_min();

        for (n = nbdead + 1; n <= NDEAD; n++) {
                for (i = 1; i <= 19; i++) {
                        deadlist[i][n] = 0;
                }
        }

        icount = 0;
        if (xmax > 1e10) for (i = 0; i < (int)n_elements; i++)
                if (r4data[i] > 1e10) {
                        r4data[i] = 1e10;
                        icount++;
                }

        nstep = (int)((float)naxis[1] / sqrt((float)n_elements * speed / 100.0));
        dp_output("%i\n", nstep);

        dp_output("Calculating median values\n");
        iii = 0;
        ii = 0;
        for (i = 1; i < naxis[1]; i += nstep) {
                for (k = 1; k <= naxis[2]; k += nstep) {
                        ii++;
                        vec1[ii] = fabs(r4data[F_I(i, k)] - r4data[F_I(i+1, k)]);
                        iii++;
                        vec3[iii] = r4data[F_I(i, k)];
                }
        }
        xmax = xmin = vec1[1];
        for (i = 1; i <= ii; i++) {
                if (vec1[i] > xmax) xmax = vec1[i];
                if (vec1[i] < xmin) xmin = vec1[i];
        }

        for (i = 1; i <= ii; i++) {
                vec2[i] = vec1[i];
        }
        hpsort(ii, vec2);
        dp_output("%f %f %f\n", vec2[1], vec2[3], xmax);
        xmedian = vec2[(int)((ii - 1) / 2.0) + 1];
        dp_output("median of differences between adjacent pixels: %f\n", xmedian);

        for (i = 2; i <= ii; i++) vec1[i-1] = fabs(vec2[i] - xmedian);
        xmax = 0.0;
        for (i = 1; i <= ii; i++) {
                if (vec1[i] > xmax) xmax = vec1[i];
                if (vec1[i] < xmin) xmin = vec1[i];
        }
        for (i = 1; i <= ii; i++) vec2[i] = vec1[i];
        hpsort(ii, vec2);
        xmederr = vec2[(int)((ii - 1) / 2.0) + 1];
        dp_output("                  median error on differences: %f\n", xmederr);

        xmax = xmin = vec3[1];
        for (i = 1; i <= iii; i++) {
                if (vec3[i] > 40000.0) dp_output("%f\n", vec3[i]);
                if (vec3[i] > xmax) xmax = vec3[i];
                if (vec3[i] < xmin) xmin = vec3[i];
        }
        for (i = 1; i <= ii; i++) vec2[i] = vec3[i];
        hpsort(ii, vec2);
        xmedflux = vec2[(int)((iii - 1) / 2.0) + 1];
        dp_output("                         median flux in frame: %f\n", xmedflux);

        ik = 0;
        for (i = -1; i <= 1; i++) {
                for (k = -1; k <= 1; k++) {
                        if ((i != 0) || (k != 0)) {
                                ik++;
                                nv[ik] = i;
                                nw[ik] = k;
                        }
                }
        }

        ikl = nbdead + 1;
        nflag1 = 0;
        nflag2 = 0;
        nflag3 = 0;

        for (irun = 1; irun <= 2; irun++) {
                for (i = 1; i < naxis[1]; i++) {
                        for (k = 1; k < naxis[2]; k++) {
                                deadlist[3][ikl] = 0;
                                deadlist[1][ikl] = i;
                                deadlist[2][ikl] = k;
                                for (ip = 4; ip <= 19; ip++) {
                                        deadlist[ip][ikl] = 0;
                                }
                                delta = 0.0;
                                ikk = 0;
                                iik = -1;
                                for (ik = 1; ik <= 8; ik++) {
                                        ix = i + nv[ik];
                                        kx = k + nw[ik];
                                        if ((ix >= 1) && (ix <= naxis[1]) && (kx >= 1) && (kx <= naxis[2])) {
                                                checksum = 0.0;
                                                icheck = 0;
                                                iik++;
                                                for (ih = 1; ih <= 8; ih++) {
                                                        if (ih != ik) {
                                                                ixh = i + nv[ih];
                                                                kxh = k + nw[ih];
                                                                if ((ixh >= 1) && (ixh <= naxis[1]) && (kxh >= 1) && (kxh <= naxis[2])) {
                                                                        check = fabs(r4data[F_I(ix, kx)] - r4data[F_I(ixh, kxh)]) - xmedian;
                                                                        checksum += fabs(check);
                                                                        icheck++;
                                                                }
                                                        }
                                                }
                                                if (icheck > 0) checksum /= (float)icheck;
                                                icheck++;

                                                check = fabs(fabs(r4data[F_I(ix, kx)] - xmedflux) - xmedian);
                                                if ((checksum < fact*xmederr) || (check < fact*xmederr)) {
                                                        ikk++;
                                                        deadlist[3][ikl]++;
                                                        deadlist[3+2*ikk-1][ikl] = ix;
                                                        deadlist[4+2*ikk-1][ikl] = kx;
                                                        delta += fabs(r4data[F_I(i, k)] - r4data[F_I(ix, kx)]);
                                                }
                                        }
                                }

                                if ((iik == icheck) && (deadlist[3][ikl] == 0)) {
                                        nflag3++;
                                        deadlist[3][1] = 1;
                                        deadlist[4][1] = ix;
                                        deadlist[5][1] = kx;
                                        delta += fabs(r4data[F_I(i, k)] - r4data[F_I(ix, kx)]);
                                }

                                comp = (float)(deadlist[3][ikl]) * (xmedian + fact * xmederr);
                                if (delta > comp) {
                                        sum = 0.0;
                                        for (ip = 1; ip <= deadlist[3][ikl]; ip++) {
                                                iii = deadlist[2+2*ip][ikl];
                                                kkk = deadlist[3+2*ip][ikl];
                                                sum += r4data[F_I(iii, kkk)] / (float)(deadlist[3][ikl]);
                                        }
                                        iii = deadlist[1][ikl];
                                        kkk = deadlist[2][ikl];
                                        r4data[F_I(iii, kkk)] = sum;
                                        ikl++;
                                        if (ikl > NDEAD) {
                                                ikl = NDEAD;
                                                nflag2 = 1;
                                        }
                                }
                        }
                }
        }
        nbdead = ikl;
        dp_output("Number of dead pixels : %i\n", ikl);
        xikl = (float)ikl / (float)n_elements;
        if (xikl > 0.05) nflag1 = 1;

        if (nflag1 != 0) {
                dp_output("WARNING ---- you may be over-correcting!\n");
                dp_output("WARNING ---- algorithm finds more than 5 percent dead pixels\n");
                dp_output("WARNING ---- try different input parameters and\n");
                dp_output("WARNING ---- input data\n");
        }
        else if (nflag2 != 0) {
                dp_output("WARNING ---- number of dead pixels larger than allowed\n");
                dp_output("WARNING ---- by NDEAD in array_dim.h\n");
        }
        else if (nflag3 != 0) {
                dp_output("WARNING ---- no repare pixels found for %i pixels\n", nflag3);
                dp_output("WARNING ---- pixels have been set to a value of one\n");
                dp_output("WARNING ---- of their neighbouring pixels\n");
        }

        fd = fopen(dname, "w+b");
        sprintf(dummy, "%6i\n", ikl);
        fwrite(dummy, 1, strlen(dummy), fd);
        for (i = 1; i <= ikl; i++) {
                sprintf(dummy, "%4i %4i %1i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i\n",
                deadlist[1][i], deadlist[2][i], deadlist[3][i], deadlist[4][i], deadlist[5][i],
                deadlist[6][i], deadlist[7][i], deadlist[8][i], deadlist[9][i], deadlist[10][i],
                deadlist[11][i], deadlist[12][i], deadlist[13][i], deadlist[14][i], deadlist[15][i],
                deadlist[16][i], deadlist[17][i], deadlist[18][i], deadlist[19][i]);
                fwrite(dummy, 1, strlen(dummy), fd);
        }
        fclose(fd);
}
*/

/*
void Fits::dpl_map(int xsize, int ysize, char *dname)
{
        int deadlist[20][NDEAD+1];
        FILE *fd;
        ULONG i, imax;

        fd = fopen(dname, "rb");
        fscanf(fd, "%6li", &imax);
        dp_output("Number of dead pixels: %li\n", imax);
        for (i = 1; i <= imax; i++) {
                fscanf(fd, "%4i %4i %1i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i %4i",
                &deadlist[1][i], &deadlist[2][i], &deadlist[3][i], &deadlist[4][i], &deadlist[5][i],
                &deadlist[6][i], &deadlist[7][i], &deadlist[8][i], &deadlist[9][i], &deadlist[10][i],
                &deadlist[11][i], &deadlist[12][i], &deadlist[13][i], &deadlist[14][i], &deadlist[15][i],
                &deadlist[16][i], &deadlist[17][i], &deadlist[18][i], &deadlist[19][i]);
        }
        fclose(fd);

        if (!create(xsize, ysize)) return;

        for (i = 1; i <= imax; i++)
                r4data[F_I(deadlist[1][i], deadlist[2][i])] = (float)(deadlist[3][i]);

}
*/
