#' ADAMONT Nearest Neighbors computes the distance between reference data grid centroid and SF data grid
#'
#'@author Paola Marson, \email{paola.marson@meteo.fr} for PROSNOW version
#'@author Lauriane Batté, \email{lauriane.batte@meteo.fr} for CSTools adaptation
#'@description This function computes the nearest neighbor for each reference data (lon, lat) point in the experiment dataset by computing the distance between the reference dataset grid and the experiment data. This is the first step in the ADAMONT method adapted from Verfaillie et al. (2018).
#'
#'@param method a string among three options ('ADA': standard ADAMONT distance, 'simple': lon/lat straight euclidian distance, 'radius': distance on the sphere)
#'@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment longitudes in \code{$lon} and latitudes in \code{$lat}
#'@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the reference data on a different grid, with longitudes in \code{$lon} and latitudes in \code{$lat}.
#'
#'@return NN
#'
#'@import s2dverification
#'@import ncdf4

NearestNeighbors <- function (exp, obs, method='ADA') {
   
   if (!inherits(exp, 's2dv_cube') || !inherits(obs, 's2dv_cube')) {
     stop("Inputs 'exp' and 'obs' must be of class 's2dv_cube', ",
          "as output by CSTools::CST_Load.")
   }
   exp_lon <- exp$lon
   exp_lat <- exp$lat
   obs_lon <- obs$lon
   obs_lat <- obs$lat
   dim_exp_lon <- dim(exp_lon)
   dim_exp_lat <- dim(exp_lat)
   dim_obs_lon <- dim(obs_lon)
   dim_obs_lat <- dim(obs_lat)

   # Flatten longitudes and latitudes in case of 2-D longitudes and latitudes (Lambert grids, etc.)
   if ((length(dim_exp_lon)==2) & (length(dim_exp_lat)==2)){
      dim(exp_lon) <- c(dim_exp_lon[1]*dim_exp_lon[2])
      dim(exp_lat) <- c(dim_exp_lat[1]*dim_exp_lat[2])
   }	
   if ((length(dim_obs_lon)==2) & (length(dim_obs_lat)==2)){
      dim(obs_lon) <- c(dim_obs_lon[1]*dim_obs_lon[2])
      dim(obs_lat) <- c(dim_obs_lat[1]*dim_obs_lat[2])
   }
 
   # Now lat and lon arrays have 1 dimension, length npt (= nlat*nlon)

   OBS_grid <- cbind(obs_lon,obs_lat)
   EXP_grid <- cbind(exp_lon,exp_lat)

   dist_min<-min_lon<-min_lat<-imin_lon<-imin_lat<-array(dim=nrow(OBS_grid))
   if (method == 'ADA'){
	C<-cos(OBS_grid[,2]*pi/180)^2
	for (i in 1:nrow(OBS_grid)){
		dist<-(OBS_grid[i,2]-EXP_grid[,2])^2+C[i]*(OBS_grid[i,1]-EXP_grid[,1])^2
		dist_min[i]<-min(dist)
		min_lon[i]<-EXP_grid[which.min(dist),1]
		min_lat[i]<-EXP_grid[which.min(dist),2]
		imin_lon[i]<-which(exp_lon==min_lon[i])
		imin_lat[i]<-which(exp_lat==min_lat[i])
	}
   } else if (method == 'simple'){
	for (i in 1:nrow(OBS_grid)){
		dist<-(OBS_grid[i,2]-EXP_grid[,2])^2+(OBS_grid[i,1]-EXP_grid[,1])^2
		dist_min[i]<-min(dist)
		min_lon[i]<-EXP_grid[which.min(dist),1]
		min_lat[i]<-EXP_grid[which.min(dist),2]
		imin_lon[i]<-which(exp_lon==min_lon[i]) 
		imin_lat[i]<-which(exp_lat==min_lat[i])
	}
   } else if (method == 'radius'){
	R <- 6371e3 # metres, Earth radius
	EXP_gridr<-EXP_grid*pi/180
	OBS_gridr<-OBS_grid*pi/180
	for (i in 1:nrow(OBS_grid)){
		a<-sin((OBS_gridr[i,2]-EXP_gridr[,2])/2)^2 + cos(OBS_gridr[i,2])*cos(EXP_gridr[,2])*sin((OBS_gridr[i,1]-EXP_gridr[,1])/2)^2
		c<-2*atan2(sqrt(a),sqrt(1-a))
		dist<-R*c
		dist_min[i]<-min(dist)
		min_lon[i]<-EXP_grid[which.min(dist),1]
		min_lat[i]<-EXP_grid[which.min(dist),2]
		imin_lon[i]<-which(exp_lon==min_lon[i])
		imin_lat[i]<-which(exp_lat==min_lat[i])
	}
   } else {
   	stop("Adamont_NearestNeighbors supports method = 'ADA', 'simple' or 'radius' only.")
   }

   # Reshape outputs to original grid
   dim(min_lon)=dim_obs_lon
   dim(min_lat)=dim_obs_lat
   dim(imin_lon)=dim_obs_lon
   dim(imin_lat)=dim_obs_lat

   NN=list(dist_min=dist_min, min_lon=min_lon, min_lat=min_lat, imin_lon=imin_lon, imin_lat=imin_lat)

   return(NN)
}

