# Function to permute arrays of non-atomic elements (e.g. POSIXct)
.aperm2 <- function(x, new_order) {
  y <- array(1:length(x), dim = dim(x))
  y <- aperm(y, new_order)
  old_dims <- dim(x)
  x <- x[as.vector(y)]
  dim(x) <- old_dims[new_order]
  x
}

"+.s2dv_ts_plot" <- function(a, b) {
  a_is_s2dv <- FALSE
  if ('s2dv_ts_plot' %in% class(a)) {
    class(a) <- class(a)[-which(class(a) == 's2dv_ts_plot')]
    a_is_s2dv <- TRUE
  }
  b_is_s2dv <- FALSE
  if ('s2dv_ts_plot' %in% class(b)) {
    class(b) <- class(b)[-which(class(b) == 's2dv_ts_plot')]
    b_is_s2dv <- TRUE
  }

  if (a_is_s2dv && b_is_s2dv) {
    is_datetime_x_axis <- FALSE

    a_built <- ggplot_build(a)
    if (any(grepl('Datetime', class(a_built$layout$panel_scales$x[[1]])))) {
      a_breaks <- a_built$layout$panel_scales$x[[1]]$breaks
      is_datetime_x_axis <- TRUE
    } else {
      a_breaks <- do.call(a_built$layout$panel_scales$x[[1]]$get_breaks, list())
    }
    a_labels <- a_built$layout$panel_ranges[[1]]$x.labels

    b_built <- ggplot_build(b)
    if (any(grepl('Datetime', class(b_built$layout$panel_scales$x[[1]])))) {
      b_breaks <- b_built$layout$panel_scales$x[[1]]$breaks
      is_datetime_x_axis <- TRUE
    } else {
      b_breaks <- do.call(b_built$layout$panel_scales$x[[1]]$get_breaks, list())
    }
    b_labels <- b_built$layout$panel_ranges[[1]]$x.labels

    if (!identical(class(a_breaks), class(b_breaks))) {
      stop("Can only merge s2dv plots with x axes of the same type.")
    }

    x_axis_labels <- c(a_labels, b_labels)
    c <- a + b$layers
    if (is_datetime_x_axis) {
      c <- suppressMessages(c + scale_x_datetime(breaks = c(a_breaks, b_breaks),
                                                 labels = x_axis_labels))
    } else {
      c <- suppressMessages(c + scale_x_discrete(breaks = c(a_breaks, b_breaks),
                                                 labels = x_axis_labels))
    }
  } else {
    c <- a + b
  }
  class(c) <- c('s2dv_ts_plot', class(c))
  c
}

