/*
    Copyright (c) 2010, Luis Alvarez <lalvarez@dis.ulpgc.es>
	All rights reserved.  
	
    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 3 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, see <http://www.gnu.org/licenses/>.
*/

/**
 * \page code_utilities.cpp \link utilities.cpp \endlink
 * \brief \ref code_utilities.h "See utilities.h"
 * \brief Code of  \ref utilities.cpp "utilities.cpp"
 * \includelineno utilities.cpp
 */

/**
 * \file utilities.cpp
 * \brief Basic image utilities procedures
 * \author Luis Alvarez \n \n
 * \brief \b Code: \n
 * - \ref code_utilities.cpp "utilities.cpp" \n
 * - \ref code_utilities.h "utilities.h"
 */

#include "utilities.h"

std::vector < std::vector <ami::point2d<int> > > ami::utilities::construct_neighborhood(const int radius)
{
  if( radius==0) return(std::vector< std::vector <ami::point2d<int> > >());
  float sq2=sqrt((float) 2.);
  float sq3=sq2+1e-4;
  int size=(int) (sq2*radius);
  std::vector< std::vector< ami::point2d<int> > > neigh_vector(size);

  for(int k=1;k<=radius;++k){
    int k2=(int) (sq3*sq2*k)-1;
    if(k2>=size) break;
    neigh_vector[k2].push_back(ami::point2d< int>(k,k));
    neigh_vector[k2].push_back(ami::point2d< int>(-k,-k));
    neigh_vector[k2].push_back(ami::point2d< int>(k,-k));
    neigh_vector[k2].push_back(ami::point2d< int>(-k,k));
  }
  for(int k=1;k<=radius;++k){
    int k2=(int) (sq2*k)-1;
    neigh_vector[k2].push_back(ami::point2d< int>(k,0));
    neigh_vector[k2].push_back(ami::point2d< int>(-k,0));
    neigh_vector[k2].push_back(ami::point2d< int>(0,k));
    neigh_vector[k2].push_back(ami::point2d< int>(0,-k));
  }
  for(int k=1;k<=radius;++k){
    for(int m=k+1;m<=radius;m++){
      int k2=(int) (sq3*sqrt((float) k*k+m*m))-1;
      if(k2>=size) {m=radius+1; continue;}
      neigh_vector[k2].push_back(ami::point2d< int>(k,m));
      neigh_vector[k2].push_back(ami::point2d< int>(-k,m));
      neigh_vector[k2].push_back(ami::point2d< int>(k,-m));
      neigh_vector[k2].push_back(ami::point2d< int>(-k,-m));
      neigh_vector[k2].push_back(ami::point2d< int>(m,k));
      neigh_vector[k2].push_back(ami::point2d< int>(m,-k));
      neigh_vector[k2].push_back(ami::point2d< int>(-m,k));
      neigh_vector[k2].push_back(ami::point2d< int>(-m,-k));
    }
  }

  return(neigh_vector);
}



/**
 * \fn std::vector < std::vector <int> > ami::utilities::boundary_neighborhood_9n(const int width, const int height)
 * \brief function to compute the 9 neighborhood index of the image boundary
 * \author Luis Alvarez
 * \return b[k][l] where for a boundary point k : b[k][0]=m (image index point), b[k][1]=m-width,
  b[k][2]=m+width, b[k][3]=m+1, b[k][4]=m-1, b[k][5]=m-width+1, b[k][6]=m+width+1, b[k][7]=m-width-1,
  b[k][8]=m+width-1 (if point is outside the image we take the nearest one inside the image)
*/

