SeriesAggreg <- function(TabSeries,
                         TimeFormat, NewTimeFormat,
                         ConvertFun,
                         YearFirstMonth = 1, TimeLag = 0,
                         verbose = TRUE) {
  
  
  ##_Arguments_check
  
  ##check_TabSeries
  if (is.null(TabSeries)       ) {
    stop("'TabSeries' must be a data.frame containing the dates and data to be converted")
  }
  if (!is.data.frame(TabSeries)) {
    stop("'TabSeries' must be a data.frame containing the dates and data to be converted")
  }
  if (ncol(TabSeries) < 2) {
    stop("'TabSeries' must contain at least two columns (including the coulmn of dates")
  }
  ##check_TimeFormat
  if (!any(class(TabSeries[, 1]) %in% "POSIXt")) {
    stop("'TabSeries' first column must be a vector of class 'POSIXlt' or 'POSIXct'")
  }
  if (any(class(TabSeries[, 1]) %in% "POSIXlt")) {
    TabSeries[, 1] <- as.POSIXct(TabSeries[, 1])
  }
  for (iCol in 2:ncol(TabSeries)) {
    if (!is.numeric(TabSeries[,iCol])) {
      stop("'TabSeries' columns (other than the first one) be of numeric class")
    }
  }
  if (is.null(TimeFormat)) {
    stop("'TimeFormat' must be one of 'hourly', 'daily', 'monthly' or 'yearly'")
  }
  if (!is.vector(TimeFormat)) {
    stop("'TimeFormat' must be one of 'hourly', 'daily', 'monthly' or 'yearly'")
  }
  if (!is.character(TimeFormat)) {
    stop("'TimeFormat' must be one of 'hourly', 'daily', 'monthly' or 'yearly'")
  }
  if (length(TimeFormat) != 1) {
    stop("'TimeFormat' must be one of 'hourly', 'daily', 'monthly' or 'yearly'")
  } 
  if (! TimeFormat %in% c("hourly", "daily", "monthly", "yearly")) {
    stop("'TimeFormat' must be one of 'hourly', 'daily', 'monthly' or 'yearly'")
  }
  ##check_NewTimeFormat
  if (is.null(NewTimeFormat)) {
    stop("'NewTimeFormat' must be one of 'hourly', 'daily', 'monthly' or 'yearly'")
  }
  if (!is.vector(NewTimeFormat)) {
    stop("'NewTimeFormat' must be one of 'hourly', 'daily', 'monthly' or 'yearly'")
  }
  if (!is.character(NewTimeFormat)) {
    stop("'NewTimeFormat' must be one of 'hourly', 'daily', 'monthly' or 'yearly'")
  }
  if (length(NewTimeFormat) != 1) {
    stop("'NewTimeFormat' must be one of 'hourly', 'daily', 'monthly' or 'yearly'")
  }
  if (! NewTimeFormat %in% c("hourly", "daily", "monthly", "yearly")) {
    stop("'NewTimeFormat' must be one of 'hourly', 'daily', 'monthly' or 'yearly'")
  }
  ##check_ConvertFun
  if (is.null(ConvertFun)) {
    stop("'ConvertFun' must be a vector of character")
  }
  if (!is.vector(ConvertFun)) {
    stop("'ConvertFun' must be a vector of character")
  }
  if (!is.character(ConvertFun)) {
    stop("'ConvertFun' must be a vector of character")
  }
  if (length(ConvertFun) != (ncol(TabSeries) - 1)) {
    stop(
      paste("'ConvertFun' must be of length", ncol(TabSeries) - 1, "(length=ncol(TabSeries)-1)")
    )
  }
  if (sum(ConvertFun %in% c("sum", "mean") == FALSE) != 0) {
    stop("'ConvertFun' elements must be one of 'sum' or 'mean'")
  }
  ##check_YearFirstMonth
  if (is.null(YearFirstMonth)) {
    stop("'YearFirstMonth' must be an integer between 1 and 12")
  }
  if (!is.vector(YearFirstMonth)) {
    stop("'YearFirstMonth' must be an integer between 1 and 12")
  }
  if (!is.numeric(YearFirstMonth)) {
    stop("'YearFirstMonth' must be an integer between 1 and 12")
  }
  YearFirstMonth <- as.integer(YearFirstMonth)
  if (length(YearFirstMonth) != 1) {
    stop("'YearFirstMonth' must be only one integer between 1 and 12")
  }
  if (YearFirstMonth %in% (1:12) == FALSE) {
    stop("'YearFirstMonth' must be only one integer between 1 and 12")
  } 
  ##check_DatesR_integrity
  if (TimeFormat == "hourly") {
    by <- "hours"
  }
  if (TimeFormat == "daily") {
    by <- "days"
  }
  if (TimeFormat == "monthly") {
    by <- "months"
  }
  if (TimeFormat == "yearly") {
    by <- "years"
  }
  TmpDatesR <- seq(from = TabSeries[1, 1], to = tail(TabSeries[, 1], 1), by = by)
  if (!identical(TabSeries[, 1], TmpDatesR)) {
    stop("some dates might not be ordered or are missing in 'TabSeries'")
  }
  ##check_conversion_direction
  if ((TimeFormat == "daily"   & NewTimeFormat %in% c("hourly")                  ) |
      (TimeFormat == "monthly" & NewTimeFormat %in% c("hourly","daily")          ) |
      (TimeFormat == "yearly"  & NewTimeFormat %in% c("hourly","daily","monthly"))) { 
    stop("only time aggregation can be performed")
  } 
  ##check_if_conversion_not_needed
  if ((TimeFormat == "hourly"  & NewTimeFormat == "hourly" ) |
      (TimeFormat == "daily"   & NewTimeFormat == "daily"  ) |
      (TimeFormat == "monthly" & NewTimeFormat == "monthly") |
      (TimeFormat == "yearly"  & NewTimeFormat == "yearly" )) { 
    if (verbose) {
      warning("the old and new format are identical \n\t -> no time-step conversion was performed")
      return(TabSeries)
    }
  }
  
  
  ##_Time_step_conversion
  
  ##_Handle_conventional_difference_between_hourly_series_and_others
  TmpDatesR <- TabSeries[, 1]
  #if (TimeFormat=="hourly") { TmpDatesR <- TmpDatesR - 60*60; }
  TmpDatesR <- TmpDatesR + TimeLag
  Hmax <- "00"
  if (TimeFormat == "hourly") {
    Hmax <- "23"
  }
  
  ##_Identify_the_part_of_the_series_to_be_aggregated
  NDaysInMonth <- list("31", c("28", "29"), "31", "30", "31", "30", "31", "31", "30", "31", "30", "31")
  YearLastMonth <- YearFirstMonth + 11
  if (YearLastMonth > 12) {
    YearLastMonth <- YearLastMonth - 12
  }
  YearFirstMonthTxt <- formatC(YearFirstMonth, format = "d", width = 2, flag = "0")
  YearLastMonthTxt  <- formatC(YearLastMonth , format = "d", width = 2, flag = "0")
  if (NewTimeFormat == "daily") {
    Ind1 <- which(format(TmpDatesR,    "%H") == "00")
    Ind2 <- which(format(TmpDatesR,    "%H") == Hmax)
    if (Ind2[1] < Ind1[1]) {
      Ind2 <- Ind2[2:length(Ind2)]
    }
    if (tail(Ind1, 1) > tail(Ind2, 1)) {
      Ind1 <- Ind1[1:(length(Ind1) - 1)]
    }
    ### Aggr <- NULL; iii <- 0; for(kkk in 1:length(Ind1)) {
    ### iii <- iii+1; Aggr <- c(Aggr,rep(iii,length(Ind1[kkk]:Ind2[kkk]))); }
    Aggr <- as.numeric(format(TmpDatesR[min(Ind1):max(Ind2)], "%Y%m%d"))
    ### more efficient
    NewDatesR <- data.frame(seq(from = TmpDatesR[min(Ind1)], to = TmpDatesR[max(Ind2)], by = "days"))
  }
  if (NewTimeFormat=="monthly") {
    Ind1 <- which(format(TmpDatesR,  "%d%H") == "0100")
    Ind2 <- which(format(TmpDatesR,"%m%d%H") %in% paste0(c("0131", "0228", "0229", "0331", "0430", "0531", "0630", "0731", "0831", "0930", "1031", "1130", "1231"), Hmax))
    Ind2[1:(length(Ind2) - 1)][diff(Ind2) == 1] <- NA
    Ind2 <- Ind2[!is.na(Ind2)] ### to keep only feb 29 if both feb 28 and feb 29 exists
    if (Ind2[1] < Ind1[1]) {
      Ind2 <- Ind2[2:length(Ind2)]
    }
    if (tail(Ind1, 1) > tail(Ind2, 1)) {
      Ind1 <- Ind1[1:(length(Ind1) - 1)]
    }
    ### Aggr <- NULL; iii <- 0; for(kkk in 1:length(Ind1)) { 
    ### iii <- iii+1; Aggr <- c(Aggr,rep(iii,length(Ind1[kkk]:Ind2[kkk]))); }
    Aggr <- as.numeric(format(TmpDatesR[min(Ind1):max(Ind2)],"%Y%m"));  ### more efficient
    NewDatesR <- data.frame(seq(from=TmpDatesR[min(Ind1)],to=TmpDatesR[max(Ind2)],by="months"))
  }
  if (NewTimeFormat == "yearly") {
    Ind1 <- which(format(TmpDatesR, "%m%d%H") %in% paste0(YearFirstMonthTxt, "0100"))
    Ind2 <- which(format(TmpDatesR, "%m%d%H") %in% paste0(YearLastMonthTxt, NDaysInMonth[[YearLastMonth]], Hmax))
    Ind2[1:(length(Ind2) - 1)][diff(Ind2) == 1] <- NA
    Ind2 <- Ind2[!is.na(Ind2)]
    ### to keep only feb 29 if both feb 28 and feb 29 exists
    if (Ind2[1] < Ind1[1]) {
      Ind2 <- Ind2[2:length(Ind2)]
    }
    if (tail(Ind1, 1) > tail(Ind2, 1)) {
      Ind1 <- Ind1[1:(length(Ind1) - 1)]
    }
    Aggr <- NULL
    iii <- 0
    for (kkk in 1:length(Ind1)) {
      iii <- iii + 1
      Aggr <- c(Aggr, rep(iii, length(Ind1[kkk]:Ind2[kkk])))
    }
    ### Aggr <- as.numeric(format(TmpDatesR[min(Ind1):max(Ind2)],"%Y")); ### not working if YearFirstMonth != 01
    NewDatesR <- data.frame(seq(from = TmpDatesR[min(Ind1)], to = TmpDatesR[max(Ind2)], by = "years"))
  }
  ##_Aggreation_and_export
  NewTabSeries <- data.frame(NewDatesR)
  for (iCol in 2:ncol(TabSeries)) {
    AggregData <- aggregate(TabSeries[min(Ind1):max(Ind2), iCol],
                            by = list(Aggr),
                            FUN = ConvertFun[iCol - 1], na.rm = FALSE)[, 2]
    NewTabSeries <- data.frame(NewTabSeries, AggregData)
  }
  names(NewTabSeries) <- names(TabSeries)
  return(NewTabSeries)
  
  
}