PlotTimeSeries <- function(var,
                           coords = NULL,
                           x_axis_dim = NULL, 
                           curves_along = NULL, 
                           colour_along = NULL, 
                           style_along = NULL,
                           points = FALSE, 
                           shape_along = NULL,
#                           mean_along = NULL, 
#                           minmax_along = mean_along, 
#                           interval_along = NULL, 
#                           interval_type = 'errorbar',
#                           test_identifier = NULL, 
#                           intercept = NA, 
                           first_date = NULL,
                           time_period = NULL,
                           ftime_period = NULL,
                           sdate_period = NULL, 
                           ref_dimnames = NULL,
                           file_name = NULL, 
                           file_width = NA, 
                           file_height = NA, 
                           file_size_units = 'in'
                          ) { 
#  library(ggplot2)

#  ArrayToMultiCDM <- function(var) {
#    x <- var
#    class(x) <- "multiCDM"
#    attr(x, "additionalAttributes") <- attributes(x)
#    attr(x, "Dataset") <- list(Dataset = NULL, Realization = NULL, 
#                               Variable = list(Variable = NULL, hasCfname = NULL, withUnits = NULL),
#                               TemporalPeriod = list(hasStartyear = NULL,
#                                                     hasEndyear = NULL,
#                                                     hasMonth = NULL),
#                               TemporalInstant = list(hasInitDate = NULL),
#                               SpatialExtent = list(lon = NULL, lat = NULL))
#    
#    attr(x, "Step") <- list(class = NULL, type = NULL, attribute = NULL)
#    attr(x, "dimensionNames") <- names(dim(x))
#    attr(x, "Command") <- list(generatedbyCommand = NULL, fromPackage = NULL,
#                               packageVersion = NULL, withForecast = NULL, 
#                               withReference = NULL)
#    attr(x, "hasNext") <- NULL
#    invisible(x)
#  }
#  
#  
  # This function translates a dimension specification (e.g. for colour_along, 
  # shape_along, ...), that can be either NA, a number, a name or NULL, into
  # NA or a number always. If the specification is NULL (the user does not
  # provide it) some pre-defined names will be seeked among the dimension
  # names, and the first one that matches will be selected as target dimension.
  # If it is not found, it is left as NA (do not apply colours, shapes, ...).
  CheckDimensionSpec <- function(param, dims, seek_names = NULL) {
    dimnames <- names(dims)
    if (is.null(param)) {
      if (is.null(dimnames)) {
        param <- NA
      } else {
        param <- NA
        found <- FALSE
        i <- 1
        while ((i <= length(seek_names)) && !found) {
          if (seek_names[i] %in% dimnames) {
            # Do not consider dimensions of length 1 while seeking
            if (dims[grep(seek_names[i], dimnames)] > 1) {
              param <- seek_names[i]
              found <- TRUE
            }
          }
          i <- i + 1
        }
      }
    }
    # If it's a NA value, transform to a logical NA always, for safety.
    if (is.na(param)) {
      param <- as.logical(param)
    }
    if (is.character(param)) {
      if (param %in% dimnames) {
        param <- which(dimnames == param)
      } else {
        stop("All the dimension names provided as character strings in the ",
             "'_along' parameters must be one of the dimension names of 'var'.")
      }
    } else if (is.numeric(param)) {
      param <- round(param)
      if (param > length(dimnames)) {
        stop("All the dimension indices provided as integer numbers in the ",
             "'_along' parameters must be in the range 1:length(dim(var)).")
      }
    } else if (!is.na(param)) {
      stop("Each provided '_along' parameter must be either a character string",
           ", a numeric value or NA.")
    }
    
    param
  }

#  Convert2LongName <- function(x) {
#    if (x == "sdate") {
#      x <- "Start Date"
#    } else if (x == "ftime") {
#      x <- "Forecast Time"
#    } else if (x == "member") {
#      x <- "Member"
#    } else if (x == "dataset") {
#      x <- "Dataset"
#    } else if (x == "lat") {
#      x <- "Latitude"
#    } else if (x == "lon") {
#      x <- "Longitude"
#    }
#    return(x)
#  }

  # Check points
  if (!is.logical(points)) {
    stop("Parameter 'points' must be logical.")
  }

  # Check device parameters
  if (!is.null(file_name)) {
    if (!is.character(file_name) && !is.null(file_name)) {
      stop("Parameter 'file_name' must be a character string.")
    }
    # Check size parameters
    if (!is.numeric(file_width) && !is.na(file_width)) {
      stop("Parameter 'file_width' must be numeric.")
    }
    if (!is.numeric(file_height) && !is.na(file_height)) {
      stop("Parameter 'file_height' must be numeric.")
    }
    if (!(file_size_units %in% c("cm", "in", "mm"))) {
      stop("Parameter 'file_size_units' must be one of 'cm', 'in' or 'mm'.")
    }
  }

#  if (class(var) == 'multiCDM') {
#    # Store the multidata in a seperate object for convenience
#    metadata <- attributes(var)
#  } else if (is.array(var)) {
#    var <- ArrayToMultiCDM(var)
#    metadata <- attributes(var)
#  } else {
#    stop("Parameter 'var' must be a 'multiCDM' object or a numeric array.")
#  }

  # Check var
  if (!is.numeric(var)) {
    stop("Parameter 'var' must be a numeric object.")
  }
  if (is.null(dim(var))) {
    if (length(var) < 1) {
      stop("Parameter 'var' must contain at least one value.")
    }
    dim(var) <- length(var)
  }
  # Trick to accept numeric vectors
  if (length(dim(var)) == 1) {
    dim(var) <- c(dim(var), 1)
  }
  if (is.null(names(dim(var)))) {
    names(dim(var)) <- paste0('dim', 1:length(dim(var)))
  } else {
    # Check no dim names are of the form dimN for safety.
    if (any(grepl('^dim[0-9]{1,}$', names(dim(var))))) {
      stop("Dimension names of 'var' must not follow the pattern 'dimN'.")
    }
    # Name unnamed dimensions
    if (any(nchar(names(dim(var))) == 0)) {
      names(dim(var))[which(nchar(names(dim(var))) == 0)] <- paste0('dim', which(nchar(names(dim(var))) == 0))
    }
    # Check there are no repeated dimension names, for safety.
    if (length(names(dim(var))) != length(unique(names(dim(var))))) {
      stop("There must be no repeated names in the provided dimension ",
           "names for any of the arrays in 'var'.")
    }
  }
  dims <- dim(var)

  # Check ref_dimnames
  if (is.null(ref_dimnames)) {
    ref_dimnames <- c('dataset', 'var', 'member', 'sdate', 'ftime', 
                      'level', 'lat', 'lon', 'test')  
  } else if (!is.character(ref_dimnames) || (length(ref_dimnames) != 9)) {
    stop("Parameter 'ref_dimnames' must be a character vector of length ",
         "9, with the expected dimension names for the datasets, variables, ",
         "levels, members, start dates, forecast time steps, latitudes, ",
         "longitudes and statistical test.")
  }
  names(ref_dimnames) <- c('dataset', 'var', 'member', 'sdate', 'ftime',
                           'level', 'lat', 'lon', 'test')

  # Check first_date
  if (is.null(first_date)) {
    first_date <- as.POSIXct('0000-01-01')
  }
  if (!any(c('Date', 'POSIXt') %in% class(first_date))) {
    stop("Parameter 'first_date' must be a Date or POSIXt object.")
  }

  # Check time_period
  if (is.null(time_period)) {
    time_period <- 1
  }
  if (is.numeric(time_period)) {
    time_period <- paste(time_period, 'month')
  }
  if (!is.character(time_period)) {
    stop("Parameter 'time_period' must be numeric or a character string.")
  }
  if (length(strsplit(time_period)[[1]]) != 2) {
    stop("Parameter 'time_period' must follow the format '<number> <time units>' as specified in ?difftime.")
  }
  time_period <- as.difftime(strsplit(time_period)[[1]][1], 
                             units = strsplit(time_period)[[1]][2])

  # Check ftime_period
  if (is.null(ftime_period)) {
    ftime_period <- 1
  }
  if (is.numeric(ftime_period)) {
    ftime_period <- paste(ftime_period, 'month')
  }
  if (!is.character(ftime_period)) {
    stop("Parameter 'ftime_period' must be numeric or a character string.")
  }
  if (length(strsplit(ftime_period)[[1]]) != 2) {
    stop("Parameter 'ftime_period' must follow the format '<number> <time units>' as specified in ?difftime.")
  }
  ftime_period <- as.difftime(strsplit(ftime_period)[[1]][1], 
                              units = strsplit(ftime_period)[[1]][2])
 
  # Check sdate_period
  if (is.null(sdate_period)) {
    sdate_period <- 1
  }
  if (is.numeric(sdate_period)) {
    sdate_period <- paste(sdate_period, 'month')
  }
  if (!is.character(sdate_period)) {
    stop("Parameter 'sdate_period' must be numeric or a character string.")
  }
  if (length(strsplit(sdate_period)[[1]]) != 2) {
    stop("Parameter 'sdate_period' must follow the format '<number> <time units>' as specified in ?difsdate.")
  }
  sdate_period <- as.difsdate(strsplit(sdate_period)[[1]][1], 
                              units = strsplit(sdate_period)[[1]][2])

  periods <- list(time = time_period, ftime = ftime_period, sdate = sdate_period)

#  
#  if (is.null(mean_along) && all(ref_dimnames[5:6] %in% dimnames)) {
#    mean_along <- ref_dimnames[5]
#  }
#  mean_along <- CheckDimensionSpec(mean_along, dimnames)
#  
#  # Check interval_along
#  if (is.null(interval_along) && all(ref_dimnames[c(6, 9)] %in% dimnames)) {
#    interval_along <- ref_dimnames[9]
#  }
#  interval_along <- CheckDimensionSpec(interval_along, dimnames)
#  
#  # Check error_bar_along
#  if (is.null(error_bar_along) && all(ref_dimnames[c(5, 9)] %in% dimnames) &&
#      !(ref_dimnames[6] %in% dimnames)) {
#    error_bar_along <- ref_dimnames[9]
#  }
#  error_bar_along <- CheckDimensionSpec(error_bar_along, dimnames)
#  
#  # Check error_type
#  if (!interval_type %in% c('errorbar', 'line')) {
#    stop("Parameter 'interval_type' must be either 'line' or 'errobar'.")
#  }
#  
#  # Check horizontal line
#  if (!is.na(intercept) & !is.numeric(intercept)) {
#    stop("Parameter 'intercept' must be either NA or numeric.")
#  }
#
  # Check x_axis_dim.
  x_axis_dim <- CheckDimensionSpec(x_axis_dim, dims, ref_dimnames[c('ftime', 'sdate')])
  # If no time dimension is found, pick the longest one for the x axis.
  if (is.na(x_axis_dim)) {
    x_axis_dim <- which.max(dims)
    print(paste0("No 'x_axis_dim' specified and no time dimension found in ",
                 "'var', according to 'ref_dimnames'. Taking the longest ",
                 "dimension (", names(dims)[x_axis_dim], 
                 ") to be represented along x axis."))
  }

  # Check coords
  # Examples of valid inputs:
  # list(member = c('r1i1p1', 'r2i1p1', 'r3i1p1'),
  #      time = as.POSIXct(paste0(1990:2000, '-11-01')))
  # list(dim1 = c('r1i1p1', 'r2i1p1', 'r3i1p1'),
  #      dim2 = as.POSIXct(paste0(1990:2000, '-11-01')))
  # sdates <- as.POSIXct(paste0(1990:2000, '-11-01'))
  # dim(sdates) <- 'sdate'
  # list(member = c('r1i1p1', 'r2i1p1', 'r3i1p1'),
  #      time = sdates)
  if (is.null(coords)) {
    coords <- list()
  }
  if (!is.list(coords)) {
    stop("Parameter 'coords', if provided, must be a list with coordinate ",
         "values for dimensions in 'var'.")
  }
  if (is.null(names(coords)) && (length(coords) > 0)) {
    stop("Parameter 'coords', if provided, must be a named list where the ",
         "names match the dimension names of 'var', and the values are the ",
         "associated coordinate values for the corresponding dimension.")
  }
  # Check the provided coords are ok.
  inflate_array <- function(a, d) {
    missing_dims <- dims[which(!(names(d) %in% names(dim(a))))]
    original_dims <- dim(a)
    a <- rep(a, prod(missing_dims))
    dim(a) <- c(original_dims, missing_dims)
    new_order <- sapply(names(d), 
                        function (x) {
                          which(names(dim(a)) == x)[1]
                        })
    a <- .aperm2(a, new_order)
    names(dim(a)) <- names(d)
    a
  }
  if (length(coords) > 0) {
    for (i in names(coords)) {
      are_time_coords <- any(c('Date', 'POSIXt') %in% class(coords[[i]]))
      if (!(i %in% names(dims))) {
        stop("Provided coordinates in 'coords' for a non-existing ",
             "dimension '", i, "' (not present in 'var').")
      }
      # If provided coordinate variable for x axis, need to extend that array
      # with repeated data to have same dimensions as 'var'. This way each point
      # in 'var' will have an associated x axis position.
      if (names(dims)[x_axis_dim] == i) {
        if (is.vector(coords[[i]]) || (are_time_coords && !is.array(coords[[i]]))) {
          if (length(coords[[i]]) != dims[i]) {
            stop("Provided coordinates of wrong length in 'coords' for the ",
                 "dimension '", i, "'.")
          }
          dim(coords[[i]]) <- setNames(length(coords[[i]]), i)
        }
        if (!is.array(coords[[i]])) {
          stop("The provided coordinates for the dimension '", i, 
               "' (selected as x-axis dimension) must be a vector or an array.")
        }
        if (is.null(names(dim(coords[[i]])))) {
          stop("The provided coordinates for the dimension '", i, 
               "', if provided as an array, must have dimension names.")
        }
        if (!identical(unname(dim(coords[[i]])), 
                       unname(dims[names(dim(coords[[i]]))]))) {
          stop("The dimensions of the provided coordinates for the dimension '", i,
               "', if provided as an array, must be of the same length of those ",
               "in the parameter 'var'.")
        }
        if (length(dim(coords[[i]])) < length(dims)) {
          if (are_time_coords) {
            if (any(c('time', ref_dimnames[c('ftime', 'sdate')]) %in% names(dims))) {
              # This for loop has been copy/pasted below
              for (time_dim_name in c('time', ref_dimnames[c('ftime', 'sdate')])) {
                if ((time_dim_name %in% names(dims)) && !(time_dim_name %in% names(dims(coords[[i]])))) {
                  old_coord_dims <- dim(coords[[i]])
                  coords[[i]] <- rep(coords[[i]], dims[time_dim_name])
                  if (dims[time_dim_name] > 0) {
                    time_addition <- rep((0:(dims[time_dim_name] - 1) * periods[[time_dim_name]], each = length(coords[[i]])))
                    coords[[i]] <- coords[[i]] + time_addition
                  }
                  dim(coords[[i]]) <- c(old_coord_dims, dims[time_dim_name])
                }
              }
            }
          } else if (i %in% c('time', ref_dimnames[c('ftime', 'sdate')])) {
            warning("Provided coordinates which are not dates for the dimension '",
                    i, "'.")
          } 
          coords[[i]] <- inflate_array(coords[[i]], dims)
        }
      } else if (length(coords[[i]]) != dims[i]) {
        stop("Provided coordinates of wrong length in 'coords' for the ",
             "dimension ", i, ".")
      }
    } else if ((i %in% c('time', ref_dimnames[c('ftime', 'sdate')])) &&
               !are_time_coords) {
      warning("Provided coordinates which are not dates for the dimension '", i, "'.")
    }
  }
  # Populate non-provided coords with default values.
  for (i in names(dims)) {
    if (!(i %in% names(coords))) {
      if ((names(dims)[x_axis_dim] == i)) {
        if (i %in% c(ref_dimnames[c('ftime', 'sdate')], 'time'))) {
          coords[[i]] <- seq(first_date, length.out = dims[i], by = periods[[time_dim_name]])
          dim(coords[[i]]) <- dims[i]
          # This for loop has been copy/pasted from above
          for (time_dim_name in c('time', ref_dimnames[c('ftime', 'sdate')])) {
            if ((time_dim_name %in% names(dims)) && !(time_dim_name %in% names(dims(coords[[i]])))) {
              old_coord_dims <- dim(coords[[i]])
              coords[[i]] <- rep(coords[[i]], dims[time_dim_name])
              if (dims[time_dim_name] > 0) {
                time_addition <- rep((0:(dims[time_dim_name] - 1) * periods[[time_dim_name]], each = length(coords[[i]])))
                coords[[i]] <- coords[[i]] + time_addition
              }
              dim(coords[[i]]) <- c(old_coord_dims, dims[time_dim_name])
            }
          }
        } else {
          coords[[i]] <- 1:dims[i]
          dim(coords[[i]]) <- dims[i]
        }
        coords[[i]] <- inflate_array(coords[[i]], dims)
      } else if (i %in% ref_dimnames) {
        ## TODO
        #if (values present in input data/metadata) {
        #  take from there and check
        coords[[i]] <- paste(i, 1:dims[i])
        #} else 1:dims[i]
      } else {
        coords[[i]] <- 1:dims[i]
      }
    }
  }
  # Put in proper order
  coords <- coords[names(dims)]
  is_time_x_axis <- any(c('Date', 'POSIXt') %in% class(coords[[x_axis_dim]]))

  # Check curves along
  if (is.null(curves_along)) {
    curves_along <- x_axis_dim
  }
  curves_along <- CheckDimensionSpec(curves_along, dims)
  group_dims <- 1:length(dims)
  if (!is.na(curves_along)) {
    group_dims <- group_dims[-curves_along]
  }

  # Check colour_along
