forecastByDateStore <- function(DT.GL_gross_forDS, datesToForecast, byCols=kCols.datestore) {
## DT.GL_gross_forDS  all dates should be (date <= lastDateClosed)
## returns a DT with a row for each datesToForecast-byCols_nodate value combination and a forecasted value

  suppressPackageStartupMessages(library(forecast))

  if (!is.data.table(DT.GL_gross_forDS))
    stop("DT.GL_gross_forDS must be a data.table")
  if ("date" %ni% names(DT.GL_gross_forDS))
    stop("DT.GL_gross_forDS must have a column named date.")

  ## How many values to forecast (is the number of dates to forecast)
  n.forecast <- length(datesToForecast)

  ## Remove date from byCols
  byCols_nodate <- setdiff(byCols, "date")

  ## Calculate the number of expected rows, for controlling at end
  expectedRows <- n.forecast * nrow(unique(DT.GL_gross_forDS, by=byCols_nodate))

  ## debugging
  browser(expr=inDebugMode("forecast"), text="in forecastByDateStore() right before executing forecast")

  ## EXECUTE FORECAST
  ret <- {
           DT.GL_gross_forDS[date >= firstSCdate, list(GL_gross_forDS=tail(GL_gross_forDS, 8)), keyby=byCols
                            ][, {
                                  forecasts <- forecast(GL_gross_forDS, n.forecast)
                                  list(   date=datesToForecast
                                        , forecasted_value = c(forecasts$mean)
                                        , forecasted_lo80 = forecasts$lower[, 1]
                                        , forecasted_lo95 = forecasts$lower[, 2]
                                        , forecasted_hi80 = forecasts$upper[, 1]
                                        , forecasted_hi95 = forecasts$upper[, 2]
                                        )
                                }
                              , keyby=byCols_nodate]
         }

  ## ERROR CHECK
  if (nrow(ret) != expectedRows)
    {mc <- match.call(); warning(sprintf("The forecast maybe wrong.  We expected %i rows and instead received %i. The match.call() is \n    %s", expectedRows, nrow(ret), pasteC(capture.output(mc), C=" ")))}

  ## TODO:  INCORPORATE OA 
  ## If there was a steady increase in OA, apply that increase
  if (FALSE)  # ABANDONING THIS PART FOR NOW
  if (exists("DT.split_percs.unexpanded")) {
    ## we will include the last lastDateClosed
    datesPlusLast <- sort(c(lastDateClosed, datesToForecast))
    stopifnot(!anyDuplicated(datesPlusLast))

    tmp_DT.OA <- setkeyIfNot(copy(DT.split_percs.unexpanded), byCols_nodate, superset.ok=TRUE, organize=TRUE, verbose=FALSE)
    ## subset the tmp_DT.OA 
    tmp_DT.OA_subset <- tmp_DT.OA[unique(ret)][!is.na(OA_gross_forDSOM), unique(.SD, by=NULL), .SDcols=byCols]
    ## .. crop even further, drop any stores where not all dates are present
    tmp_DT.OA_subset[, keep := all(datesPlusLast %in% date), by=byCols_nodate]
    tmp_DT.OA_subset <- unique(tmp_DT.OA_subset[(keep)][, keep := NULL], by=byCols_nodate)

    ## execute the crop
    tmp_DT.OA <- tmp_DT.OA[tmp_DT.OA_subset]

    ## Sum up the DSOM gross by DS
    tmp_DT.OA <- tmp_DT.OA[date %in% datesPlusLast, list(OA_gross_forDS = sumn(OA_gross_forDSOM)), keyby=byCols]

    ## If the monthly total is relatively close to 0, treat as NA, so as to not mess up the percIncr
    ##  Note that if that is the true value, if there are several in a row (ie, there is a real trend), then they have very little impact on the bottom line
    ##     whereas if it is an anomoly but a real value, it is nevertheless an outlier (ie, no trend that is immediately useful in forecastin)
    tmp_DT.OA[equals0(OA_gross_forDS, tol=10), OA_gross_forDS := NA]

    ## CALUCLATE THE % OF INCREASE, Month-on-Month
    tmp_DT.OA[, percIncr := percentIncrease(OA_gross_forDS), by=byCols_nodate]

    ## Calculate the increase of the increase
    tmp_DT.OA[, percIncr_of_percIncr := percentIncrease(percIncr), by=byCols_nodate]

    tmp_DT.OA[!is.na(percIncr_of_percIncr), fwp(percIncr_of_percIncr), by=byCols_nodate]
    tmp_DT.OA[!is.na(percIncr), fwp(percIncr), by=byCols_nodate]

    "Abandoning this for now, as I am not convinced this is properly lined up with the GL values, so not really meaningful.
    Especially since the last month is already a predicted value (ie, gest)"
  }

  ## Return with key set
  setkeyIfNot(ret, byCols, organize=TRUE, verbose=FALSE)
}