std::vector < std::vector <int> > ami::utilities::boundary_neighborhood_9n(const int width, const int height)
{
  if( width<3 || height<3) return(std::vector< std::vector <int> >());
  int size=2*(width+height)-4;
  std::vector< std::vector <int> > neigh_vector(size, std::vector<int>(9));

  int m,cont=0;

  // FIRST CORNER
  neigh_vector[cont][0]=0; // pixel position index
  neigh_vector[cont][1]=0; // North pixel position index
  neigh_vector[cont][2]=width; // South pixel position index
  neigh_vector[cont][3]=1; // Est pixel position index
  neigh_vector[cont][4]=0; // West pixel position index
  neigh_vector[cont][5]=1; // North-Est pixel position index
  neigh_vector[cont][6]=width+1; // South-Est pixel position index
  neigh_vector[cont][7]=0; // North-West pixel position index
  neigh_vector[cont][8]=0; // South-West pixel position index
  cont++;

  // BOUNDARY HORIZONTAL LINE
  for(int k=1,k_end=width-1;k!=k_end;++k,++cont){
    neigh_vector[cont][0]=k; // pixel position index
    neigh_vector[cont][1]=k; // North pixel position index
    neigh_vector[cont][2]=k+width; // South pixel position index
    neigh_vector[cont][3]=k+1; // Est pixel position index
    neigh_vector[cont][4]=k-1; // West pixel position index
    neigh_vector[cont][5]=k+1; // North-Est pixel position index
    neigh_vector[cont][6]=k+width+1; // South-Est pixel position index
    neigh_vector[cont][7]=k-1; // North-West pixel position index
    neigh_vector[cont][8]=k+width-1; // South-West pixel position index
  }

  // SECOND CORNER
  m=width-1;
  neigh_vector[cont][0]=m; // pixel position index
  neigh_vector[cont][1]=m; // North pixel position index
  neigh_vector[cont][2]=m+width; // South pixel position index
  neigh_vector[cont][3]=m; // Est pixel position index
  neigh_vector[cont][4]=m-1; // West pixel position index
  neigh_vector[cont][5]=m; // North-Est pixel position index
  neigh_vector[cont][6]=m+width; // South-Est pixel position index
  neigh_vector[cont][7]=m-1; // North-West pixel position index
  neigh_vector[cont][8]=m+width-1; // South-West pixel position index
  cont++;

  // BOUNDARY VERTICAL LINE
  for(int k=1,k_end=height-1;k!=k_end;++k,++cont){
    m=(k+1)*width-1;
    neigh_vector[cont][0]=m; // pixel position index
    neigh_vector[cont][1]=m-width; // North pixel position index
    neigh_vector[cont][2]=m+width; // South pixel position index
    neigh_vector[cont][3]=m; // Est pixel position index
    neigh_vector[cont][4]=m-1; // West pixel position index
    neigh_vector[cont][5]=m-width; // North-Est pixel position index
    neigh_vector[cont][6]=m+width; // South-Est pixel position index
    neigh_vector[cont][7]=m-width-1; // North-West pixel position index
    neigh_vector[cont][8]=m+width-1; // South-West pixel position index

  }

  // CORNER POINT
  m=height*width-1;
  neigh_vector[cont][0]=m; // pixel position index
  neigh_vector[cont][1]=m-width; // North pixel position index
  neigh_vector[cont][2]=m; // South pixel position index
  neigh_vector[cont][3]=m; // Est pixel position index
  neigh_vector[cont][4]=m-1; // West pixel position index
  neigh_vector[cont][5]=m-width; // North-Est pixel position index
  neigh_vector[cont][6]=m; // South-Est pixel position index
  neigh_vector[cont][7]=m-width-1; // North-West pixel position index
  neigh_vector[cont][8]=m-1; // South-West pixel position index
  cont++;

  // HORIZONTAL LINE
  for(int k=2,k_end=width;k!=k_end;++k,++cont){
    m=height*width-k;
    neigh_vector[cont][0]=m; // pixel position index
    neigh_vector[cont][1]=m-width; // North pixel position index
    neigh_vector[cont][2]=m; // South pixel position index
    neigh_vector[cont][3]=m+1; // Est pixel position index
    neigh_vector[cont][4]=m-1; // West pixel position index
    neigh_vector[cont][5]=m-width+1; // North-Est pixel position index
    neigh_vector[cont][6]=m+1; // South-Est pixel position index
    neigh_vector[cont][7]=m-width-1; // North-West pixel position index
    neigh_vector[cont][8]=m-1; // South-West pixel position index
  }

  // CORNER POINT
  m=(height-1)*width;
  neigh_vector[cont][0]=m; // pixel position index
  neigh_vector[cont][1]=m-width; // North pixel position index
  neigh_vector[cont][2]=m; // South pixel position index
  neigh_vector[cont][3]=m+1; // Est pixel position index
  neigh_vector[cont][4]=m; // West pixel position index
  neigh_vector[cont][5]=m-width+1; // North-Est pixel position index
  neigh_vector[cont][6]=m+1; // South-Est pixel position index
  neigh_vector[cont][7]=m-width; // North-West pixel position index
  neigh_vector[cont][8]=m; // South-West pixel position index
  cont++;

  // VERTICAL LINE
  for(int k=2,k_end=height;k!=k_end;++k,++cont){
    m=(height-k)*width;
    neigh_vector[cont][0]=m; // pixel position index
    neigh_vector[cont][1]=m-width; // North pixel position index
    neigh_vector[cont][2]=m+width; // South pixel position index
    neigh_vector[cont][3]=m+1; // Est pixel position index
    neigh_vector[cont][4]=m; // West pixel position index
    neigh_vector[cont][5]=m-width+1; // North-Est pixel position index
    neigh_vector[cont][6]=m+width+1; // South-Est pixel position index
    neigh_vector[cont][7]=m-width; // North-West pixel position index
    neigh_vector[cont][8]=m+width; // South-West pixel position index
  }

  return(neigh_vector);
}