#  if (is.null(interval_along)){
    if (all(ref_dimnames[c('sdate', 'ftime')] %in% names(dims))) {
      colour_along_priorities <- ref_dimnames['sdate']
    } else {
      colour_along_priorities <- ref_dimnames[c('dataset', 'var', 'member', 'level')]
    }
    colour_along <- CheckDimensionSpec(colour_along, dims,
                                       colour_along_priorities)
#  }
  # Check style_along
#  if (is.null(interval_along)){
    style_along_priorities <- ref_dimnames[c('dataset', 'var', 'member', 'level')]
    if (!is.na(colour_along)) {
      style_along_priorities <- setdiff(style_along_priorities, names(dims)[colour_along])
    }
    if (is.null(style_along)) {
      style_along_priorities <- setdiff(style_along_priorities, names(dims)[curves_along])
    }
    style_along <- CheckDimensionSpec(style_along, dims,
                                      style_along_priorities)
    if (identical(as.integer(style_along), as.integer(curves_along)) && 
        !is.na(style_along)) {
      stop("Impossible to change curve style along the 'curves_along' dimension. ",
           "Provide different values for 'curves_along' or 'style_along'.")
    }
#  }
  # Check shape_along
#  if (is.null(interval_along)){
    shape_along_priorities <- style_along_priorities
    if (!is.na(style_along)) {
      shape_along_priorities <- setdiff(shape_along_priorities, names(dims)[style_along])
    }
    shape_along <- CheckDimensionSpec(shape_along, dims,
                                      shape_along_priorities)
