#' Rotation of Atomic Coordinates
#' 
#' Rotation of atomic coordinates around a given vector.
#' 
#' \code{R} is a generic function. Method for objects of class \sQuote{coords}
#' first convert the coordinates into Cartesian coordinates using \code{crystal}
#' if needed. Once rotated, the coordinates are reconverted back to the original
#' basis set using again \code{crystal}. Method for objects of class \sQuote{pdb}
#' first extracts coordinates from the object using the function \code{coords},
#' performs the rotation, and updates the coordinates of the \sQuote{pdb} object
#' using the function \code{coords<-}.
#' 
#' @return An object of the same class as \code{x} with rotated coordinates.
#' 
#' @param obj an R object containing atomic coordinates.
#' @param angle the angle of the rotation in degrees.
#' @param x the x-component of the rotation vector.
#' @param y the y-component of the rotation vector.
#' @param z the z-component of the rotation vector.
#' @param mask a logical vector indicating the set of coordinates to which the rotation has to be applyed.
#' @param crystal an object of class \sQuote{crystal} used to convert fractional into Cartesian coordinates (when needed).
#' @param \dots further arguments passed to or from other methods.
#' 
#' @seealso 
#' Helper functions for rotation around a given Cartesian vector:\cr
#' \code{\link{Rx}}, \code{\link{Ry}}, \code{\link{Rz}}\cr
#' Passing from Cartesian to fractional coordinates (or Vis Versa):\cr
#' \code{\link{xyz2abc}}, \code{\link{abc2xyz}}
#' 
#' @examples 
#' # First lets read a pdb file
#' x <- read.pdb(system.file("examples/PCBM_ODCB.pdb", package="Rpdb"))
#' cell <- cell.coords(x)
#' visualize(x, mode = NULL)
#'
#' # Rotation of the structure around the c-axis
#' visualize(R(x, 90, x=cell["x","c"], y=cell["y","c"], z=cell["z","c"]),
#'           mode = NULL)
#' # Rotation of the residue 1 around the c-axis
#' visualize(R(x, 90, x=cell["x","c"], y=cell["y","c"], z=cell["z","c"], mask = x$atoms$resid == 1),
#'           mode = NULL)
#'           
#' @keywords manip
#' 
#' @name rotation
#' @export
R <- function(...)
  UseMethod("R")

#' @rdname rotation
#' @export
R.coords <- function(obj, angle = 0, x = 0, y = 0, z = 1,
		mask = TRUE, crystal = NULL, ...) {
	if(! is.coords(obj)) stop("'obj' must be an object of class 'coords'");

  if(length(mask) != natom(obj)){
    if(length(mask) != 1)
      warning("'mask' has been recycled")
    mask <- rep(mask, length = natom(obj))
  }
  
  basis.ori <- basis(obj)
  if(basis.ori != "xyz"){
    if(is.null(crystal))
      stop("Please specify a 'crystal' obj to convert the fractional coordinates into Cartesian")
    obj <- abc2xyz(obj, crystal = crystal);
  }
  M <- rgl::rotationMatrix(angle=angle*pi/180, x = x, y = y, z = z)[1:3,1:3]
  obj[mask,] <- coords(as.matrix(obj[mask,])%*%M, basis = "xyz")
	if(basis.ori != "xyz")
		obj = xyz2abc(obj, crystal = crystal);
	return(obj)
}

#' @rdname rotation
#' @export
R.pdb <- function(obj, angle = 0, x = 0, y = 0, z = 1, mask = TRUE,
		crystal = obj$crystal, ...) {
	if(! is.pdb(obj)) stop("'obj' must be an object of class 'pdb'");
	
	coords(obj) = R(coords(obj), angle = angle, x = x, y = y, z = z,
		mask = mask, crystal = crystal, ...);
	
	return(obj)
}
