#' Build a parameter list for the mobility-based SEAIR model
#'
#' Constructs and validates the list of transmission and stage-progression
#' parameters used by the mobility-based SEAIR model
#' \eqn{\partial_t S = -\beta m S \mathcal{K}},
#' \eqn{\partial_t E = \beta m S \mathcal{K} - \sigma E},
#' \eqn{\partial_t A = \sigma E - (\kappa + \gamma_A) A},
#' \eqn{\partial_t I = \kappa A - \gamma_I I},
#' \eqn{\partial_t R = \gamma_A A + \gamma_I I},
#' where
#' \eqn{\mathcal{K}(A,I) = \int_0^1 \bar{m}\,(\alpha A(\bar{m}) + \delta I(\bar{m}))\,d\bar{m}}.
#'
#' @param beta Transmission rate \eqn{\beta > 0}.
#' @param sigma Rate \eqn{\sigma > 0} of progression from latent (E) to
#'   asymptomatic (A).
#' @param kappa Symptom-onset rate \eqn{\kappa > 0} (A to I).
#' @param gamma_A Recovery rate \eqn{\gamma_A > 0} from the asymptomatic
#'   compartment.
#' @param gamma_I Recovery rate \eqn{\gamma_I > 0} from the symptomatic
#'   compartment.
#' @param alpha Relative infectiousness \eqn{\alpha \in [0,1]} of the
#'   asymptomatic stage.
#' @param delta Mobility-reduction factor \eqn{\delta \in (0,1]} for the
#'   symptomatic stage.
#'
#' @return A list of class \code{"seair_params"} containing the checked
#'   parameters.
#'
#' @examples
#' seair_params(beta = 1.5, sigma = 0.3, kappa = 0.2,
#'              gamma_A = 0.1, gamma_I = 0.13,
#'              alpha = 0.5, delta = 0.3)
#'
#' @export
seair_params <- function(beta, sigma, kappa, gamma_A, gamma_I,
                         alpha = 1, delta = 1) {
  stopifnot(
    is.numeric(beta),    length(beta)    == 1, beta    > 0,
    is.numeric(sigma),   length(sigma)   == 1, sigma   > 0,
    is.numeric(kappa),   length(kappa)   == 1, kappa   > 0,
    is.numeric(gamma_A), length(gamma_A) == 1, gamma_A > 0,
    is.numeric(gamma_I), length(gamma_I) == 1, gamma_I > 0,
    is.numeric(alpha),   length(alpha)   == 1, alpha >= 0, alpha <= 1,
    is.numeric(delta),   length(delta)   == 1, delta >  0, delta <= 1
  )
  out <- list(beta = beta, sigma = sigma, kappa = kappa,
              gamma_A = gamma_A, gamma_I = gamma_I,
              alpha = alpha, delta = delta)
  class(out) <- "seair_params"
  out
}

#' Build initial conditions for the SEAIR system
#'
#' Constructs initial profiles \eqn{(S_0, E_0, A_0, I_0, R_0)} on the
#' mobility grid from a user-supplied mobility density \eqn{f(m)} and a
#' seed fraction \eqn{I_{\mathrm{seed}}} placed in the symptomatic
#' compartment.  The initial condition is "proportional":
#' \eqn{S_0 = (1 - I_{\mathrm{seed}}) f},
#' \eqn{I_0 = I_{\mathrm{seed}} f},
#' \eqn{E_0 = A_0 = R_0 = 0}.
#'
#' @param m_grid Numeric vector of mobility grid points in \eqn{[0,1]}.
#' @param f_vals Numeric vector of densities \eqn{f(m)} evaluated at
#'   \code{m_grid}.  It is renormalised internally so that the
#'   trapezoidal integral over \code{m_grid} equals 1.
#' @param I_seed Non-negative fraction of the population seeded in the
#'   symptomatic compartment (default \code{1e-4}).
#'
#' @return A list of class \code{"seair_init"} with entries
#'   \code{m_grid}, \code{f}, \code{S}, \code{E}, \code{A}, \code{I},
#'   \code{R}, and \code{I_seed}.
#'
#' @examples
#' m <- seq(0, 1, length.out = 101)
#' f <- dbeta(m, 2, 2)
#' seair_init(m, f, I_seed = 1e-4)
#'
#' @export
seair_init <- function(m_grid, f_vals, I_seed = 1e-4) {
  stopifnot(is.numeric(m_grid), is.numeric(f_vals),
            length(m_grid) == length(f_vals),
            length(m_grid) >= 3,
            all(is.finite(m_grid)), all(is.finite(f_vals)),
            all(m_grid >= 0), all(m_grid <= 1), all(diff(m_grid) > 0),
            all(f_vals >= 0),
            is.numeric(I_seed), length(I_seed) == 1,
            I_seed >= 0, I_seed < 1)

  total <- .trap(m_grid, f_vals)
  if (total <= 0) stop("f_vals integrates to zero; check your density.")
  f <- f_vals / total

  S <- (1 - I_seed) * f
  I <- I_seed       * f
  E <- A <- R <- rep(0, length(m_grid))

  out <- list(m_grid = m_grid, f = f,
              S = S, E = E, A = A, I = I, R = R,
              I_seed = I_seed)
  class(out) <- "seair_init"
  out
}

## Trapezoidal rule on a (possibly non-uniform) grid.
.trap <- function(x, y) {
  n <- length(x)
  if (n < 2) return(0)
  sum((x[-1] - x[-n]) * (y[-1] + y[-n])) / 2
}
