#' Calculate the Spectral Principal Components Filters Spatial Functional Data
#'
#' This function calculates the SPC filters from a given spectral density
#' operator.
#' @param FS the spectral density.
#' @param Npc the number of principal components to be computed.
#' @param L the maximum lag for the filters, as an integer or vector of
#'   integers.
#' @return the SPC filters.
#' @seealso \code{\link{fsd.spca}}
#' @details The eigenvectors used for the calculation of the Fourier expansion
#'   are oriented such that the sum of their coordinates over the basis of the
#'   fd object (i.e. the entries in the \code{coefs} array) is a non-negative
#'   real number. If the basis is not orthonormal, this is done with respect to
#'   a virtual orthonormal basis in the background.
#' @keywords fsd
#' @export

fsd.spca.filters = function (FS, Npc = 1, L = 3)
{
  if (!inherits(FS, "fsd.freqdom"))
    stop("FS needs to be a spectral density operator")

  L = floor(L)

  if (length(L) == 1)
    L = rep(L, length(FS$freq[[1]]))

  if (length(L) != length(FS$freq[[1]]))
    stop("Dimensions incompatible")

  dims = dim(FS$operators[[1]])[1]
  Npc = min(Npc, dims)

  if (!is.null(FS$basis)) {
    B = inprod(FS$basis, FS$basis)
  } else {
    B = diag(dim(FS$operators[[1]])[1])
  }
  B.root = eigen(B)$vectors %*% diag(sqrt(eigen(B)$values)) %*%
    t(eigen(B)$vectors)
  B.root.minus = solve(B.root)
  for (i in 1:length(FS$operators)) {
    FS$operators[[i]] = B.root %*% FS$operators[[i]] %*% B.root
  }

  Esys = lapply(FS$operators, eigen, symmetric = TRUE)
  Evecs = sapply(Esys, function(w) {w$vectors[ ,1:Npc, drop=FALSE]},
                 simplify="array")

  Evals = sapply(Esys, '[[', "values", simplify="array")
  Evals.int = apply(pmax(Re(Evals), 0), 1, sum)
  names(Evals.int) = paste("PC", 1:dim(Evals)[1])

  sfpca.var = Evals.int/sum(Evals.int)

  v = rep(1, dims)
  phiv = apply(Evecs, 2:3, "%*%", v)
  phiv[abs(phiv) < 1e-14] = 1
  phiv = phiv/abs(phiv)

  Evecs = Evecs / rep(phiv, each = dims)

  laglist = unfold(lapply(L, function(h) {-h:h}))

  filters = fsd.fourier.inverse(Evecs, FS$freq, laglist)

  for (i in 1:length(filters$operators)) {
    filters$operators[[i]] = B.root.minus %*% filters$operators[[i]]
  }

  filters$basis = FS$basis

  filters$var = sfpca.var

  phinorms = sapply(filters$operators, function(M) {diag(t(M) %*% B %*% M)})

  if (Npc == 1)
    phinorms = sum(phinorms)
  else
    phinorms = apply(phinorms, 1, sum)

  if (any(phinorms < 0.9))
    warning("The chosen clipping parameter L is low.\n",
            "Summed squared norms for the PC filters are only ",
            toString(round(phinorms, 3)))

  filters
}