#  }

#  # Check xtitle
#  if (is.null(xtitle)) {
#    xtitle <- NA
#    xtitle <- dimnames[x_axis_dim]
#    xtitle <- Convert2LongName(xtitle)
#    x_axis_format <- NULL
#
#   if (period == '1 month' && length(levels(var$date)) <= 12) {
#      x_axis_format <- scale_x_datetime(labels = date_format("%m"))
#      xtitle <- paste(xtitle, "(Months)")
#    }
#  } else if (is.na(xtitle)) {
#    xtitle <- as.logical(xtitle)
#  } else if (!is.character(xtitle)) {
#    stop("Parameter 'xtitle' must be a character string.")
#  }
#  
#  # Check ytitle
#  if (is.null(ytitle)) {
#    ytitle <- 'value'
#    if (!is.null(metadata$Step$type)) {
#      ytitle <- paste(metadata$Step$type)
#    } else if (!is.null(metadata$Command$generatedbyCommand)) { 
#      ytitle <- paste(metadata$Command$generatedbyCommand)
#    } else if (is.null(metadata$Command$generatedbyCommand) & 
#               is.null(metadata$Step$type) & !is.null(metadata$Dataset$Variable$hasCfname)) {
#                 ytitle <- metadata$Dataset$Variable$hasCfname
#               }
#    if (!is.null(metadata$Dataset$Variable$withUnits)) {
#      ytitle <- paste(ytitle, "(", metadata$Dataset$Variable$withUnits, ")")
#    }
#  } else if (is.na(ytitle)) {
#    ytitle <- as.logical(ytitle)
#  } else if (!is.character(ytitle)) {
#    stop("Parameter 'ytitle' must be a character string.")
#  }
#  
#  if (is.null(title)) {
#    title <- NA
#    if (!is.null(metadata)) {
#      if (!is.null(metadata$Dataset$Variable$hasVariable)) {
#        title <- metadata$Dataset$Variable$hasVariable
#      } else {
#        title <- " "
#        if (!is.null(metadata$Command$withForecast)) {
#          title <- paste(metadata$Command$withForecast, "and", metadata$Command$withReference)
#        } else if (is.null(metadata$Command$withForecast) && !is.null(metadata$Dataset$Dataset)) {
#          title <- paste("for", metadata$Dataset$Dataset)
#        }
#        if (!is.null(metadata$Dataset$Variable)) {
#          title <- paste(metadata$Dataset$Variable$Variable, title)
#        }
#        if (!is.null(metadata$Step$type)) {
#          title <- paste(metadata$Step$type, "for", title)
#        }
#      }
#    }
#  } else if (is.na(title)) {
#    title <- as.logical(title)
#  } else if (!is.character(title)) {
#    stop("Parameter 'title' must be a character string.")
#  }

  # Plot
  g <- ggplot()
  # Convert array into data frame for ggplot2.
  var <- as.data.frame.table(var)
  names(var) <- c(names(dims), 'value')
  x_coord_copy <- coords[[x_axis_dim]]
  dim(x_coord_copy) <- NULL
  if (!is_time_x_axis && !is.numeric(x_coord_copy)) {
    vals <- unique(x_coord_copy)
    var$coord <- sapply(x_coord_copy, function(x) which(vals == x)[1])
  } else {
    var$coord <- x_coord_copy
  }