/**
 * \fn std::vector < std::vector <int> > ami::utilities::boundary_neighborhood_5n(const int width, const int height)
 * \brief function to compute the 5 neighborhood index of the image boundary
 * \author Luis Alvarez
 * \return b[k][l] where for a boundary point k : b[k][0]=m (image index point), b[k][1]=m-width,
  b[k][2]=m+width, b[k][3]=m+1, b[k][4]=m-1, (if point is outside the image we take the nearest
  one inside the image)
*/
std::vector < std::vector <int> > ami::utilities::boundary_neighborhood_5n(const int width, const int height)
{
  if( width<3 || height<3) return(std::vector< std::vector <int> >());
  int size=2*(width+height)-4;
  std::vector< std::vector <int> > neigh_vector(size, std::vector<int>(5));

  int m,cont=0;

  // FIRST CORNER
  neigh_vector[cont][0]=0; // pixel position index
  neigh_vector[cont][1]=0; // North pixel position index
  neigh_vector[cont][2]=width; // South pixel position index
  neigh_vector[cont][3]=1; // Est pixel position index
  neigh_vector[cont][4]=0; // West pixel position index
  cont++;

  // BOUNDARY HORIZONTAL LINE
  for(int k=1,k_end=width-1;k!=k_end;++k,++cont){
    neigh_vector[cont][0]=k; // pixel position index
    neigh_vector[cont][1]=k; // North pixel position index
    neigh_vector[cont][2]=k+width; // South pixel position index
    neigh_vector[cont][3]=k+1; // Est pixel position index
    neigh_vector[cont][4]=k-1; // West pixel position index
  }

  // SECOND CORNER
  neigh_vector[cont][0]=width-1; // pixel position index
  neigh_vector[cont][1]=width-1; // North pixel position index
  neigh_vector[cont][2]=2*width-1; // South pixel position index
  neigh_vector[cont][3]=width-1; // Est pixel position index
  neigh_vector[cont][4]=width-2; // West pixel position index
  cont++;

  // BOUNDARY VERTICAL LINE
  for(int k=1,k_end=height-1;k!=k_end;++k,++cont){
    m=(k+1)*width-1;
    neigh_vector[cont][0]=m; // pixel position index
    neigh_vector[cont][1]=m-width; // North pixel position index
    neigh_vector[cont][2]=m+width; // South pixel position index
    neigh_vector[cont][3]=m; // Est pixel position index
    neigh_vector[cont][4]=m-1; // West pixel position index
  }

  // CORNER POINT
  m=height*width-1;
  neigh_vector[cont][0]=m; // pixel position index
  neigh_vector[cont][1]=m-width; // North pixel position index
  neigh_vector[cont][2]=m; // South pixel position index
  neigh_vector[cont][3]=m; // Est pixel position index
  neigh_vector[cont][4]=m-1; // West pixel position index
  cont++;

  // HORIZONTAL LINE
  for(int k=2,k_end=width;k!=k_end;++k,++cont){
    m=height*width-k;
    neigh_vector[cont][0]=m; // pixel position index
    neigh_vector[cont][1]=m-width; // North pixel position index
    neigh_vector[cont][2]=m; // South pixel position index
    neigh_vector[cont][3]=m+1; // Est pixel position index
    neigh_vector[cont][4]=m-1; // West pixel position index
  }

  // CORNER POINT
  m=(height-1)*width;
  neigh_vector[cont][0]=m; // pixel position index
  neigh_vector[cont][1]=m-width; // North pixel position index
  neigh_vector[cont][2]=m; // South pixel position index
  neigh_vector[cont][3]=m+1; // Est pixel position index
  neigh_vector[cont][4]=m; // West pixel position index
  cont++;

  // VERTICAL LINE
  for(int k=2,k_end=height;k!=k_end;++k,++cont){
    m=(height-k)*width;
    neigh_vector[cont][0]=m; // pixel position index
    neigh_vector[cont][1]=m-width; // North pixel position index
    neigh_vector[cont][2]=m+width; // South pixel position index
    neigh_vector[cont][3]=m+1; // Est pixel position index
    neigh_vector[cont][4]=m; // West pixel position index
  }

  return(neigh_vector);
}

