#' Field Design Layout Plot for Agricultural Experiments
#'
#' Generates randomized field layouts for agricultural experimental designs including Completely Randomized Design (CRD), Randomized Block Design (RBD),
#' and Latin Square Design (LSD), factorial designs, split-plot, and strip-plot.
#'
#' @param design Type of design. Options include" "CRD", "RBD", "LSD", "FCRD","FRBD", "SPLIT", or "STRIP".
#' @param treatments Vector of treatment labels (used in CRD, RBD, LSD).
#' @param A Vector of Factor A levels (for factorial, split, strip designs).
#' @param B Vector of Factor B levels (for factorial, split, strip designs).
#' @param blocks Number of blocks (required for RBD, SPLIT, STRIP).
#' @param replications Number of replications. Can be a single number (equal replication) or a vector (unequal replication, e.g., c(2,3,4,2)).
#' @param plot_length Length of each plot.
#' @param plot_width Width of each plot.
#' @param alley Spacing between adjacent plots.
#' @param block_gap Gap between blocks.
#' @param border Border area around the field layout.
#' @param plot_length Length of each plot
#' @param plot_width Width of each plot
#' @param treatment_label_size Size of treatment labels in the layout
#' @param block_label_size Size of block labels
#' @param show_field_dimensions Logical; display field dimensions
#' @param show_plot_dimensions Logical; display plot dimensions
#' @param seed Random seed for reproducibility. Optional random seed to ensure reproducibility of randomization; same seed gives the same layout/results across runs.
#'
#' @details
#' This function supports:
#' \itemize{
#'   \item Completely Randomized Design (CRD)
#'   \item Randomized Block Design (RBD)
#'   \item Latin Square Design (LSD)
#'   \item Factorial CRD and RBD (FCRD, FRBD)
#'   \item Split-plot and strip-plot designs
#'   \item Unequal and equal replication structures in CRD
#'   \item Custom plot dimensions and spacing
#' }
#'
#' @return A list containing:
#' \describe{
#'   \item{plot}{A ggplot object showing field layout}
#'   \item{layout}{Data frame containing plot arrangement}
#' }
#'
#' @importFrom ggplot2 ggplot geom_rect geom_text geom_segment annotate aes coord_fixed theme_void guides guide_legend
#' @importFrom grid arrow unit
#'
#'
#' @examples
#' layout_crd <- fld_layout(
#'     design = "CRD",
#'     treatments = paste0("T", 1:7),
#'     replications = c(2,3,4,2,1,5,4),
#'     plot_length = 2.5,
#'   plot_width = 1.8,
#'   alley = 0.3,
#'   border = 1.5,
#'   seed = 123
#'   )
#'   layout_crd$plot
#'
#'
#' # RBD example
#' layout_rbd <- fld_layout(
#'   design = "RBD",
#'   treatments = paste0("T",1:7),
#'   blocks = 4,
#'   seed = 123
#' )
#' layout_rbd$plot
#'
#' layout_rbd <- fld_layout(
#'   design = "RBD",
#'   treatments = paste0("T",1:7),
#'   blocks = 4,
#'    treatment_label_size = 4,
#'    block_label_size = 3,
#'   show_field_dimensions = FALSE,
#'   show_plot_dimensions = FALSE,
#'   seed = 123
#' )
#' layout_rbd$plot
#'
#' # Latin Square Design
#' layout_lsd <- fld_layout(
#'   design = "LSD",
#'   treatments = paste0("T", 1:6),
#'   seed = 123
#' )
#' layout_lsd$plot
#'
#' # Factorial CRD
#' result <- fld_layout(
#'   design = "FCRD",
#'   A = c("A1", "A2", "A3"),
#'   B = c("B1", "B2", "B3", "B4"),
#'   replications = 3,
#'   seed = 101
#' )
#' result$plot
#'
#' # Split plot
#' result2 <- fld_layout(
#'   design = "SPLIT",
#'   A = c("A1", "A2", "A3"),
#'   B = c("B1", "B2", "B3", "B4"),
#'   blocks = 3,
#'   seed = 101 )
#' result2$plot
#'
#' @export
fld_layout<- function(
    design = "RBD",
    treatments = paste0("T", 1:5),
    A = c("A1", "A2"),
    B = c("B1", "B2", "B3"),
    blocks = 3,
    replications = 3,
    plot_length = 2,
    plot_width = 1,
    alley = 0.3,
    block_gap = 0.6,
    border = NULL,
    treatment_label_size = NULL,
    block_label_size = NULL,
    show_field_dimensions = TRUE,
    show_plot_dimensions = TRUE,
    seed = NULL
){


  if(is.null(border)) border <- 1.5
  if(!is.null(seed)) set.seed(seed)

  # VALIDATION

  if(design %in% c("RBD", "FRBD", "SPLIT", "STRIP") && length(replications) > 1){
    stop(" Unequal replication not allowed for RBD, FRBD, SPLIT and STRIP.\n",
         "\n Please provide a single number for replications.")
  }

  # DESIGN GENERATION


  if(design == "RBD"){
    layout_list <- lapply(1:blocks, function(i) sample(treatments))
    df <- do.call(rbind, lapply(1:blocks, function(b){
      data.frame(Block = b, Plot = 1:length(treatments),
                 Treatment = layout_list[[b]])
    }))
    df$row <- df$Block
    df$col <- df$Plot
  }

  if(design == "CRD"){
    if(length(replications) == 1){
      reps <- rep(replications, length(treatments))
    } else {
      if(length(replications) != length(treatments)){
        stop("For CRD with unequal replications, 'replications' must be a vector of same length as treatments")
      }
      reps <- replications
    }

    trt_list <- lapply(1:length(treatments), function(i) rep(treatments[i], reps[i]))
    trt <- sample(unlist(trt_list))

    df <- data.frame(Plot = 1:length(trt), Treatment = trt)

    n_col <- length(treatments)
    max_row <- max(reps)

    # FIXED : Create row and col only up to actual number of plots
    row_vec <- rep(1:max_row, each = n_col)[1:length(trt)]
    col_vec <- rep(1:n_col, times = max_row)[1:length(trt)]

    df$row <- row_vec
    df$col <- col_vec
    df$Block <- 1
  }

  if(design == "LSD"){
    n <- length(treatments)
    mat <- outer(1:n, 1:n, function(i,j) ((i+j-2)%%n)+1)
    mat <- matrix(treatments[mat], n)
    mat <- mat[sample(1:n), sample(1:n)]

    df <- expand.grid(Row=1:n, Col=1:n)
    df$Treatment <- as.vector(mat)
    df$row <- df$Row
    df$col <- df$Col
    df$Block <- df$Row
  }

  if(design == "FCRD"){
    comb <- expand.grid(A = A, B = B)
    trt_levels <- paste(comb$A, comb$B, sep=":")
    n_trt <- length(trt_levels)

    if(length(replications) == 1){
      reps <- rep(replications, n_trt)
    } else {
      if(length(replications) != n_trt){
        stop("For FACTORIAL with unequal replications, 'replications' must match number of treatment combinations")
      }
      reps <- replications
    }

    trt_list <- lapply(1:n_trt, function(i) rep(trt_levels[i], reps[i]))
    trt_all <- sample(unlist(trt_list))

    df <- data.frame(Plot = 1:length(trt_all), Treatment = trt_all)

    n_col <- n_trt
    max_row <- max(reps)

    row_vec <- rep(1:max_row, each = n_col)[1:length(trt_all)]
    col_vec <- rep(1:n_col, times = max_row)[1:length(trt_all)]

    df$row <- row_vec
    df$col <- col_vec
    df$Block <- 1
  }

  if(design == "FRBD"){
    trt <- as.vector(outer(A, B, paste, sep=":"))
    layout_list <- lapply(1:blocks, function(i) sample(trt))
    df <- do.call(rbind, lapply(1:blocks, function(b){
      data.frame(Block = b, Plot = 1:length(trt),
                 Treatment = layout_list[[b]])
    }))
    df$row <- df$Block
    df$col <- df$Plot
  }

  if(design == "SPLIT"){
    df <- do.call(rbind, lapply(1:blocks, function(b){
      main_order <- sample(A)
      temp <- expand.grid(Main = main_order, Sub = B)
      temp$Block <- b
      temp <- do.call(rbind, lapply(split(temp, temp$Main), function(x){
        x$Sub <- sample(as.character(x$Sub))
        x
      }))
      temp$MainID <- match(temp$Main, main_order)
      temp$SubID  <- ave(temp$Sub, temp$Main, FUN = seq_along)
      temp$row <- (b - 1) * length(A) + temp$MainID
      temp$col <- temp$SubID
      temp$Treatment <- paste(temp$Main, temp$Sub, sep=":")
      temp
    }))
    df <- df[, c("Block", "row", "col", "Treatment")]
  }

  if(design == "STRIP"){
    df <- do.call(rbind, lapply(1:blocks, function(b){
      A_order <- sample(A)
      B_order <- sample(B)
      temp <- expand.grid(A = A_order, B = B_order)
      temp$Block <- b
      temp$col <- match(temp$A, A_order)
      temp$row_local <- match(temp$B, B_order)
      temp
    }))
    df$row <- (df$Block - 1) * length(B) + df$row_local
    df$Treatment <- paste(df$A, df$B, sep=":")
  }


  # COORDINATES

  df$row <- as.numeric(df$row)
  df$col <- as.numeric(df$col)

  df$xmin <- (df$col - 1) * (plot_length + alley)
  df$xmax <- df$xmin + plot_length

  df$ymin <- (max(df$row) - df$row) * (plot_width + block_gap)
  df$ymax <- df$ymin + plot_width

  xmin_f <- min(df$xmin) - border
  xmax_f <- max(df$xmax) + border
  ymin_f <- min(df$ymin) - border
  ymax_f <- max(df$ymax) + border

  field_length <- max(df$xmax) - min(df$xmin)
  field_width  <- max(df$ymax) - min(df$ymin)

  if(is.null(treatment_label_size)){
    treatment_label_size <- max(2.2, min(4.5, 2.8 * sqrt(plot_length * plot_width)/2))
  }
  if(is.null(block_label_size)){
    block_label_size <- 3.5 + border * 0.8
  }

  # BLOCK BOXES

  block_boxes <- do.call(rbind, lapply(unique(df$Block), function(b) {
    sub <- df[df$Block == b, ]
    data.frame(
      xmin = min(sub$xmin),
      xmax = max(sub$xmax),
      ymin = min(sub$ymin),
      ymax = max(sub$ymax),
      label = paste0("B-", as.character(as.roman(b))," ")
    )
  }))
  block_boxes$strip_w <- 0.18 * nchar(block_boxes$label) + 0.4


  # BASE PLOT
  p <- ggplot2::ggplot() +

    ggplot2::annotate("rect", xmin = xmin_f, xmax = xmax_f,
                      ymin = ymin_f, ymax = ymax_f,
                      fill = "#e4fee6", color = "black") +

    ggplot2::annotate("rect", xmin = min(df$xmin), xmax = max(df$xmax),
                      ymin = min(df$ymin), ymax = max(df$ymax),
                      fill = "#fffaf0", color = "black") +

    # Plots with dotted borders
    ggplot2::geom_rect(data = df,
                       ggplot2::aes(
                         xmin = xmin,
                         xmax = xmax,
                         ymin = ymin,
                         ymax = ymax,
                         fill = Treatment
                       ),
                       color = "black", linewidth = 0.4, linetype = "dotted") +

    ggplot2::geom_text(data = df,
                       ggplot2::aes(
                         x = (xmin + xmax)/2,
                         y = (ymin + ymax)/2,
                         label = Treatment
                       ),
                       size = treatment_label_size) +

    # Block box
    ggplot2::geom_rect(data = block_boxes,
                       ggplot2::aes(
                         xmin = xmin,
                         xmax = xmax,
                         ymin = ymin,
                         ymax = ymax
                       ),
                       fill = NA, color = "black", linewidth = 0.85)


  # TOP DIMENSIONS

  if(show_plot_dimensions){
    top_df <- df[df$Block == 1, ]

    p <- p +
      geom_segment(data = top_df,
                   aes(x = xmin, xend = xmax,
                       y = ymax_f + 0.6, yend = ymax_f + 0.6),
                   arrow = arrow(ends = "both", length = unit(0.2, "cm"))) +
      geom_text(data = top_df,
                aes(x = (xmin + xmax)/2, y = ymax_f + 1.15,
                    label = paste(plot_length, "m")),
                size = 3)

    alley_df <- top_df[top_df$col < max(top_df$col), ]
    if(nrow(alley_df) > 0){
      p <- p +
        geom_segment(data = alley_df,
                     aes(x = xmax, xend = xmax + alley,
                         y = ymax_f + 0.6, yend = ymax_f + 0.6),
                     arrow = arrow(ends = "both", length = unit(0.2, "cm"))) +
        geom_text(data = alley_df,
                  aes(x = xmax + alley/2, y = ymax_f + 1.15,
                      label = paste(alley, "m")),
                  size = 3)
    }
  }


  # RIGHT SIDE DIMENSIONS

  if(show_plot_dimensions){
    plot_segments <- unique(df[, c("ymin", "ymax")])
    plot_segments <- plot_segments[order(plot_segments$ymin), ]

    p <- p +
      geom_segment(data = plot_segments,
                   aes(x = xmax_f + 0.6, xend = xmax_f + 0.6,
                       y = ymin, yend = ymax),
                   arrow = arrow(ends = "both", length = unit(0.2, "cm"))) +
      geom_text(data = plot_segments,
                aes(x = xmax_f + 1.15,
                    y = (ymin + ymax)/2,
                    label = paste(plot_width, "m")),
                angle = 90, size = 3)

    if(nrow(plot_segments) > 1){
      gap_segments <- data.frame(
        ymin = plot_segments$ymax[-nrow(plot_segments)],
        ymax = plot_segments$ymin[-1]
      )
      p <- p +
        geom_segment(data = gap_segments,
                     aes(x = xmax_f + 0.6, xend = xmax_f + 0.6,
                         y = ymin, yend = ymax),
                     arrow = arrow(ends = "both", length = unit(0.2, "cm"))) +
        geom_text(data = gap_segments,
                  aes(x = xmax_f + 1.15,
                      y = (ymin + ymax)/2,
                      label = paste(block_gap, "m")),
                  angle = 90, size = 3)
    }
  }

  # OVERALL FIELD LENGTH & WIDTH

  if(show_field_dimensions){
    p <- p +
      annotate("segment",
               x = min(df$xmin), xend = max(df$xmax),
               y = ymin_f - 0.5, yend = ymin_f - 0.5,
               arrow = arrow(ends = "both", length = unit(0.3, "cm"))) +
      annotate("text",
               x = (min(df$xmin) + max(df$xmax))/2,
               y = ymin_f - 1.05,
               label = paste("Length:", round(field_length, 1), "m"),
               size = 3.5) +

      annotate("segment",
               x = xmax_f + 2, xend = xmax_f + 2,
               y = min(df$ymin), yend = max(df$ymax),
               arrow = arrow(ends = "both", length = unit(0.3, "cm"))) +
      annotate("text",
               x = xmax_f + 2.5,
               y = (min(df$ymin) + max(df$ymax))/2,
               angle = 90,
               label = paste("Width:", round(field_width, 1), "m"),
               size = 3.5)
  }

  # NSEW DIRECTION

  p <- p +
    annotate("segment", x = xmax_f + 2.2, xend = xmax_f + 2.2,
             y = ymax_f + 0.6, yend = ymax_f + 1.5,
             arrow = arrow(length = unit(0.25, "cm"))) +
    annotate("segment", x = xmax_f + 2.2, xend = xmax_f + 3.1,
             y = ymax_f + 0.6, yend = ymax_f + 0.6,
             arrow = arrow(length = unit(0.25, "cm"))) +
    annotate("segment", x = xmax_f + 2.2, xend = xmax_f + 2.2,
             y = ymax_f + 0.6, yend = ymax_f - 0.4,
             arrow = arrow(length = unit(0.25, "cm"))) +
    annotate("segment", x = xmax_f + 2.2, xend = xmax_f + 1.3,
             y = ymax_f + 0.6, yend = ymax_f + 0.6,
             arrow = arrow(length = unit(0.25, "cm")))


  # TREATMENT LEGEND + CLEAN AXIS

  p <- p +
    guides(fill = guide_legend(title = "Treatment",
                               title.position = "top",
                               title.hjust = 0.5)) +

    coord_fixed() + theme_void()
  return(list(layout = df, plot = p))
}






