#'Daily anomalies
#'
#'@description This function computes daily anomalies from a vector containing the daily time series.
#'@param data A numeric multidimensional array of daily data with one time dimension.
#'@param dates A vector of dates with a calendar attributes. If NULL (by default), the 'time' attributes of parameter 'data' are considered.
#'@param calendar A character indicating the calendar type.
#'@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 timedim An integer number indicating the position of the time dimension in the parameter \code{data}. If NULL (by default), the dimension called 'time' in parameter \code{data}.
#'@param ncores The number of cores to be used when computing the index.
#'@return An array of daily anomalies of the same length 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 = 366 + 365, model = 2)
#'time <- seq(ISOdate(1903,1,1), ISOdate(1904,12,31), "days")
#'metadata <- list(time = list(standard_name = 'time', long_name = 'time',
#'                             calendar = 'noleap',
#'                             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, calendar = NULL, timedim = NULL, na.rm = TRUE, ncores = NULL) {
  if (is.null(data)) {
    stop("Parameters 'data' cannot be NULL.")
  }
  if (is.null(dim(data)) | length(dim(data)) < 1) {
    stop("Parameters 'data' must have at least two dimensions.")
  }

  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("No dates provided in parameter 'dates' nor as attribute of parameter 'data'.")
    }

    if (is.null(calendar)) {
      calendar <- attributes(dates)$calendar
      if (is.null(calendar)) {
        calendar <- attributes(dates)$variables$time$calendar
      }
      if (is.null(calendar)) {
        stop("The attribute 'calendar' must be present in the parameter 'data' or specified in parameter 'calendar'.")
      }
      # (end identifying)
    }

    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("Dates provided in parameter 'dates' must be of class 'POSIXct' or convertable to 'POSIXct'.")
      }
    }
    dates <- as.PCICt(dates, cal = calendar)
    dates = as.character(dates)
    jdays <- as.numeric(strftime(dates, format = "%j"))
    if (calendar == "gregorian" | calendar == "standard" | calendar == "proleptic_gregorian") {
      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) | length(timedim) == 0) {
    stop("Please specify time dimension in 'data' 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("Parameter 'na.rm' has length > 1 and only the first element will be used.")
  }

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

 DailyAno <- Apply(data, margins = margin, fun = .DailyAno, jdays = jdays, na.rm = na.rm, ncores = ncores)
 DailyAno <- DailyAno$output1
 names(dim(DailyAno))[1] <- "time"
 return(DailyAno)
}

.DailyAno <- function(data, jdays = NULL, 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)
}
