/******************************** LICENSE ********************************

 Copyright 2007 European Centre for Medium-Range Weather Forecasts (ECMWF)

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at 

    http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 ******************************** LICENSE ********************************/

/*! \file Filter.cc
    Implementation of Filter class.
    
    Magics Team - ECMWF 2004
   
    Created: Wed 14-Jun-2004
    
*/

#include "Filter.h"
#include "MagLog.h"
#include "Timer.h"

using namespace magics;

template <class P>	
Filter<P>::Filter(MatrixHandler<P>& matrix, int nrows, int ncols) : matrix_(matrix), nrows_(nrows), ncols_(ncols)
{
	flr_  = 0;
	fbuf_ = NULL;

        //Cache input data
//	matrix.cache(matrix_);

}

template <class P>
Filter<P>::~Filter()
{
	FreeImageBuffers();
}

template <class P>
bool Filter<P>::AllocImageBuffers()
{ 
	int i;

	FreeImageBuffers();

	// Allocate space rotating buffer
	fbuf_ = new double*[nrows_];
	if( fbuf_ == NULL ) return false;
	int nb_columns = matrix_.columns();
	for (i = 0; i < nrows_; i++)
	{
		fbuf_[i] = NULL;
		fbuf_[i] = new double [nb_columns];
		if( fbuf_[i] == NULL ) return false;
	}

	return true;
}

template <class P>
void Filter<P>:: FreeImageBuffers()
{
	if( fbuf_ == NULL ) return;
	int i;
	for ( i = 0; i < nrows_; i++)
	{
		if( fbuf_[i] != NULL )
		{
			delete [] fbuf_[i];
			fbuf_[i] = NULL;
		}
	}

	delete [] fbuf_;
	fbuf_ = NULL;

	return;
}

template <class P>
bool Filter<P>::InitBuffer()
{
	int  lin, col;

	// Allocate space for output buffer
	if( AllocImageBuffers() == false ) return false;
	int nb_columns = matrix_.columns();
	// Fill buffer with first nrows_ input data
	for( lin = 0; lin < nrows_; lin++ )
	    for( col = 0; col < nb_columns; col++ )
		 fbuf_[lin][col] = matrix_(lin,col);

	flr_ = nrows_-1;

	return true;
}

template <class P>
bool Filter<P>::ScrollBuffer()
{
	int		ind,
			col;
	int             nr = nrows_-1;
	double	*aux;
	int nb_rows = matrix_.rows();
	int nb_columns = matrix_.columns();

	// verify if the line is within of the image
	flr_++;
 	if( flr_ <  nb_rows)
	{
		aux = fbuf_[0];
		for( ind = 0; ind < nrows_-1; ind++ ) fbuf_[ind] = fbuf_[ind+1];
		fbuf_[ nr ] = aux;
		for( col = 0; col < nb_columns; col++ )
			fbuf_[nr][col] = matrix_(flr_,col);
	}
	else  return false;

	return true;
}

template <class P>	
MinMaxFilter<P>::MinMaxFilter(MatrixHandler<P>& matrix, int nrows, int ncols, int flag) : Filter<P>(matrix,nrows,ncols), mflag_(flag)
{
}

template <class P>
MinMaxFilter<P>::~MinMaxFilter()
{
}

