# Do this in a separate file to see the generated help:
#library(devtools)
#document()
#load_all(as.package("../../onlineforecast"))
#?forecastmodel

#' R6 class for a forecastmodel
#' 
#' This class holds the variables and functions needed for defining and setting up a forecast model - independent of the fitting scheme.
#' See the vignettes on how to setup and use a model and the website \url{https://onlineforecasting.org} for more info.
#'
#' @title Class for forecastmodels
#' @name forecastmodel
#' @details
#' 
#' Holds all the information needed independently of the fitting scheme (e.g. lm_fit or rls_fit), see the fields and functions below.
#'
#' The fields are separated into:
#'   - Fields for setting up the model
#'   - Fields used when fitting (e.g. which horizons to fit for is set in \code{kseq}
#'
#' See the fields description below.
#' 
#' Note, it's an R6 class, hence an object variable is a pointer (reference), which means two important points:
#'  - In order to make a copy, the function clone_deep() must be used (usually \code{clone(deep=TRUE)}, but that will end in an infinite loop).
#'  - It can be manimulated directly in functions (without return). The code is written such that no external functions manipulate the model object, except for online updating.
#'
#' For online updating (i.e. receiving new data and updating the fit), then the model definition and the data becomes entangled, since transformation functions like low-pass filtering with \code{\link{lp}()} requires past values.
#' See the vignette ??(ref to online vignette, not yet available) and note that \code{\link{rls_fit}()} resets the state, which is also done in all \code{xxx_fit} functions (e.g. \code{\link{rls_fit}}.
#'
#' 
#' @section Public fields used for setting up the model:
#'
#'     - output = NA, character: Name of the output.
#'
#'     - inputs = list(), add them with add_inputs(): List of inputs (which are R6 objects) (note the "cloning of list of reference objects" issue below in deep_clone function)
#'
#'     - regprmexpr = NA: The expression (as character) used for generating the regprm, e.g. "\code{\link{rls_prm}()}" for RLS.
#'
#'     - regprm = list(): Regression parameters calculated by evaluating the \code{regprmexpr}.
#'
#'     - prmbounds = as.matrix(data.frame(lower=NA, init=NA, upper=NA)): The bounds for optimization of the parameters, e.g. with \code{\link{rls_optim}()}.
#'
#'     - outputrange = NA, numeric vector of length 2: Limits of the predictions cropped in the range, e.g. outputrange = c(0,Inf) removes all negative output predictions.
#'
#'
#' @section Public fields used when the model is fitted:
#'
#'     - kseq = NA: The horizons to fit for.
#'
#'     - kseqopt = NA: The horizons to fit for when optimizing.
#' 
#'     - p = NA: The (transformation stage) parameters used for the fit.
#'
#'     - Lfits = list(): The regression fits, one for each k in kseq (simply a list with the latest fit).
#'
#'     - datatr = NA: Transformed input data (data.list with all inputs for regression)
#'
#'
#----------------------------------------------------------------
#' @section Public methods:
#' All public functions are described below and in examples a section for each is included:
#----------------------------------------------------------------

#----------------------------------------------------------------
#' @section \code{$new()}:
#' Create a new `forecastmodel` object.
#' 
#' Returns a forecastmodel object.
#' @examples
#' # New object
#' model <- forecastmodel$new()
#'
#' # Print it
#' model
#'
#----------------------------------------------------------------

#----------------------------------------------------------------
#' @section \code{$add_inputs(...)}:
#'         Add inputs to the model.
#'
#'         - \code{...}: The inputs are given as arguments, see examples.
#'
#' @examples
#'
#' # Add model inputs
#' model$add_inputs(Ta = "lp(Ta)")
#' # See it
#' model$inputs
#' # Update to use no low-pass filter
#' model$add_inputs(Ta = "Ta")
#' model$inputs
#' # Add another
#' model$add_inputs(I = "lp(I)")
#' model$inputs
#'
#' # Simply a list, so manipulate directly
#' class(model$inputs$Ta)
#' model$inputs$Ta$expr <- "lp(Ta, a1=0.9)"
#----------------------------------------------------------------

#----------------------------------------------------------------
#' @section \code{$add_regprm(regprm_expr)}:
#' Add expression (as character) which generates regression parameters.
#'
#' @examples
#'
#' # Add the parameters for the regression stage
#' model$add_regprm("rls_prm(lambda=0.99)")
#' # The evaluation is a list, which is set in
#' model$regprm
#' 
#----------------------------------------------------------------

#----------------------------------------------------------------
#' @section \code{$add_prmbounds(...)}:
#' Add the transformation parameters and bounds for optimization.
#'
#' @examples
#'
#' # Set the lambda to be optimized between 0.9 and 0.999, starting at 0.99
#' model$add_prmbounds(lambda = c(0.9, 0.99, 0.999))
#' # Note the "__" syntax to set parameters for inputs: "input__prm"
#' model$add_prmbounds(Ta__a1 = c(0.8, 0.95, 0.99))
#----------------------------------------------------------------

#----------------------------------------------------------------
#' @section \code{$get_prmbounds(...)}:
#' Get the transformation parameter bounds, used by optimization functions e.g. \code{\link{rls_optim}()}.
#'
#' @examples
#' 
#' # Get the lower bounds
#' model$get_prmbounds("lower")
#----------------------------------------------------------------

#----------------------------------------------------------------
#' @section \code{$insert_prm(prm)}:
#' Insert the transformation parameters prm in the input expressions and regression expressions, and keep them in $prm (simply string manipulation).
#'
#' @examples
#' 
#' # Insert the init parameters
#' prm <- model$get_prmbounds("init")
#' prm
#' # Before
#' model$inputs$Ta$expr
#' # After
#' model$insert_prm(prm)
#' model$inputs$Ta$expr
#----------------------------------------------------------------

#----------------------------------------------------------------
#' @section \code{$transform_data(data)}:
#' Function for transforming the input data to the regression stage input data (see \code{vignette("setup-data", package = "onlineforecast")}).
#'
#----------------------------------------------------------------

#----------------------------------------------------------------
#' @section \code{$reset_state()}:
#' Resets the input states and stored data for iterative fitting (datatr rows and yAR) (see ??(ref to online updating vignette, not yet available).
#'
#----------------------------------------------------------------

#----------------------------------------------------------------
#' @section \code{$check(data = NA)}:
#' Check if the model is setup correctly.
#'
#' @examples
#' 
#' # Check if the model is setup and can be used with a given data.list
#' # An error is thrown
#' try(model$check(Dbuilding))
#' # Add the model output
#' model$output <- "heatload"
#' # Still not error free
#' try(model$check(Dbuilding))
#' # Add the horizons to fit for
#' model$kseq <- 1:4
#' # Finally, no errors :)
#' model$check(Dbuilding)
NULL
# Don't delete the NULL above