#' @rdname WeatherRegimes
#' @title Function for Calculating the Cluster analysis
#'
#' @author Verónica Torralba - BSC, \email{veronica.torralba@bsc.es}
#'
#' @description This function computes the weather regimes from a cluster analysis. 
#'It can be applied over the dataset with dimensions 
#'c(year/month, month/day, lon, lat), or by using PCs obtained from the application of the 
#'EOFs analysis to filter the dataset.  
#'The cluster analysis can be performed with the traditional k-means or those methods 
#'included in the hclust (stats package).
#'
#'@references Cortesi, N., V., Torralba, N., González-Reviriego, A., Soret, and F.J., Doblas-Reyes (2019). 
#' Characterization of European wind speed variability using weather regimes. Climate Dynamics,53, 
#' 4961–4976, doi:10.1007/s00382-019-04839-5. 
#'@references Torralba, V. (2019) Seasonal climate prediction for the wind energy sector: methods and tools 
#' for the development of a climate service. Thesis. Available online: \url{https://eprints.ucm.es/56841/}
#'
#'@param data Array of data (anomalies) with dimensions c(year/month, month/day, lon, lat).
#'@param EOFs Whether to compute the EOFs (default = 'TRUE') or not (FALSE) over data.
#'@param threshold Value with the percentage of variance to be explained by the PCs.
#' Only sufficient PCs to explain this much variance will be used in the clustering.
#'@param lon Vector of longitudes.
#'@param lat Vector of latitudes.
#'@param ncenters Number of clusters to be calculated with the clustering function.
#'@param method Different options to estimate the clusters. The most traditional approach is the k-means analysis (default=’kmeans’) 
#'but the function also support the different methods included in the hclust . These methods are:  
#'"ward.D", "ward.D2", "single", "complete", "average" (= UPGMA), "mcquitty" (= WPGMA), "median" (= WPGMC) or "centroid" (= UPGMC). 
#' For more details about these methods see the hclust function documentation included in the stats package.
#'@param nstarts Parameter for the cluster analysis determining how many random sets to choose (Only if method='kmeans' is selected).
#'@param iter.max Parameter to select the maximum number of iterations allowed (Only if method='kmeans' is selected).
#'@param ncores The number of multicore threads to use for parallel computation}. 
#'@return A list with elements \code{$composite} (3-d array (lon, lat, k) containing the composites k=1,..,K for case (*1)
# or only k=1 for any specific cluster, i.e., case (*2)),
#'         \code{pvalue} (a matrix of EOF patterns obtained by regression for each variable),
#'         \code{cluster} (A time series of integers (from 1:k) indicating the cluster to which each point is allocated.),
#'         \code{center} (A vector of integers (from 1:ncenters) indicating the cluster to which each point is allocated (only if method=’kmeans’ has been selected).),
#'         \code{cluster_lengths} (Length of repeated values in the cluster time series (only if method=’kmeans’ has been selected).),
#'         \code{persistence} (The value of the regime whose length is given in cluster_lengths (only if method=’kmeans’ has been selected)),
#'         \code{frequency} (Percentage of days in a month/season belonging to each cluster (only if method=’kmeans’ has been selected).),
#'         
#'@examples 
#'@export

WeatherRegime <- function(data, EOFS = TRUE, neofs = 30, threshold = NULL, lon = NULL, lat = NULL, ncenters = NULL, method = "kmeans",
                          nstart = 30, iter.max = 100, ncores = NULL) {
  if (length(dim(data)) > 4) {
    sdate <- which(names(dim(data)) == "sdate")
    ftime <- which(names(dim(data)) == "ftime")
    lon_dim <- which(names(dim(data)) == "lon")
    lat_dim <- which(names(dim(data)) == "lat")
    dims <- c(1 : length(dim(data)))[-c(sdate, ftime, lon_dim, lat_dim)]
    data <- aperm(data, c(sdate, ftime, lat_dim, lon_dim, dims))
    margins <- 5 : length(dim(data))
    result <- Apply(data = list(data), margins = list(margins), fun = ".WeatherRegime", EOFS = EOFS, neofs = neofs, 
                    threshold = threshold, lon = lon, lat = lat, ncenters = ncenters, method = method, 
                    ncores = ncores)
  } else {
    result <- .WeatherRegime(data, EOFS = EOFS, neofs = neofs, threshold = threshold,
                                  lon = lon, lat = lat, ncenters = ncenters, method = method)
  }
  return(result)
}


