#' Sort an Object according to the Keys
#'
#' @param x an object to be sorted.\cr
#' If x is a matrix, all the column will be sorted.
#' @param ... vectors to be uses as keys.\cr
#' Must be the same length as x.
#' @param descending = 1 to sort descendingly when ... is present.\cr
#' @param cols vector consisting of the column numbers of x
#' which will be used as keys.\cr
#' This has priority over ... .
#'
#' @details
#' In order to sort descendingly using cols argument,
#'  specify negative column number in cols= \cr
#' or use - sign in numeric ... ariguments. \cr
#' Or, use descending=1 option.
#'
#' @return Object of the same size as x.
#'
#' @examples
#' # sort the rows of x according to the order of ... .
#' sortit( demomat(4,2), c(4,3,1,2) )
#' sortit( demomat(4,2), c(4,3,1,2), c("a","a","b","b") )
#'
#' # sort the rows of x using the colums specified in cols= as keys.
#' sortit( diag(4), cols=1:4 )
#'
#' # sorting the rows of a matrix
#' X <- as.matrix( expand.grid( 1:2, 3:5 ) )
#' colnames(X)=c("xx1","xx2")
#' sortit( X, cols=c(1,2) )
#' Print( sortit( X, cols=c(1,-2) ), sortit( X, X[,1], -X[,2] ) )
#'
#' # the last column first or the first column first.
#' Print( sortit( X, cols=1:2 ), sortit( X, cols=2:1 ) )
#'
#' # sort the result of unique function
#' set.seed(1701)
#' Y <- matrix( sample(letters[1:3],99, replace=1),,3 )
#' Yu <- unique(Y)
#' Print( Yu, sortit(Yu, cols=1:3) )
#' Print( sortit(Yu, Yu[, 1], Yu[, 2], Yu[, 3]) )
#' Print( Yu, sortit(Yu, cols=-(1:3)) )
#' Print( sortit(Yu, Yu[, 1], Yu[, 2], Yu[, 3], descending=1) )
#'
#' @export
#'

sortit <- function( x, ..., descending=0, cols=NULL ){
 # sort x by multiple keys and return the sorted x
 # 20111123,24,25
 # drop=F -> drop=0: 130110
 # as.logical(desceinding): 20140510
 # cols: 20140510
 # descending bug fix: 20140520
 # vector x: 20141217,18
 # bugfix for cols: 20150825
 # roxgen comments: 20161104c,1121,20170917
 # bugfix for R4.2.1: 20220806
 #
 # Args:
 #  x          an object whose rows will be sorted
 #             When x is a matrix, all the columns will be sorted.
 #  ...        list of the keys
 #             These are NOT key names/numbers, but values.
 #             Each key must have the same length as nrow(x)
 #            ... can be NULL when sorting vector x by its value.
 #  descending = 1 to sort descendingly for all the keys when ... is given.
 #
 #  cols     = list of columns of x to be used as keys
 #             negative column number means descending sort for that key
 #             cols has priority over ... .
 #
 #

 # is x a vector? 20141217,18
 isvec=is.vector(x)
 # if( isvec ){
 #  if( is.null(cols) ) cols=1
 #  if( cols < 0 ) descending=1
 #  return( sort(x,decreasing=as.logical(descending)) )
 # }

 # is cols given?
 if( !is.null(cols) ){
  # name=deparse(substitute(x))
  name="x"   # 20150825
  #Print(name, cols)
  loc=which( cols < 0 )
  cols=abs(cols)
  b=paste(name,"[,",cols,"]", sep="")
  bs=paste("length(",name,"[,",cols,"])-"
           ,"rank(",name,"[,",cols,"], ties.method='min')"
           , sep="")
  #Print(b,bs, size(b),loc)
  if( length(loc) > 0 ){
   b[loc]=bs[loc]
  }
  b=paste(b,collapse=",")
  #Print(b)
  code=paste("od=order(",b,")")
  # Print(code)
  eval(parse(text=code))
  # sort x
  if ( isvec ){
   x=x[od]
  }
  else{
   x=x[od,,drop=0]
  }
  return(x)
 } # end of cols != NULL

 # Here key is given as ...

 # process ... argument
 value=list(...)
 name=as.character( as.list(substitute(list(...)))[-1L] )
 #Print(name,value,names(value))
 nkeys=length(name)
 lenv=as.numeric(lapply(value,length))
 #Print(name,value,nkeys,lenv,descending)


 # vec or matrix/data.frame
 if ( isvec ) len=length(x)
 else len=nrow(x)

 # error check
 if( any( len != lenv ) ){
  cat("\n\nerror:(sortit) The length of the body and the key(s) differ. \n\n")
  Print(len,lenv)
  return( NULL )
 }

 # no key case
 if ( nkeys == 0 ){
  if ( is.matrix(x) && ncol(x) > 1 ){
   cat("\n\nerror:(sortit) To sort a matrix, a key must be given. \n\n")
   return( NULL )
  }
  else{
   if( is.matrix(x) ) return(
    matrix(sort(x,decreasing=as.logical(descending)),,1) )
   else return( sort(x,decreasing=as.logical(descending)) )
  }
 }

 # find order and take care of descending option
 dec=rep(0,nkeys)
 if( descending ) dec=rep(1,nkeys)
 od=order(..., decreasing=descending)

 # sort x
 if ( isvec ) x=x[od] else x=x[od,,drop=0]

 return(x)

} # end of sortit


