#'Compute the Trend of the Ensemble Mean
#'
#'Compute the polynomial regression along the forecast time with any degree of 
#'polynomial. It returns the regression coefficients (including the intercept) 
#'and the confidence intervals if needed. The detrended array is also provided.\cr
#'The confidence interval relies on the student-T distribution.\cr\cr
#'
#'@param data An numeric array including the dimension along which the trend 
#'  is computed.
#'@param time_dim A character string indicating the dimension along which to 
#'  compute the trend.
#'@param interval A positive numeric indicating the unit length between two 
#' points along 'time_dim' dimension. Set 1 as default.
#'@param siglev A numeric indicating the confidence level for the 
#'  regression computation. Set 0.95 as default.
#'@param conf A logical value indicating whether to retrieve the confidence 
#'  intervals or not. Set TRUE as default.
#'@param polydeg A positive integer indicating the degree of polynomial 
#'  regression. Set 1 as default.
#'@param ncores An integer indicating the number of cores to use for parallel 
#'  computation. Default value is NULL.
#'
#'@return 
#'\item{$trend}{
#'  A numeric array with same dimensions as parameter 'data' except the 
#'  'time_dim' dimension, which is replaced by a 'stats' dimension containing 
#'  the regression coefficients from the lowest order (i.e., intercept) to 
#'  the highest degree. The length of the 'stats' dimension should be 
#'  \code{polydeg + 1}.  
#'}
#'\item{$conf.lower}{
#'  A numeric array with same dimensions as parameter 'data' except the 
#'  'time_dim' dimension, which is replaced by a 'stats' dimension containing 
#'  the lower limit of the \code{siglev}\% confidence interval for all
#'  the regression coefficients with the same order as $trend. The length of the 
#'  'stats' dimension should be \code{polydeg + 1}. Only present if \code{conf = TRUE}.
#'}
#'\item{$conf.upper}{
#'  A numeric array with same dimensions as parameter 'data' except the 
#'  'time_dim' dimension, which is replaced by a 'stats' dimension containing 
#'  the upper limit of the \code{siglev}\% confidence interval for all
#'  the regression coefficients with the same order as $trend. The length of the 
#'  'stats' dimension should be \code{polydeg + 1}. Only present if \code{conf = TRUE}.
#'}
#'\item{$detrended}{
#'  A numeric array with the same dimensions as paramter 'data', the detrended 
#'  values along the 'time_dim' dimension.
#'}
#'
#'@keywords datagen
#'@author History:\cr
#'0.1  -  2011-05  (V. Guemas, \email{virginie.guemas@@ic3.cat})  -  Original code\cr
#'1.0  -  2013-09  (N. Manubens, \email{nicolau.manubens@@ic3.cat})  -  Formatting to CRAN\cr
#'2.0  -  2017-02  (A. Hunter, \email{alasdair.hunter@@bsc.es})  -  Adapt to veriApply()
#'3.0  -  2019-12  (A. Ho, \email{an.ho@bsc.es})  -  Adapt to multiApply()
#'@examples
#'# Load sample data as in Load() example:
#'example(Load)
#'months_between_startdates <- 60
#'trend <- Trend(sampleData$obs, polydeg = 2)
#'
#'@rdname Trend
#'@import multiApply
#'@export
Trend <- function(data, time_dim = 'sdate', interval = 1, siglev = 0.95, conf = TRUE, polydeg = 1, ncores = NULL) {

  # Check inputs 
  ## data
  if (is.null(data)) {
    stop("Parameter 'data' cannot be NULL.")
  }
  if (!is.numeric(data)) {
    stop("Parameter 'data' must be a numeric array.")
  }
  if (is.null(dim(data))) {  #is vector
    dim(data) <- c(length(data))
    names(dim(data)) <- time_dim
  }
  if(any(is.null(names(dim(data))))| any(nchar(names(dim(data))) == 0)) {
    stop("Parameter 'data' must have dimension names.")
  }
  ## time_dim
  if (!is.character(time_dim) | length(time_dim) > 1) {
    stop("Parameter 'time_dim' must be a character string.")
  }
  if (!time_dim %in% names(dim(data))) {
    stop("Parameter 'time_dim' is not found in 'data' dimension.")
  }
  ## interval
  if (any(!is.numeric(interval) | interval <= 0 | length(interval) > 1)) {
    stop("Parameter 'interval' must be a positive number.")
  }
  ## siglev
  if (!is.numeric(siglev) | siglev < 0 | siglev > 1 | length(siglev) > 1) {
    stop("Parameter 'siglev' must be a numeric number between 0 and 1.")
  }
  ## conf
  if (!is.logical(conf) | length(conf) > 1) {
    stop("Parameter 'conf' must be one logical value.")
  }
  ## polydeg
  if (!is.numeric(polydeg) | polydeg %% 1 != 0 | polydeg < 0 |
      length(polydeg) > 1) {
    stop("Parameter 'polydeg' must be a positive integer.")
  }
  ## ncores
  if (!is.null(ncores)) {
    if (!is.numeric(ncores) | ncores %% 1 != 0 | ncores < 0 |
      length(ncores) > 1) {
      stop("Parameter 'ncores' must be a positive integer.")
    }
  }

  ###############################
  # Calculate Trend
  dim_names <- names(dim(data))

  output <- Apply(list(data),
                  target_dims = time_dim,
                  fun = .Trend,
                  #output_dims = ,
                  interval = interval, siglev = siglev, conf = conf,
                  polydeg = polydeg,
                  ncores = ncores)

  output <- lapply(output, .reorder, time_dim = time_dim, dim_names = dim_names)

  return(output)
}

.Trend <- function(x, interval = interval, siglev = siglev, conf = conf, polydeg = polydeg) {
  if (any(!is.na(x))) {
    mon <- seq(x) * interval
    lm.out <- lm(x ~ poly(mon, degree = polydeg, raw = TRUE), na.action = na.omit)
    trend <- lm.out$coefficients  #intercept, slope1, slope2,...

    if (conf) {
      conf.lower <- confint(lm.out, level = siglev)[, 1]
      conf.upper <- confint(lm.out, level = siglev)[, 2]
    }

    detrended <- c()
    detrended[is.na(x) == FALSE] <- x[is.na(x) == FALSE] - lm.out$fitted.values
  } else {
    trend <- NA
    detrend <- NA
    if (conf) {
      conf.lower <- NA
      conf.upper <- NA
    }
  }

  if (conf) {
    return(list(trend = trend, conf.lower = conf.lower, conf.upper = conf.upper, 
                detrended = detrended))
  } else {
    return(list(trend = trend, detrended = detrended))
  }

}

  .reorder <- function(output, time_dim, dim_names) {

    # Add dim name back
    if (is.null(dim(output))) {
      dim(output) <- c(stats = length(output))

    } else {  #is an array
      if (length(dim(output)) == 1) {
        if (!is.null(names(dim(output)))) {
          dim(output) <- c(1, dim(output))
          names(dim(output))[1] <- time_dim
        } else {
          names(dim(output)) <- time_dim
        }
      } else {  # more than one dim
        if (names(dim(output))[1] != "") {
          dim(output) <- c(1, dim(output))
          names(dim(output))[1] <- time_dim
        } else {   #regular case
          names(dim(output))[1] <- time_dim
        }
      }
    }
    # reorder
    pos <- match(dim_names, names(dim(output)))
    output <- aperm(output, pos)
    names(dim(output)) <- dim_names
    names(dim(output))[names(dim(output)) == time_dim] <- 'stats'

    return(output)
  }