.WeatherRegime <- function(data, EOFS = TRUE, neofs = 30, threshold = NULL, lon = NULL, lat = NULL, ncenters = NULL, method = "kmeans",
                           nstart = 30) {
  
  names(dim(data)) <- c("sdate", "ftime", "lat", "lon")
  sdate <- which(names(dim(data)) == "sdate")
  ftime <- which(names(dim(data)) == "ftime")
  nftimes <- dim(data)[ftime]
  nsdates <- dim(data)[sdate]
  lon2 <- which(names(dim(data)) == "lon")
  lat2 <- which(names(dim(data)) == "lat")
  data <- aperm(data, c(ftime, sdate, lat2, lon2))
  nlon <- dim(data)[lon2]
  nlat <- dim(data)[lat2]
  dim(data) <- c(nftimes *  nsdates, nlat, nlon)
  
  if (is.null(ncenters)) {
    stop("ncenters must be specified")
  }
  if (EOFS  == TRUE && is.null(lon)) {
    stop("longitudes must be specified")
  }
  if (EOFS  == TRUE && is.null(lat)) {
    stop("latitudes must be specified")
  }
  
  if (EOFS  == TRUE) {
    dataPC <- EOF(data, lat = as.vector(lat), 
                  lon = as.vector(lon), 
                  neofs = neofs)
    if (is.null(threshold)){
      threshold <- sum(dataPC$var)
      cluster_input <- dataPC$PC
    } else {
      threshold <- threshold
      minPC <- head(as.numeric(which(cumsum(dataPC$var) > threshold)), 1)
      cluster_input <- dataPC$PC[, 1 : minPC]    }
  } else {
    cluster_input <- data
    latWeights <- InsertDim(InsertDim(cos(lat*pi/180), 1, nftimes*nsdates), 3, nlon)
    cluster_input <- cluster_input * latWeights
    dim(cluster_input) <- c(nftimes * nsdates, nlat * nlon)
  } 
  if (method == "kmeans") {
    clust <- kmeans(cluster_input, centers = ncenters, 
                     iter.max = 100, nstart = nstart, trace = FALSE)
    result <- array(0, c(ncenters, nlat, nlon))
    data <- aperm(data, c(2, 3, 1))
    result <- Composite(data, clust$cluster)
    names(dim(result$composite)) <- c("lon", "lat", "cluster")
    cluster_timeseries <- list(lengths = c(), values = c())
    frequency <- persistence <- matrix(NA, nsdates, ncenters)
    for (i in 1 : nsdates) {
      occurences <-
        rle(result$cluster[((i * nftimes) + 1 - nftimes):(i * nftimes)])
      cluster_timeseries <-list( lengths = c(cluster_timeseries$lengths, 
                                             occurences$lengths),
          values = c(cluster_timeseries$values, occurences$values)
        )
      for (j in 1 : ncenters) {
        frequency[i,j] <- (sum(occurences$lengths[occurences$values == j]) /  nftimes) * 100
        persistence[i,j] <- mean(occurences$lengths[occurences$values == j])
      }
    }
  } else {
    result <- hclust(dist(cluster_input), method = method)
    clusterCut <- cutree(result, ncenters)
    data <- aperm(data, c(3, 2, 1))
    result <- Composite(data, clusterCut)
  }
  if (method == "kmeans") {
    return(
      list(
        composite = result$composite,
        pvalue = result$pvalue,
        cluster = as.array(clust$cluster),
        center = as.array(clust$center),
        cluster_lengths = as.array(cluster_timeseries$lengths),
        cluster_values = as.array(cluster_timeseries$values),
        persistence = as.array(persistence),
        frequency = frequency
      )
    )
  } else {
    return(list(
      composite = result$composite,
      pvalue = result$pvalue,
      cluster = as.array(clusterCut)
    ))
  }
}

