#'Daily anomalies
#'
#'@description This function computes temporal anomalies from a vector containing time series.
#'@param data A numeric n-dimensional array of data including one time dimension.
#'@param dates A vector of dates if \code{jdays} is not supplied. If NULL (by default), the 'time' attributes of parameter 'data' are considered.
#'@param storefreq The time frequency of input data. Can be 'daily' (default) or 'monthly'.
#'@param jdays A vector of the corresponding day of the year. This vector must be the same length as the time dimension of the parameter \code{data}.
#'@param timedim An integer number indicating the position of the time dimension in the parameter \code{data}. If NULL (by default), use the dimension called 'time' or 'dates' in parameter \code{data}.
#'@param na.rm A logical indicating whether missing values should be removed. If \code{na.rm} is FALSE an NA value in any of the arguments will cause a value of NA to be returned, otherwise (TRUE by default) NA values are ignored.
#'@param ncores The number of cores to be used when computing the index.
#'@return A named array of time anomalies of the same dimension as parameter \code{data}.
#'
#'@import multiApply 
#'@import PCICt
#'@examples
#'# Two-year data array:
#'data <- rnorm(2 * 3 * 731 * 2, 2, 1)
#'dim(data) <- c(lon = 2, lat = 3, time = 365 + 366, model = 2)
#'time <- seq(ISOdate(1903,1,1), ISOdate(1904,12,31), "days")
#'metadata <- list(time = list(standard_name = 'time', long_name = 'time',
#'                             calendar = 'standard',
#'                             units = 'days since 1970-01-01 00:00:00', prec = 'double',
#'                             dim = list(list(name ='time', unlim = FALSE))))
#'attr(time, 'variables') <- metadata
#'attr(data, 'Variables')$dat1$time <- time
#'attr(data, 'Variables')$dat2$time <- time
#'attr(data, 'Variables')$common[[2]]$dim[[3]]$len <- length(time)
#'attr(data, 'Variables')$common[[2]]$dim[[3]]$vals <- time
#'
#'a <- DailyAno(data, timedim = 3, na.rm = TRUE, ncores = NULL)
#'str(a)
#'@export
DailyAno <- function(data, dates = NULL, storefreq = 'daily', jdays = NULL, 
                     timedim = NULL, na.rm = TRUE, ncores = NULL) {
  if (is.null(data)) {
    stop("Parameter 'data' cannot be NULL.")
  }
  if (is.null(dim(data))) {
    dim(data) <- c(time = length(data))
    timedim <- 1
    warning(paste0("Parameter 'data' is a vector. Used as a time series ",
                   "and parameter 'timedim' is set to be 1.")
    )
  }
 if (is.null(jdays)) {
   if (is.null(dates)) {
     dates <- attr(data, 'Variables')$common$time
     if (is.null(dates)) {
       dates <- attr(data, 'Variables')$dat1$time
     }
   }
     if (is.null(dates)) {
       stop(paste0("No dates provided in parameter 'dates' nor as attribute ",
                   "of parameter 'data'. At least one of the parameters ",
                   "'jdays' or 'dates' must be supplied.")
       )
     }
   
    if (!any(class(dates) %in% c('POSIXct'))) {
      dates <- try( {
        if (is.character(dates)) {
          as.POSIXct(dates, format = "%Y%m%d")
        } else {
          as.POSIXct(dates)
        }
      })
      if ('try-error' %in% class(dates) | sum(is.na(dates)) == length(dates)) {
        stop(paste0("Dates provided in parameter 'dates' must be of class ",
                    "'POSIXct' or convertable to 'POSIXct'.")
        )
      }
    }

   if (storefreq == 'daily') {
      jdays <- as.numeric(strftime(dates, format = "%j"))
   } else if (storefreq == 'monthly') {
      jdays <- as.numeric(strftime(dates, format = "%m"))
   } else stop("Parameter 'storefreq' should be either 'daily' or 'monthly'.")


    year <- as.numeric(strftime(dates, format = "%Y"))
    if (length(unique(year)) > 1) {
        pos <- ((year / 100) %% 1 == 0) + ((year / 4) %% 1 == 0) +
                ((year / 400) %% 1 == 0)
        pos <- which(pos == 0 | pos == 2 | pos == 4)
      if (length(pos) > 0) {
          pos <- pos[which(jdays[pos] > 59)] 
          jdays[pos] <- jdays[pos] + 1
      }
    }
 }

  if (is.null(timedim)) {
    timedim <- which(names(dim(data)) == c("dates") | 
                     names(dim(data)) == c("time"))
  }
  if (length(timedim) == 0) {
    stop(paste0("The time dimension is not found in data. Please specify ",
                "with parameter 'timedim'.")
    )
  }

  if (!is.logical(na.rm)) {
    stop("Parameter 'na.rm' must be logical.")
  }
  if (length(na.rm) > 1) {
    na.rm = na.rm[1]
    warning(paste0("Parameter 'na.rm' has length > 1 and only the first ",
                   "element will be used.")
    )
  }

 margin <- c(1 : length(dim(data)))[-c(timedim)]

 DailyAno <- Apply(data, margins = margin, fun = .DailyAno, jdays = jdays, 
                   na.rm = na.rm, ncores = ncores)
 DailyAno <- DailyAno$output1
 names(dim(DailyAno))[1] <- names(dim(data))[timedim]
 if(is.null(names(dim(DailyAno))[1])) {
   names(dim(DailyAno))[1] <- 'time'
 }
 return(DailyAno)
}

.DailyAno <- function(data, jdays, na.rm = na.rm) {
  climatology <- tapply(data, INDEX = jdays, FUN = mean, na.rm = na.rm)
  anomalies <- c()
  for (i in 1 : length(data)) {
    index <-  which(names(climatology) == jdays[i])
    anomalies[i] <- data[i] - climatology[index]
  }
  return(anomalies)
}