/**
 * \fn std::vector <int>  ami::utilities::boundary_neighborhood_1n(const int width, const int height)
 * \brief function to compute the neighborhood index of the image boundary
 * \author Luis Alvarez
 * \return b[k] where for a boundary point k : b[k]=m (image index point)
*/
std::vector <int>  ami::utilities::boundary_neighborhood_1n(const int width, const int height)
{
  if( width<3 || height<3) return(std::vector <int>());

  int size=2*(width+height)-4;
  std::vector <int>  neigh_vector(size);

  int m,cont=0;

  // FIRST CORNER
  neigh_vector[cont]=0; // pixel position index
  cont++;

  // BOUNDARY HORIZONTAL LINE
  for(int k=1,k_end=width-1;k!=k_end;++k,++cont){
    neigh_vector[cont]=k; // pixel position index
  }

  // SECOND CORNER
  neigh_vector[cont]=width-1; // pixel position index
  cont++;

  // BOUNDARY VERTICAL LINE
  for(int k=1,k_end=height-1;k!=k_end;++k,++cont){
    m=(k+1)*width-1;
    neigh_vector[cont]=m; // pixel position index
  }

  // CORNER POINT
  m=height*width-1;
  neigh_vector[cont]=m; // pixel position index
  cont++;

  // HORIZONTAL LINE
  for(int k=2,k_end=width;k!=k_end;++k,++cont){
    m=height*width-k;
    neigh_vector[cont]=m; // pixel position index
  }

  // CORNER POINT
  m=(height-1)*width;
  neigh_vector[cont]=m; // pixel position index
  cont++;

  // VERTICAL LINE
  for(int k=2,k_end=height;k!=k_end;++k,++cont){
    m=(height-k)*width;
    neigh_vector[cont]=m; // pixel position index
  }

  //std::cout << "size= " << size << "  cont= " << cont << std::endl; //system("pause");

  return(neigh_vector);
}
