tm <- tempdir()
stopifnot(file.exists(tm))

library(stringr)
library(zeallot)

prevOptState <- options(parallelly.fork.enable = TRUE)

test_that("Merge Uniform Cells Clusters", {
  utils::data("test.dataset", package = "COTAN")

  obj <- COTAN(raw = test.dataset)
  obj <- initializeMetaDataset(objCOTAN = obj, GEO = " ",
                               sequencingMethod = "artificial",
                               sampleCondition = "test")

  obj <- proceedToCoex(objCOTAN = obj,
                       calcCoex = FALSE, cores = 6L, saveObj = FALSE)

  clusters <- factor(readRDS(file.path(getwd(), "split.clusters.test.RDS")))
  genesNamesTest <- readRDS(file.path(getwd(), "genes.names.test.RDS"))

  obj <- addClusterization(objCOTAN = obj,
                           clName = "clusters", clusters = clusters)

  coexDF <- DEAOnClusters(objCOTAN = obj)

  obj <- addClusterizationCoex(objCOTAN = obj,
                               clName = "clusters", coexDF = coexDF)

  expect_setequal(colnames(coexDF), levels(clusters))
  expect_identical(rownames(coexDF), getGenes(objCOTAN = obj))

  lfcDF <- logFoldChangeOnClusters(objCOTAN = obj,
                                   clusters = clusters,
                                   floorLambdaFraction = 0.01)

  expect_setequal(colnames(lfcDF), clusters)
  expect_identical(rownames(lfcDF), getGenes(objCOTAN = obj))
  expect_gte(min(colSums(lfcDF > 0.0)), 260L)
  expect_lte(max(colSums(lfcDF > 0.0)), 340L)
  expect_lt(max(colMeans(lfcDF)),  0.0)
  expect_gt(min(colMeans(lfcDF)), -0.28)

  adjustmentMethod <- "bonferroni"

  pValDF <- pValueFromDEA(coexDF, numCells = getNumCells(objCOTAN = obj),
                          adjustmentMethod = "none")
  adjPValDF <- pValueFromDEA(coexDF, numCells = getNumCells(objCOTAN = obj),
                             adjustmentMethod = adjustmentMethod)

  expect_setequal(colnames(pValDF), levels(clusters))
  expect_identical(rownames(pValDF), getGenes(objCOTAN = obj))
  expect_setequal(colnames(adjPValDF), levels(clusters))
  expect_identical(rownames(adjPValDF), getGenes(objCOTAN = obj))

  coexDfExp <- readRDS(file.path(getwd(), "coex.clusters.test.RDS"))
  pValDfExp <- readRDS(file.path(getwd(), "pvalues.clusters.test.RDS"))

  expect_equal(coexDF[genesNamesTest, ], coexDfExp, tolerance = 1.0e-8)
  expect_equal(pValDF[genesNamesTest, ], pValDfExp, tolerance = 1.0e-8)

  deltaExpression <- clustersDeltaExpression(objCOTAN = obj)

  expect_identical(rownames(deltaExpression), getGenes(objCOTAN = obj))
  expect_setequal(colnames(deltaExpression), levels(clusters))

  groupMarkers <- list(G1 = c("g-000010", "g-000020", "g-000138"),
                       G2 = c("g-000300", "g-000330", "g-000660"),
                       G3 = c("g-000510", "g-000530", "g-000550",
                              "g-000570", "g-000590"))

  eDf <- geneSetEnrichment(clustersCoex = coexDF, groupMarkers = groupMarkers)

  expect_identical(nrow(eDf), length(groupMarkers))
  expect_identical(ncol(eDf), ncol(coexDF) + 2L)
  expect_lte(max(eDf[, 1L:(ncol(eDf) - 2L)]), 1L)
  expect_gte(min(eDf[, 1L:(ncol(eDf) - 2L)]), 0L)
  expect_gte(min(eDf[["N. total"]] - eDf[["N. detected"]]), 0L)
  expect_equal(eDf[["N. total"]], lengths(groupMarkers), ignore_attr = TRUE)

  checker <- new("AdvancedGDIUniformityCheck")
  checkers <- list(checker, shiftCheckerThresholds(checker, 0.1))

  suppressWarnings({
    c(mergedClusters, mergedCoexDF) %<-%
      mergeUniformCellsClusters(
        objCOTAN = obj, clusters = clusters,
        checkers = checkers, allCheckResults = data.frame(),
        batchSize = 1L, distance = "cosine", hclustMethod = "ward.D2",
        cores = 6L, optimizeForSpeed = TRUE, deviceStr = "cuda",
        saveObj = TRUE, outDir = tm)
  })
  expect_true(file.exists(file.path(tm, "test", "leafs_merge",
                                    "merge_clusterization_1.csv")))
  expect_true(file.exists(file.path(tm, "test", "leafs_merge",
                                    "merge_clusterization_4.csv")))
  expect_true(file.exists(file.path(tm, "test", "leafs_merge",
                                    "all_check_results_1.csv")))
  expect_true(file.exists(file.path(tm, "test", "leafs_merge",
                                    "all_check_results_4.csv")))
  expect_true(file.exists(file.path(tm, "test", "merge_check_results.csv")))
  expect_true(file.exists(file.path(tm, "test", "merge_clusterization.csv")))

  allCheckDF <- read.csv(file.path(tm, "test", "leafs_merge",
                                   "all_check_results_4.csv"),
                         header = TRUE, row.names = 1L)
  expect_identical(nrow(allCheckDF), 4L)
  expect_identical(ncol(allCheckDF), 35L)

  allCheckRes <- dfToCheckers(allCheckDF)

  expect_length(allCheckRes, nrow(allCheckDF))
  expect_named(allCheckRes)
  expect_true(all(stringr::str_ends(names(allCheckRes), fixed("-merge"))))
  expect_true(all(vapply(allCheckRes, methods::is,
                         logical(1L), "AdvancedGDIUniformityCheck")))
  expect_false(allCheckRes[[3L]]@isUniform)
  expect_true(is.finite(allCheckRes[[3L]]@firstCheck@fractionBeyond))
  expect_true(is.finite(allCheckRes[[3L]]@firstCheck@quantileAtRatio))

  expect_identical(nlevels(mergedClusters), 2L)
  expect_setequal(colnames(mergedCoexDF), mergedClusters)
  expect_identical(rownames(mergedCoexDF), getGenes(objCOTAN = obj))

  obj <- addClusterization(objCOTAN = obj,
                           clName = "merge", clusters = mergedClusters,
                           coexDF = mergedCoexDF, override = FALSE)

  expect_identical(reorderClusterization(objCOTAN = obj)[1L:2L],
                   list("clusters" = mergedClusters, "coex" = mergedCoexDF))

  mergedLfcDF <- logFoldChangeOnClusters(objCOTAN = obj, clName = "merge")

  expect_setequal(colnames(mergedLfcDF), mergedClusters)
  expect_identical(rownames(mergedLfcDF), getGenes(objCOTAN = obj))
  # with 2 clusters the changes are symmetric
  expect_equal(mergedLfcDF[[1L]], -mergedLfcDF[[2L]], tolerance = 1.0e-12)

  # Test the low GDI (homogeneity) for each defined clusters
  simpleChecker <- checkers[[2L]]@thirdCheck
  for (cl in levels(mergedClusters)[[1L]]) {
    cat(paste("Tested cluster:", cl))

    cellsToDrop <- names(clusters)[mergedClusters != cl]

    tmpObj <- dropGenesCells(objCOTAN = obj, cells = cellsToDrop)

    suppressWarnings({
      tmpObj <- proceedToCoex(objCOTAN = tmpObj, cores = 6L, saveObj = FALSE)
    })

    gdiData <- calculateGDI(objCOTAN = tmpObj)

    expect_lte(sum(gdiData[["GDI"]] >= simpleChecker@GDIThreshold),
               simpleChecker@maxRatioBeyond * nrow(gdiData))
  }

  gc()
})

options(prevOptState)
