/*===========================================================================
  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 medpixel=0;
  frame_data plaindiff=0;
  frame_data squarediff=0;
  frame_data pixelthres=0;
  frame_data pixelthres2=0;
  double kappa2bis=0;

  frame_data *fdvecbuf1=0;
  frame_data *fdvecbuf2=0;
  frame_data *fdvecbuf3=0;
  frame_mask *fmvecbuf1=0;
  frame_mask *fmvecbuf2=0;
  frame_mask *fmvecbuf3=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) {
    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;
    fmvecbuf1 = myframe->badpixel[0];
    fmvecbuf2 = pixtocheck[0];
    fmvecbuf3 = badpixeltemp[0];
    fdvecbuf1 = myframe->frame_array[0];
    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) {
          /* 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;
          }
          plaindiff = medpixel-fdvecbuf1[iyixindex];
          squarediff = plaindiff*plaindiff;
          pixelthres = kappa2*fdvecbuf2[iyixindex];
          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;
}
