/*===========================================================================
  Copyright (C) 2001 European Southern Observatory (ESO)

  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.

  Corresponding concerning ESO-MIDAS should be addressed as follows:
    Internet e-mail: midas@eso.org
    Postal address: European Southern Observatory
            Data Management Division 
            Karl-Schwarzschild-Strasse 2
            D 85748 Garching bei Muenchen 
            GERMANY
===========================================================================*/
/* Program  : medianfilterframe.c                                          */
/* Author   : G. Mulas  -  ITAL_FLAMES Consortium                          */
/* Date     :                                                              */
/*                                                                         */
/* Purpose  : Missing                                                      */
/*                                                                         */
/*                                                                         */
/* Input:  see interface                                                   */ 
/*                                                                      */
/* Output:                                                              */
/*                                                                         */
/* DRS Functions called:                                                   */
/* none                                                                    */ 
/*                                                                         */ 
/* Pseudocode:                                                             */
/* Missing                                                                 */ 
/*                                                                         */ 
/* Version  :                                                              */
/* Last modification date: 2002/08/05                                      */
/* Who     When        Why                Where                            */
/* AMo     02-08-05   Add header         header                            */
/*-------------------------------------------------------------------------*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif


#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <flames_midas_def.h>
#include <flames_medianfilterframe.h>
#include <flames_uves.h>
#include <flames_newmatrix.h>

static int 
fdcomp(const void *pixel1, const void *pixel2);

flames_err medianfilterframe(flames_frame *myframe, int32_t xhalfwindow, 
                             int32_t yhalfwindow, int32_t maxiters,
                             double kappa2)
{

    frame_mask **badpixeltemp=0;
    frame_mask **pixtocheck=0;
    frame_data *pixbuf=0;
    frame_data **thres2array=0;
    int32_t badpixnum=0;
    int32_t maxpixnum=0;
    int32_t iters=0;
    int32_t iy=0;
    int32_t iylow=0;
    int32_t iyhigh=0;
    int32_t iy2=0;
    int32_t ix=0;
    int32_t ixlow=0;
    int32_t ixhigh=0;
    int32_t ix2=0;
    int32_t pixnum=0;
    int32_t medindex=0;







    frame_data *fdvecbuf1=0;

    frame_data *fdvecbuf3=0;



    int32_t totpixuplimit=0;
    int32_t iyixoffset=0;
    int32_t iyixindex=0;
    int32_t iy2ix2offset=0;
    int32_t iy2ix2index=0;


    maxpixnum = (1+2*yhalfwindow)*(1+2*xhalfwindow)-1;
    totpixuplimit = (myframe->subrows*myframe->subcols)-1;

    if (maxpixnum>0) {
        double kappa2bis = kappa2/10;
        badpixeltemp = fmmatrix(0, myframe->subrows-1, 0, myframe->subcols-1);
        pixtocheck = fmmatrix(0, myframe->subrows-1, 0, myframe->subcols-1);
        thres2array = fdmatrix(0, myframe->subrows-1, 0, myframe->subcols-1);

        pixbuf = fdvector(0,maxpixnum);
        badpixnum = 1;
        frame_mask* fmvecbuf1 = myframe->badpixel[0];
        frame_mask* fmvecbuf2 = pixtocheck[0];
        frame_mask* fmvecbuf3 = badpixeltemp[0];
        fdvecbuf1 = myframe->frame_array[0];
        frame_data* fdvecbuf2 = myframe->frame_sigma[0];
        fdvecbuf3 = thres2array[0];

        /* copy the old badpixel mask onto the temporary one
       (a memcpy would be faster) */
        memcpy(fmvecbuf2, fmvecbuf1,
               (size_t) (myframe->subrows*myframe->subcols*sizeof(frame_mask)));
        memcpy(fmvecbuf3, fmvecbuf1,
               (size_t) (myframe->subrows*myframe->subcols*sizeof(frame_mask)));
        for (ix=0; ix<=totpixuplimit; ix++) {
            fdvecbuf3[ix] = kappa2bis*fdvecbuf1[ix]*fdvecbuf1[ix];
        }

        for (iters=1; badpixnum>0 && iters<=maxiters; iters++) {
            badpixnum = 0;
            for (iy=0; iy<=(myframe->subrows-1); iy++) {
                iyixoffset = iy*myframe->subcols;
                if ((iylow=iy-yhalfwindow)<0) iylow=0;
                if ((iyhigh=iy+yhalfwindow)>(myframe->subrows-1))
                    iyhigh=myframe->subrows-1;
                for (ix=0; ix<=(myframe->subcols-1); ix++) {
                    iyixindex = iyixoffset+ix;
                    /* only try to filter supposedly good pixels AND we did not check
         it already in the same conditions */
                    if ((fmvecbuf1[iyixindex]==0)&&(fmvecbuf2[iyixindex]==0)) {
                        if ((ixlow=ix-xhalfwindow)<0) ixlow=0;
                        if ((ixhigh=ix+xhalfwindow)>(myframe->subcols-1))
                            ixhigh=myframe->subcols-1;
                        pixnum=0;
                        for (iy2=iylow; iy2<=iyhigh; iy2++) {
                            iy2ix2offset = iy2*myframe->subcols;
                            for (ix2=ixlow; ix2<=ixhigh; ix2++) {
                                iy2ix2index = iy2ix2offset+ix2;
                                if (fmvecbuf1[iy2ix2index]==0) {
                                    pixbuf[pixnum]=fdvecbuf1[iy2ix2index];
                                    pixnum++;
                                }
                            }
                        }
                        /* obviously, there is no point in finding the median and
           filtering if there are no other good pixels around this 
           one */
                        if (pixnum>1) {
                        	frame_data medpixel=0;
                            /* sort the pixels */
                            qsort(pixbuf, (size_t) pixnum, sizeof(frame_data), fdcomp);
                            /* now that the pixels are sorted, pick the median */
                            if (2*(pixnum/2) != pixnum) {
                                /* pixnum is odd, just pick the right index */
                                medindex = (pixnum-1)/2;
                                medpixel = pixbuf[medindex];
                            }
                            else {
                                /* pixnum is even, compute the average of the two
           median values */
                                medindex = pixnum/2;
                                medpixel = (pixbuf[medindex-1]+pixbuf[medindex])/2;
                            }
                            frame_data plaindiff = medpixel-fdvecbuf1[iyixindex];
                            frame_data squarediff = plaindiff*plaindiff;
                            frame_data pixelthres = kappa2*fdvecbuf2[iyixindex];
                            frame_data pixelthres2 = kappa2bis*medpixel*medpixel;
                            if (pixelthres2>fdvecbuf3[iyixindex])
                                pixelthres2 = fdvecbuf3[iyixindex];
                            if (pixelthres<pixelthres2) pixelthres = pixelthres2;
                            if (squarediff > pixelthres) {
                                /* mark this pixel as bad in the new badpixel mask */
                                fmvecbuf3[iyixindex] = 1;
                                badpixnum++;
                                /* we must check again the neighbouring pixels */
                                for (iy2=iylow; iy2<=iyhigh; iy2++) {
                                    iy2ix2offset = iy2*myframe->subcols;
                                    for (ix2=ixlow; ix2<=ixhigh; ix2++) {
                                        iy2ix2index = iy2ix2offset+ix2;
                                        fmvecbuf2[iy2ix2index]=0;
                                    }
                                }
                            }
                            else {
                                /* avoid checking this pixel over and over again */
                                fmvecbuf2[iyixindex]=1;
                            }
                        }
                    }
                }
            }
            /* copy the new badpixel mask onto the old one
     (a memcpy would be faster) */
            memcpy(fmvecbuf1, fmvecbuf3,
                   (size_t) (myframe->subrows*myframe->subcols*sizeof(frame_mask)));
        }
        free_fdvector(pixbuf, 0, maxpixnum);
        free_fmmatrix(badpixeltemp, 0, myframe->subrows-1, 0, myframe->subcols-1);
        free_fmmatrix(pixtocheck, 0, myframe->subrows-1, 0, myframe->subcols-1);
        free_fdmatrix(thres2array, 0, myframe->subrows-1, 0, myframe->subcols-1);
    }

    return NOERR;

}

static int 
fdcomp(const void *p1, const void *p2)
{
    const frame_data * pixel1=(const frame_data * ) p1;
    const frame_data * pixel2=(const frame_data * ) p2;

    if (*pixel1 > *pixel2) return -1;
    else if (*pixel1 < *pixel2) return 1;
    else return 0;
}
