##PLEASE PUT THE CLASSES FIRST!!!! THEN THE METHODS
#
# the class structure has been changed.  labels and scores
# are fundamental and can have different structures for
# different procedures.  so
# virual classes are defined with specializations to
#   a) class labels (as in classification outputs) vs
#       group indices (as in clustering outputs)
#   b) probability matrices (as with nnet predict) vs
#       vector scores (as in knn voting proportions)
#
#


setOldClass("dist")
setClass("MLLabel", "VIRTUAL")
setClass("predClass", contains=c("MLLabel", "character", "factor"),
	prototype=prototype(""))
setClass("groupIndex", contains=c("MLLabel", "integer"),
	prototype=prototype(integer(0)))
newPredClass <- function(x) new("predClass", x)
newGroupIndex <- function(x) new("groupIndex", x)

setClass("MLScore", "VIRTUAL")
setClass("probMat", contains=c("MLScore", "matrix"))
setClass("probArray", contains=c("MLScore", "array"))
setClass("membMat", contains=c("MLScore", "matrix"))
setClass("qualScore", contains=c("MLScore", "numeric"))
setClass("silhouetteVec", contains=c("MLScore", "numeric"))
newProbMat <- function(x) if(length(x)>0)new("probMat", x) else new("probMat")
newProbArray <- function(x) if(length(x)>0)new("probArray", x) else new("probArray")
newMembMat <- function(x) if(length(x)>0)new("membMat", x) else new("membMat")
newQualScore <- function(x) if(length(x)>0)new("qualScore", x) else new("qualScore")
newSilhouetteVec <- function(x) if(length(x)>0)new("silhouetteVec", x) else new("silhouetteVec")

setClass("MLOutput", representation(method="character",
			RObject="ANY", call="call", distMat="dist"), "VIRTUAL")

setClass("classifOutput", representation(
	predLabels="MLLabel", predScores="MLScore", predLabelsTr="MLLabel",
	trainInds="integer", allClass="character"), contains="MLOutput",
		prototype=prototype(method="", RObject=NULL,
			call=new("call"), distMat=dist(0), 
			allClass=character(0), trainInds=integer(0),
			predLabels=newPredClass(character(0)),
			predLabelsTr=newPredClass(character(0)),
			predScores=newQualScore(numeric(0))))

setClass("clustOutput", representation(
	clustIndices="MLLabel", clustScores="MLScore"), contains="MLOutput",
		prototype=prototype(method="", RObject=NULL,
			call=new("call"), distMat=dist(0),
			clustIndices=newGroupIndex(integer(0)),
			clustScores=newSilhouetteVec(numeric(0))))

########################################################

setMethod("show", "probMat", function(object) {
	if (length(object)>0) {
		cat("summary of class membership probabilities:\n")
		print(apply(object,2,summary))
	}
})
setMethod("show", "probArray", function(object) {
	cat("dimensions of (threshold-based) class membership probabilities:\n")
	print(dim(object))
})
setMethod("show", "membMat", function(object) {
	cat("summary of cluster membership scores:\n")
	print(apply(object,2,summary))
})
setMethod("show", "qualScore", function(object) {
   if (length(object)>0)
	{
	cat("summary of class assignment quality scores:\n")
	print(summary(object))
        }
   else invisible(NULL)
})
setMethod("show", "silhouetteVec", function(object) {
	cat("summary of clustering silhouette values:\n")
	print(summary(object))
})

#
# the base output representation class now just knows
# about the MLLabel and MLScore output classes,
# but now retains call, fitted model object, and dist
#
setGeneric("RObject", function(obj) standardGeneric("RObject"))
setMethod("RObject", "MLOutput", function(obj) obj@RObject)
setGeneric("distMat", function(obj) standardGeneric("distMat"))
setMethod("distMat", "MLOutput", function(obj) obj@distMat)



setMethod("show", "MLOutput", function(object) {
	cat("MLOutput instance, method=", object@method, "\n")
	if (object@method == "nnet")
		print(object@RObject)
	if (length(object@call)>0) {cat("Call:\n "); print (object@call)}
        if (is(object, "classifOutput") && length(object@predLabels)>0) {
		cat("predicted class distribution:")
		print(table(object@predLabels))
		show(object@predScores)
	}
        else if (is(object, "clustOutput") && length(object@clustIndices)>0) {
		cat("predicted cluster size distribution:")
		print(table(object@clustIndices))
		show(object@clustScores)
	}
})

setGeneric("predLabels", function(obj) standardGeneric("predLabels"))
setMethod("predLabels", "MLOutput", function(obj) obj@predLabels@.Data)
setGeneric("predLabelsTr", function(obj) standardGeneric("predLabelsTr"))
setMethod("predLabelsTr", "MLOutput", function(obj) obj@predLabelsTr@.Data)
setGeneric("predLabels", function(obj) standardGeneric("predLabels"))
setMethod("predLabels", "classifOutput", function(obj) obj@predLabels@.Data)
setGeneric("predLabelsTr", function(obj) standardGeneric("predLabelsTr"))
setMethod("predLabelsTr", "classifOutput", function(obj) obj@predLabelsTr@.Data)
setGeneric("allClass", function(obj) standardGeneric("allClass"))
setMethod("allClass", "classifOutput", function(obj) obj@allClass)
setGeneric("trainInds", function(obj) standardGeneric("trainInds"))
setMethod("trainInds", "classifOutput", function(obj) obj@trainInds)
setGeneric("confuMat", function(obj) standardGeneric("confuMat"))
setMethod("confuMat", "classifOutput", function(obj) 
table(given=allClass(obj)[-trainInds(obj)], predicted=predLabels(obj) ) )
setGeneric("confuMatTrain", function(obj) standardGeneric("confuMatTrain"))
setMethod("confuMatTrain", "classifOutput", function(obj) {
acal = function(x) as.character(as.list(x))
maker = acal(obj@call) # test for MLearn origin
if (maker[1] != "MLearn") stop("confuMatTrain only applicable to outputs of MLearn interface.  See help(MLearn).")
table(given=allClass(obj)[trainInds(obj)], predicted=predLabelsTr(obj) ) 
 })
