# ==== Packages ====
# install.packages(c("tidyverse"))  # if needed
library(tidyverse)

# ==== Helper functions ====
series_from_intervals <- function(intervals, n_days = 365) {
  s <- rep(0L, n_days)
  for (iv in intervals) {
    start <- max(1L, iv[1]); end <- min(n_days, iv[2])
    if (end >= start) s[start:end] <- 1L
  }
  s
}

get_runs <- function(x) {
  # Return list of 1-runs and interior 0-runs as index ranges (1-based, inclusive)
  n <- length(x)
  if (n == 0) return(list(one_runs = list(), gap_runs = list()))
  # boundaries where state changes
  changes <- which(diff(x) != 0) + 1
  starts <- c(1, changes)
  ends   <- c(changes - 1, n)
  vals <- tibble(val = x[starts], start = starts, end = ends)
  one_runs <- vals %>% filter(val == 1L) %>% select(start, end)
  zero_runs <- vals %>% filter(val == 0L) %>% select(start, end)
  # interior gaps only (between first and last 1-run)
  gap_runs <- tibble(start = integer(), end = integer())
  if (nrow(one_runs) >= 2) {
    first_one_start <- one_runs$start[1]
    last_one_end <- one_runs$end[nrow(one_runs)]
    gap_runs <- zero_runs %>% filter(start >= first_one_start, end <= last_one_end)
  }
  list(one_runs = one_runs, gap_runs = gap_runs)
}

lag1_autocorr <- function(x) {
  # Returns NA if undefined
  x <- as.numeric(x)
  if (length(x) < 3) return(NA_real_)
  if (sd(x) == 0) return(NA_real_)
  ac <- acf(x, plot = FALSE, lag.max = 1)$acf
  # acf returns lag 0 at index 1, lag 1 at index 2
  as.numeric(ac[2])
}

temporal_metrics <- function(series, core_buffer = 1L) {
  x <- as.integer(series)
  n <- length(x)
  total_habitat_time <- sum(x)
  rr <- get_runs(x)
  one_runs <- rr$one_runs
  gap_runs <- rr$gap_runs
  
  one_lengths <- if (nrow(one_runs) > 0) (one_runs$end - one_runs$start + 1) else integer()
  gap_lengths <- if (nrow(gap_runs) > 0) (gap_runs$end - gap_runs$start + 1) else integer()
  
  number_of_periods <- length(one_lengths)
  temporal_isolation_mean <- if (length(gap_lengths) > 0) mean(gap_lengths) else 0
  temporal_isolation_max  <- if (length(gap_lengths) > 0) max(gap_lengths) else 0
  duration_variability    <- if (length(one_lengths) > 1) sd(one_lengths) else 0
  
  transitions <- sum(abs(diff(x)))
  temporal_edge_density <- if (n > 1) transitions / (n - 1) else 0
  
  temporal_autocorrelation_lag1 <- lag1_autocorr(x)
  temporal_aggregation <- if (n > 1) mean(x[-n] == x[-1]) else 0
  
  core_lengths <- pmax(one_lengths - 2L * core_buffer, 0)
  core_time <- sum(core_lengths)
  core_time_index <- if (total_habitat_time > 0) core_time / total_habitat_time else 0
  number_of_core_periods <- sum(one_lengths > 2L * core_buffer)
  
  tibble(
    `Total habitat time (days)` = as.integer(total_habitat_time),
    `Number of periods` = as.integer(number_of_periods),
    `Temporal isolation mean (days)` = as.numeric(round(temporal_isolation_mean, 3)),
    `Temporal isolation max (days)` = as.integer(temporal_isolation_max),
    `Duration variability (days)` = as.numeric(round(duration_variability, 3)),
    `Temporal edge density` = as.numeric(round(temporal_edge_density, 3)),
    `Lag-1 temporal autocorrelation` = as.numeric(round(temporal_autocorrelation_lag1, 3)),
    `Temporal aggregation` = as.numeric(round(temporal_aggregation, 3)),
    `Core time index` = as.numeric(round(core_time_index, 3)),
    `Number of core periods` = as.integer(number_of_core_periods)
  )
}

# ==== Construct daily dataset (rock-pool-like) ====
days <- 1:365
S1 <- series_from_intervals(list(c(80, 140)))
S2 <- series_from_intervals(list(c(90, 130)))
# S3 has same total days as S2 (41) but split 17 + 24
S3 <- series_from_intervals(list(c(92, 108), c(118, 141)))
S4 <- series_from_intervals(list(c(95, 105), c(124, 142)))  # total 30
S5 <- series_from_intervals(list(c(82, 87), c(97, 109), c(128, 135)))  # total 27

dat <- tibble(
  day_of_year = days,
  S1_continuous = S1,
  S2_loss_only = S2,
  S3_fragmentation_only_equal_to_S2 = S3,
  S4_loss_plus_fragmentation = S4,
  S5_irregular_intervals = S5
)

# Save CSV (optional)
# write_csv(dat, "rock_pool_daily_binary_5scenarios.csv")

# ==== Figure 3: stacked step timelines without legend ====

cols <- c("#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2")  # colorblind-friendly
y_labels <- c("S1_continuous","S2_loss_only",
              "S3_fragmentation_only_equal_to_S2",
              "S4_loss_plus_fragmentation",
              "S5_irregular_intervals")
cols <- c("#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2")
nseries <- length(y_labels)
offsets <- rev(seq(0, by = 1.5, length.out = nseries))  # vertical separation

# ---- build the Y matrix (each column is one scenario, offset vertically) ----
y_mat <- cbind(
  dat$S1_continuous + offsets[1],
  dat$S2_loss_only + offsets[2],
  dat$S3_fragmentation_only_equal_to_S2 + offsets[3],
  dat$S4_loss_plus_fragmentation + offsets[4],
  dat$S5_irregular_intervals + offsets[5]
)

# ---- compute left margin so long labels are not clipped ----
left_in <- max(graphics::strwidth(y_labels, units = "inches", cex = 1)) + 0.9

png("Figure3_step_timelines.png", width = 1800, height = 700, res = 150)
par(mai = c(0.9, left_in, 1.0, 0.5))  # bottom, left, top, right

# ---- plot all series with distinct colors ----
matplot(
  x = dat$day_of_year, y = y_mat,
  type = "s", lty = 1, lwd = 3, col = cols,
  xlab = "Day of year", ylab = "",
  xlim = c(0, 365), ylim = c(min(offsets) - 0.2, max(offsets) + 1.0),
  xaxt = "n", yaxt = "n",
  bty = "l",  # draw left and bottom axes (adds the y-axis line)
  main = "Scenario timelines (presence shown as elevated segments)"
)

# custom x-axis ticks at 0, 50, ..., 350
axis(1, at = seq(0, 350, by = 50), labels = seq(0, 350, by = 50))
# left-side y labels at the offsets
axis(2, at = offsets, labels = y_labels, las = 1, tick = FALSE)

# light vertical grid
abline(v = seq(0, 350, by = 50), lty = 3, col = "grey80")

dev.off()