/*!
  \brief Method to find extrema
*/
template <class P>
bool MinMaxFilter<P>::Process()
{
        if ( mflag_ == 0 ) return false;
        double val;        //central point value
        bool   found;
        int    lin,col;
        int    i,j;

        int fcr = this->nrows_*.5;  //central line
        int fcc = this->ncols_*.5;  //central column

        const bool doMin = (mflag_ == 1 || mflag_ == 3) ? true : false;
        const bool doMax = (mflag_ == 2 || mflag_ == 3) ? true : false;

	// Initialize input buffer
	if( this->InitBuffer() == false )
	{
		MagLog::debug() <<" InitBuffer Error" << "\n";
		return false;
	}

	// Processing
	long hi=0;
	long lo=0;
        double nb_rows = this->matrix_.rows()    - this->nrows_;
	double nb_cols = this->matrix_.columns() - this->ncols_;

        for( lin = 0; lin <= nb_rows; lin++ )
	{
              for( col = 0; col <= nb_cols; col++ )
	      {
		    // check min value
		    if (doMin)
		    {
			val = this->fbuf_[fcr][col+fcc];
			found = true;
			for ( i = 0; i < this->nrows_; i++)
			{
			    for ( j = 0; j < this->ncols_; j++)
			    {
				    if ( val >= this->fbuf_[i][col+j] )
				    {
					    if( i!=fcr || j!=fcc )
					    {
						    found = false; //stop searching
						    break;
					    }
				    }
			    }
			    if (!found) break;
			}

			if (found)   //minimum found
			{
//				MagLog::debug() << "Min " << lin+fcr+1 << " " << col+fcc+1 << " " << val << "\n";
				lo = lo+1;
				push_back(P(this->matrix_.column(lin+fcr, col+fcc),this->matrix_.row(lin+fcr, col+fcc),val));
				this->back().low(true);
				this->back().high(false);
			}
		    }

		    // check max value
		    if (doMax)
		    {
			val = this->fbuf_[fcr][col+fcc];
			found = true;
			for ( i = 0; i < this->nrows_; i++)
			{
			    for ( j = 0; j < this->ncols_; j++)
			    {
				    if ( val <= this->fbuf_[i][col+j] )
				    {
					    if( i!=fcr || j!=fcc )
					    {
						    found = false; //stop searching
						    break;
					    }
				    }
			    }
			    if (!found) break;
			}

			if (found)   //minimum found
			{
//				MagLog::debug() << "Max " << lin+fcr+1 << " " << col+fcc+1 << " " << val << "\n";
				hi = hi+1;
				push_back(P(this->matrix_.column(lin+fcr, col+fcc),this->matrix_.row(lin+fcr, col+fcc),val));
				this->back().high(true);
				this->back().low(false);
			}
		    }
	        } // end column

	      if ( this->ScrollBuffer() == false ) break;
	}

        MagLog::debug() <<"TOTAL POINTS LOW = " << lo << "\n"
                     <<"TOTAL POINTS HI  = " << hi << "\n";
	return true;
}


//TEST, REMOVE LATER
template <class P>
void MinMaxFilter<P>::test_build_data()
{
      printf("\n\n TESTING   \n");
	double val;        //central point value
	bool     found;
	int	 lin,col;
	int	 i,j;

	int fcr = this->nrows_/2;  //central line
	int fcc = this->ncols_/2;  //central column

	bool doMin = (mflag_ == 1 || mflag_ == 3) ? true : false;
	bool doMax = (mflag_ == 2 || mflag_ == 3) ? true : false;

	// Initialize input buffer
	if( this->InitBuffer() == false )
	{
		MagLog::debug() <<" InitBuffer Error" << "\n";
		return;
	}

	long hi=0;
	long lo=0;
	// Processing
 	for( lin = 0; lin <= this->matrix_.rows()-this->nrows_; lin++ )
	{
		printf("\n");
		for ( i = 0; i < this->nrows_; i++)
		{
			for (j = 0; j < this->matrix_.columns(); j++) MagLog::debug() << this->fbuf_[i][j] << " ";
			printf("\n");
		}

	      for( col = 0; col <= this->matrix_.columns()-this->ncols_; col++ )
	      {
		    // check min value
		    if (doMin)
		    {
			val = this->fbuf_[fcr][col+fcc];
			found = true;
			for ( i = 0; i < this->nrows_; i++)
			{
			    for ( j = 0; j < this->ncols_; j++)
			    {
				    if ( val >= this->fbuf_[i][col+j] )
				    {
					    if( i!=fcr || j!=fcc )
					    {
						    found = false; //stop searching
						    break;
					    }
				    }
			    }
			    if (!found) break;
			}

			if (found)   //minimum found
			{
				MagLog::debug() << "\n" << "Min " << lin+fcr+1 << " " << col+fcc+1 << " " << val << "\n";
				lo = lo+1;
			}
		    }

		    // check max value
		    if (doMax)
		    {
			val = this->fbuf_[fcr][col+fcc];
			found = true;
			for ( i = 0; i < this->nrows_; i++)
			{
			    for ( j = 0; j < this->ncols_; j++)
			    {
				    if ( val <= this->fbuf_[i][col+j] )
				    {
					    if( i!=fcr || j!=fcc )
					    {
						    found = false; //stop searching
						    break;
					    }
				    }
			    }
			    if (!found) break;
			}

			if (found)   //minimum found
			{
				MagLog::debug() << "\n" << "Max " << lin+fcr+1 << " " << col+fcc+1 << " " << val << "\n";
				hi = hi+1;
			}
		    }
	        } // end column

	      if ( this->ScrollBuffer() == false ) break;
 	}

	MagLog::debug() <<"TOTAL POINTS LOW=" << lo << "\n";
	MagLog::debug() <<"TOTAL POINTS HI =" << hi << "\n";

	printf("\n\nEND TEST\n");
}
