#' Generate p-Rep Test-Control Designs: Method II
#'
#' Constructs partially replicated (p-rep) test-control designs for
#' multiple environments using an affine resolvable partially balanced
#' incomplete block design framework.
#'
#' @param v Total number of test lines. It must satisfy v = p(p - 1)
#'   for an integer p >= 3; for example: 6, 12, 20, 30, 42, or 56.
#'
#' @return A list of p-rep test-control designs for all admissible
#'   numbers of environments.
#'
#' @export
TCpRep2 <- function(v) {
  
  ############################################################
  ## Input Validation
  ############################################################
  
  if (length(v) != 1L ||
      is.na(v) ||
      v < 6 ||
      v != as.integer(v)) {
    
    stop(
      "'v' must be a single integer representing the total number of test lines.",
      call. = FALSE
    )
    
  }
  
  # For Method II, v = p(p - 1).
  p <- (1 + sqrt(1 + 4 * v)) / 2
  
  if (p != as.integer(p)) {
    
    stop(
      "'v' must be of the form p(p - 1); for example: 6, 12, 20, 30, 42, or 56.",
      call. = FALSE
    )
    
  }
  
  p <- as.integer(p)
  
  # Retain the original variable used throughout the construction.
  vt <- v
  
  ############################################################
  ## Admissible Values of Environments
  ############################################################
  
  e.values <- (2:(p - 1))[
    (p - 1) %% (2:(p - 1)) == 0
  ]
  
  if (!length(e.values)) {
    stop("No admissible value of the number of environments exists.")
  }
  
  all.designs <- list()
  
  ############################################################
  ## Replication 1
  ############################################################
  
  R1 <- lapply(
    1:(p - 1),
    function(h) {
      ((h - 1) * p + 1):(h * p)
    }
  )
  
  ############################################################
  ## Replication 2
  ############################################################
  
  R2 <- lapply(
    1:p,
    function(g) {
      seq(g, vt, by = p)
    }
  )
  
  ############################################################
  ## Construct Design for Each Admissible e
  ############################################################
  
  for (e in e.values) {
    
    blocks.per.env <- (p - 1) / e
    
    grp <- split(
      1:(p - 1),
      rep(1:e, each = blocks.per.env)
    )
    
    environments <- vector(
      "list",
      e
    )
    
    for (i in 1:e) {
      
      env_blocks <- list()
      
      # Replication 1 blocks
      for (j in grp[[i]]) {
        env_blocks[[length(env_blocks) + 1]] <- R1[[j]]
      }
      
      # Control treatment for this environment
      ctrl <- paste0("C", i)
      
      # Augmented Replication 2 blocks
      for (j in 1:p) {
        env_blocks[[length(env_blocks) + 1]] <- c(
          R2[[j]],
          ctrl
        )
      }
      
      env.mat <- do.call(
        rbind,
        env_blocks
      )
      
      rownames(env.mat) <- paste0(
        "B",
        1:nrow(env.mat)
      )
      
      environments[[i]] <- env.mat
    }
    
    names(environments) <- paste0(
      "Environment_",
      1:e
    )
    
    ##########################################################
    ## Design Parameters
    ##########################################################
    
    b <- sum(
      vapply(
        environments,
        nrow,
        numeric(1)
      )
    )
    
    Parameters <- data.frame(
      
      Parameter = c(
        "Number of Test Treatments (vt)",
        "Number of Controls (vc)",
        "Number of Environments (e)",
        "Number of Blocks (b)",
        "Test Replication (rt)",
        "Control Replication (rc)",
        "Block Size (k)"
      ),
      
      Value = c(
        vt,
        e,
        e,
        b,
        3,
        p,
        p
      ),
      
      stringsAsFactors = FALSE
    )
    
    ##########################################################
    ## Non-Randomized Layout
    ##########################################################
    
    NonRandomized_Layout <- lapply(
      environments,
      function(env) {
        
        data.frame(
          Block = rownames(env),
          Lines = apply(
            env,
            1,
            function(x) paste(x, collapse = " ")
          ),
          row.names = NULL,
          stringsAsFactors = FALSE
        )
      }
    )
    
    ##########################################################
    ## Randomized Layout
    ##########################################################
    
    Randomized_Design <- lapply(
      environments,
      function(env) {
        
        randomized <- t(
          apply(
            env,
            1,
            sample
          )
        )
        
        rownames(randomized) <- rownames(env)
        
        randomized
      }
    )
    
    Randomized_Layout <- lapply(
      Randomized_Design,
      function(env) {
        
        data.frame(
          Block = rownames(env),
          Lines = apply(
            env,
            1,
            function(x) paste(x, collapse = " ")
          ),
          row.names = NULL,
          stringsAsFactors = FALSE
        )
      }
    )
    
    ##########################################################
    ## Save Design
    ##########################################################
    
    all.designs[[paste0("E", e)]] <- structure(
      list(
        Parameters = Parameters,
        Design = environments,
        NonRandomized_Layout = NonRandomized_Layout,
        Randomized_Layout = Randomized_Layout
      ),
      class = "TCpRep"
    )
  }
  
  structure(
    all.designs,
    class = "TCpRep2"
  )
  
}

#' Print p-Rep Test-Control Designs: Method II
#'
#' @param x An object of class "TCpRep2".
#' @param ... Further arguments.
#'
#' @return The input object invisibly.
#'
#' @exportS3Method print TCpRep2
print.TCpRep2 <- function(x, ...) {
  
  for (i in names(x)) {
    
    cat("\n========================================\n")
    cat(
      "Design in environments =",
      sub("E", "", i),
      "\n"
    )
    cat("========================================\n\n")
    
    cat("Design Parameters\n\n")
    
    print(
      x[[i]]$Parameters,
      row.names = FALSE
    )
    
    cat("\n----------------------------------------\n\n")
    
    cat("Non-Randomized Layout\n\n")
    
    for (env in names(x[[i]]$NonRandomized_Layout)) {
      
      cat(
        sub(
          "Environment_",
          "Environment ",
          env
        ),
        "\n"
      )
      
      print(
        x[[i]]$NonRandomized_Layout[[env]],
        row.names = FALSE
      )
      
      cat("\n")
    }
    
    cat("----------------------------------------\n\n")
    
    cat("Randomized Layout\n\n")
    
    for (env in names(x[[i]]$Randomized_Layout)) {
      
      cat(
        sub(
          "Environment_",
          "Environment ",
          env
        ),
        "\n"
      )
      
      print(
        x[[i]]$Randomized_Layout[[env]],
        row.names = FALSE
      )
      
      cat("\n")
    }
  }
  
  invisible(x)
  
}

