#'Generate samples of parameters from prior distribution
#'
#'Methods to generates samples of the parameters from the prior distribution of the ensemble model.
#'
#'@param priors An `EnsemblePrior` object specifying the prior distributions for the ensemble.
#'@param M A `numeric` that represents the number of simulators. The default is 1.
#'@param MM A `numeric` that represents the number of drivers. The default is `NULL`.
#'@param full_sample A `logical` that runs a full sampling of the prior density of the ensemble model if `TRUE`. If `FALSE`, returns the point estimate which maximises the prior density of the ensemble model.
#'@param control If creating a full sample, this is a named `list` of parameters to control Stan's sampling behaviour. See the documentation of the `stan()` function in the `rstan` package for details. The default value is `list(adapt_delta = 0.95)`. If optimizing, this value is ignored.
#'@param ... Additional arguments passed to the function \code{rstan::sampling} or  \code{rstan::optimizing}.
#'@return A `list` containing two items named `samples` and `point_estimate`. If `full_sample==TRUE`, `samples` is a `stanfit` and `point_estimate` is a `NULL` object, else `samples` is a `NULL` and `point_estimate` is a `list` object. It is possible to generate a point estimate for the prior if the individual short-term discrepancy prior follows a hierarchical parameterisation.
#'@references Stan Development Team (2020). RStan: the R interface to Stan. R package version 2.21.2. https://mc-stan.org
#'@seealso \code{\linkS4class{EnsembleFit}}
#'@export
#'@examples
#'\donttest{
#' priors <- EnsemblePrior(4)
#' prior_density <- prior_ensemble_model(priors, M = 4)
#' }
prior_ensemble_model <- function(priors, M = 1, MM = NULL,
                                    full_sample = TRUE, control = list(adapt_delta = 0.95), ...){
  stan_input <- priors@priors_stan_input
  stan_input$M <- M
  stan_input$N <- priors@d

  #Using hierarchical priors uses a different model. This speeds up the sampling enormously

  if (!is.null(MM)){
    stan_input$MM <- MM
    mod <- stanmodels$ensemble_prior_withdrivers
    if(stan_input$form_prior_ind_st == 3 || stan_input$form_prior_ind_st == 4){
      if(full_sample==FALSE){
        stop("It is possible to generate a point estimate for the prior if the individual short-term discrepancy prior follows a hierarchical parameterisation. Please generate a full sample using 'full_sample=TRUE'.")
      }
      mod <- stanmodels$ensemble_prior_hierarchical_withdrivers
    }
  }else{
    mod <- stanmodels$ensemble_prior
    if(stan_input$form_prior_ind_st == 3 || stan_input$form_prior_ind_st == 4){
      if(full_sample==FALSE){
        stop("It is possible to generate a point estimate for the prior if the individual short-term discrepancy prior follows a hierarchical parameterisation. Please generate a full sample using 'full_sample=TRUE'.")
      }
      mod <- stanmodels$ensemble_prior_hierarchical
    }
  }

  samples <- NULL; point_estimate <- NULL
  if(full_sample){
    samples <- rstan::sampling(mod, data=stan_input, control = control, ...)
  }else{
    point_estimate <- rstan::optimizing(mod, data=stan_input,as_vector=FALSE, ...)
  }
  return(list(samples=samples, point_estimate=point_estimate))
}




#'Generate samples of latent variables from prior predictive distribution
#'
#'Methods to generates samples of the latent variables from the prior predictive distribution of the ensemble model.
#'
#'@inheritParams EnsembleData
#'@param full_sample A `logical` that runs a full sampling of the prior density of the ensemble model if `TRUE`. If `FALSE`, returns the point estimate which maximises the prior density of the ensemble model.
#'@param sam_priors A `list` containing two items named `samples` and `point_estimate`. `samples` is either a `NULL` or a `stanfit` object containing the samples drawn from the prior distribution of the ensemble model and `point_estimate` is either a `NULL` or a `list` object containing the optimised prior distribution of the ensemble model. If this object is `missing` then `sample_prior` generates it.
#'@param num_samples A `numeric` specifying the number of samples to be generated. The default is 1.
#'@param ... Additional arguments passed to the function \code{rstan::sampling} or  \code{rstan::optimizing}.
#'@inherit generate_sample details
#'@return An `EnsembleSample` object.
#'@references Durbin, J. and Koopman, S. J. (2002). "A simple and efficient simulation smoother for state space time series analysis." Biometrika, 89(3), 603--616.
#'@references Chris M.Strickland, Ian. W.Turner, RobertDenhamb, Kerrie L.Mengersena. Efficient Bayesian estimation of multivariate state space models Computational Statistics & Data Analysis Volume 53, Issue 12, 1 October 2009, Pages 4116-4125
#'@seealso \code{\linkS4class{EnsembleFit}}, \code{\link{EnsembleSample}}, \code{\link{generate_sample}}, \code{\link{prior_ensemble_model}}
#'@export
#'@examples
#'\donttest{
#' priors <- EnsemblePrior(4)
#' prior_density <- prior_ensemble_model(priors, M = 4)
#' samples <- sample_prior(observations = list(SSB_obs, Sigma_obs),
#'              simulators = list(list(SSB_miz, Sigma_miz),
#'                                list(SSB_ewe, Sigma_ewe),
#'                                list(SSB_fs, Sigma_fs),
#'                                list(SSB_lm, Sigma_lm)),
#'              priors = priors,
#'              sam_priors = prior_density)
#' plot(samples) #Plot the prior predictive density.
#' }
sample_prior <- function(observations, simulators, priors, sam_priors, num_samples = 1, full_sample = TRUE, drivers = FALSE,...){
  if(missing(sam_priors)){
    if (drivers == FALSE) {
      sam_priors <- prior_ensemble_model(priors, M=length(simulators),
                                         full_sample = full_sample, ...)
    }
    else {
      sam_priors <- prior_ensemble_model(priors, M=length(simulators), MM = max(sapply(simulators, function(x) {length(x[[1]])})),
                                         full_sample = full_sample, ...)
    }
  }
  if (drivers == FALSE) {
    ens_data <- EnsembleData(observations, simulators, priors)
  }
  else {
    ens_data <- EnsembleData(observations, simulators, priors, drivers = TRUE)
  }
  fit_prior <- EnsembleFit(ens_data, sam_priors$samples, sam_priors$point_estimate)
  return(generate_sample(fit_prior,num_samples = num_samples))
}