#  
#  if (!is.na(interval_along) || !is.na(error_bar_along)) {
#    
#    if (is.na(colour_along)) {
#      if (add) {
#        colour_along <- 'blue'
#      } else {
#        colour_along <- 'red'
#      }
#    }
#    if (is.numeric(colour_along)) {
#      colour_along <- var[, colour_along]
#    }
#m
#    #test <- levels(var$test) 
#    if (!is.null(test_identifier)) {
#      test <- test_identifier
#    } else {
#      levels(var$test)[1] <- "conf_high"
#      levels(var$test)[2] <- "test"
#      levels(var$test)[3] <- "conf_low"
#      test <- "test"
#    }
#      if (length(levels(var$test)) == 1) { 
#      g <- g + geom_line(aes_string(x = var$date[var$test == test], y = var$value[var$test == test]), colour = colour_along) + 
#        x_axis_format
#        colour_along <- NA
#    } else {
#      if (interval_type == "errorbar") {
#      g <- g + geom_point(aes_string(x = var$date[var$test == test], y = var$value[var$test == test]), colour = colour_along) + 
#        geom_errorbar(aes_string(x = var$date[var$test == test], ymin = var$value[var$test == "conf_low"], 
#                                 ymax = var$value[var$test == "conf_high"]), colour = colour_along) + 
#         x_axis_format
#       colour_along <- NA
#    } else if (interval_type == "line") {
#      g <- g + geom_line(aes_string(x = var$date[var$test == test], y = var$value[var$test == test]), colour = colour_along) + 
#        geom_line(aes_string(x = var$date[var$test == test], y = var$value[var$test == "conf_low"]), colour = colour_along, linetype = 2) +  
#        geom_line(aes_string(x = var$date[var$test == test], 
#                             y = var$value[var$test == "conf_high"]), colour = colour_along, linetype = 2) +
#        x_axis_format
#      colour_along <- NA
#    }
#   }
#  } else {
    plot_aes <- aes_string(x = var[['coord']], y = var$value, 
                           group = interaction(var[, group_dims]))
    if (!is.na(colour_along)) {
      plot_aes[['colour']] <- var[, colour_along]
    }
    if (!is.na(style_along)) {
      plot_aes[['linetype']] <-  var[, style_along]
    }
    if (!is.na(shape_along) && points) {
      plot_aes[['shape']] <-  var[, shape_along]
    }
    if (is.na(curves_along) && !points) {
      print("Parameter 'curves_along' set to NA and 'points' set to FALSE. Nothing will be drawn.")
    }
    if (!is.na(curves_along)) {
      g <- g + geom_line(plot_aes, size = 0.4)
    } 
    if (points) {
      g <- g + geom_point(data = var, plot_aes, size = 2)
    }
#
#    # Draw mean line
#    if (!is.na(mean_along)) {
#      g <- g + stat_summary(data = var, fun.y= "mean", mapping = aes_string(x = var$date, y = var$value,
#                                                                            group = var[, mean_along], 
#                                                                            colour = var[, mean_along], 
#                                                                            fill = var[, mean_along]),
#                            geom = "line", size = 1, show.legend = FALSE)
#    }   
#    if (is.null(minmax_along) & !is.na(mean_along)) {
#      minmax_along <- mean_along
#    }
#    if (!is.na(minmax_along)) {
#      g <- g +  stat_summary(data = var, geom = "ribbon", fun.ymin = "min", fun.ymax = "max", aes(x = var$date, y = var$value, group = var[, minmax_along], 
#                                                                                                  colour = var[, minmax_along], fill = var[, minmax_along]), alpha = 0.3, show.legend = FALSE)
#    }
#  }
#  
#  if (!add) {
#    if (is.numeric(intercept)) {
#      g <- g + geom_hline(yintercept = intercept, size = 0.3, linetype = 2)
#    }
#  }
#  g <- g + labs(title = na.omit(title)) + xlab(xtitle) + ylab(ytitle) + 
#    theme(text=element_text(size = 16), legend.text=element_text(size = 16), 
#          axis.title=element_text(size = 16)) 
#  # Adjust scale for colour
#  
#  if (!is.na(colour_along)) {
#    g <- g + labs(colour = Convert2LongName(dimnames[colour_along]))
#    # If the dimension coloured along is the members
#    if (dimnames[colour_along] == ref_dimnames[4]) {
#      if (!is.null(metadata$Datasets$Realization)) {
#        labels <- metadata$Dataset$Realization
#      } else {
#        labels <- paste(ref_dimnames[4], 1:dims[colour_along])
#      }
#      # If the dimension coloured along is the sdates
#    } else if (dimnames[colour_along] == ref_dimnames[5]) {
#      if (!is.null(metadata$Dataset$TemporalPeriod$hasStartyear)) {
#        labels <- sdates
#      } else {
#        labels <- paste(ref_dimnames[5], 1:dims[colour_along])
#      }
#    }
#    g <- g + scale_colour_discrete(labels = labels)
#  }
#  if (!is.null(style_along)) {
#    if (dimnames[style_along] == ref_dimnames[4]) {
#      if (!is.null(metadata$Datasets)) {
#        labels <- metadata$Dataset$TemporalInstant$hasInitDate$Member_1
#      } else {
#        labels <- paste(ref_dimnames[4], 1:dims[style_along])
#      }
#      # If the dimension styled along is the sdates
#    } else if (dimnames[style_along] == ref_dimnames[5]) {
#      if (!is.null(metadata$Dataset$TemporalPeriod$hasStartyear)) {
#        labels <- sdates
#      } else {
#        labels <- paste(ref_dimnames[5], 1:dims[style_along])
#      } 
#    }
#    g <- g + scale_linetype_discrete(Convert2LongName(dimnames[style_along]),labels = labels)
#  }
#  if (!is.null(shape_along)) {
#    if (dimnames[shape_along] == ref_dimnames[4]) {
#      if (!is.null(metadata$Datasets)) {
#        labels <- metadata$Dataset$TemporalInstant$hasInitDate$Member_1
#      } else {
#        labels <- paste(ref_dimnames[4], 1:dims[shape_along])
#      }
#      # If the dimension shaped along is the sdates
#    } else if (dimnames[shape_along] == ref_dimnames[5]) {
#      if (!is.null(metadata$Dataset$TemporalPeriod$hasStartyear)) {
#        labels <- sdates
#      } else {
#        labels <- paste(ref_dimnames[5], 1:dims[shape_along])
#      }
#    }
#    g <- g + scale_shape_discrete(Convert2LongName(dimnames[shape_along]),labels = labels)
#  }
#  
  # X axis
  if (is_time_x_axis) {
    g <- g + scale_x_datetime(date_labels = "%b\n%04Y",
                              breaks = unique(coords[[x_axis_dim]]))
                              #breaks = function(x) seq(fr = x[1], to = x[2], 
                              #                         by = "1 year", expand = c(0, 0)))
  } else {
    g <- g + scale_x_continuous(labels = x_coord_copy[unique(var$coord)],
                                breaks = unique(var$coord))#breaks = unique(coords[[x_axis_dim]]), 
                              #labels = unique(coords[[x_axis_dim]]))
  }
  # Titles and labels
  if (!is.null(names(dims))) {
    x_axis_title <- names(dims)[x_axis_dim]
  } else {
    if (is_time_x_axis) {
      x_axis_title <- 'time'
    } else {
      x_axis_title <- 'x'
    }
  }
  g <- g + labs(title = "Title here") +
           labs(subtitle = "\nSubtitle here\nSubtitle here too") +
           labs(x = x_axis_title, y = 'Variable name here') + 
           theme(plot.title = element_text(hjust = 0.5, size = 18, vjust = -1)) +
           theme(plot.subtitle = element_text(hjust = 0.5, size = 15)) +
           theme(panel.border = element_rect(colour = "black", fill = NA, size = 1))

  # Legends
  # Colour legend
  draw_colour_scale <- TRUE
  if (!is.na(colour_along) && draw_colour_scale) {
    g <- g + scale_colour_manual(name = names(dims)[colour_along],
                                 values = 1:length(coords[[colour_along]]),
                                 labels = coords[[colour_along]])
  } else {
    g <- g + guides(colour = FALSE)
  }
  # Line style legend
  if (!is.na(curves_along)) {
    draw_style_scale <- TRUE
    if (!is.na(style_along) && draw_style_scale) {
      g <- g + scale_linetype_manual(name = names(dims)[style_along],
                                     values = 1:length(coords[[style_along]]),
                                     labels = coords[[style_along]])
    } else {
      g <- g + guides(linetype = FALSE)
    }
  }
  # Point shape legend
  if (points) {
    draw_shape_scale <- TRUE
    if (!is.na(shape_along) && draw_shape_scale) {
      g <- g + scale_shape_manual(name = names(dims)[shape_along],
                                  values = 1:length(coords[[shape_along]]),
                                  labels = coords[[shape_along]])
    } else {
      g <- g + guides(shape = FALSE)
    }
  }

  # Default style options
  g <- g + theme(axis.text = element_text(size = 15), 
                 axis.title = element_text(size = 18))

  # Return ggplot2 object or save to file
  if (!is.null(file_name)) {
    # Save to file
    g <- ggsave(filename = file_name, 
                plot = g,
                width = file_width, 
                height = file_height, 
                units = file_size_units)
    invisible(NULL)
  } else {
    class(g) <- c('s2dv_ts_plot', class(g))
    g
  }
}
