---
title: "Color Palette Management"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Color Palette Management}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment  = "#>",
  eval     = FALSE
)
```

## Overview

The palette module provides nine functions covering four areas:

| Area | Functions |
|---|---|
| Querying palettes | `get_palette()`, `list_palettes()` |
| Visualization | `preview_palette()`, `palette_gallery()` |
| Managing palettes | `create_palette()`, `remove_palette()`, `compile_palettes()` |
| Color conversion | `hex2rgb()`, `rgb2hex()` |

```{r load}
library(evanverse)
```

> **Note:** All code examples in this vignette are static (`eval = FALSE`).
> Output is hand-written to reflect the current implementation. If you modify
> the palette functions, re-verify the examples manually or switch chunks to `eval = TRUE`.

Palettes are stored as JSON files organized under three subdirectories —
`sequential/`, `diverging/`, and `qualitative/` — and compiled into the
`palettes` package dataset via `compile_palettes()`.

---

## 1 Querying Palettes

### `get_palette()` — Retrieve a palette by name

Returns a character vector of HEX color codes. If `type` is omitted, the
palette type is auto-detected. Use `n` to take the first N colors from a
larger palette.

```{r get-palette-basic}
# Auto-detect type
get_palette("qual_vivid")
#> [1] "#E64B35" "#4DBBD5" "#00A087" "#3C5488" "#F39B7F" "#8491B4"

# Specify type explicitly
get_palette("qual_vivid", type = "qualitative")
#> [1] "#E64B35" "#4DBBD5" "#00A087" "#3C5488" "#F39B7F" "#8491B4"

# Take only the first 3 colors
get_palette("seq_blues", type = "sequential", n = 3)
#> [1] "#deebf7" "#9ecae1" "#3182bd"
```

When `type` is wrong, the error message tells you where the palette actually lives:

```{r get-palette-wrong-type}
get_palette("qual_vivid", type = "sequential")
#> Error in `get_palette()`:
#> ! Palette "qual_vivid" not found under "sequential", but exists under "qualitative".
#> Try: get_palette("qual_vivid", type = "qualitative")
```

Requesting more colors than the palette contains raises an informative error
rather than silently recycling:

```{r get-palette-too-many}
get_palette("qual_softtrio", type = "qualitative", n = 10)
#> Error in `get_palette()`:
#> ! Palette "qual_softtrio" only has 3 colors, but requested 10.
```

---

### `list_palettes()` — Browse available palettes

Returns a data frame with columns `name`, `type`, `n_color`, and `colors`.
Filter by one or more types with the `type` argument.

```{r list-palettes-all}
list_palettes()
#>              name       type n_color                                   colors
#> 1    div_contrast  diverging       5  #2166ac, #92c5de, #f7f7f7, ...
#> 2    div_fireice   diverging       7  #4575b4, #91bfdb, #e0f3f8, ...
#> ...
#> 12   qual_vivid   qualitative       6  #E64B35, #4DBBD5, #00A087, ...
#> ...
```

```{r list-palettes-filter}
# Single type
list_palettes(type = "qualitative")

# Multiple types
list_palettes(type = c("sequential", "diverging"))
```

Results are sorted by `type`, `n_color`, and `name` by default. Set
`sort = FALSE` to keep the original file order.

---

## 2 Visualization

### `preview_palette()` — Visualize a single palette

Plots a single palette using one of five styles: `"bar"` (default), `"pie"`,
`"point"`, `"rect"`, or `"circle"`. Returns `NULL` invisibly — called for the
plotting side effect.

```{r preview-basic}
preview_palette("seq_blues", type = "sequential")
preview_palette("div_fireice", plot_type = "pie")
preview_palette("qual_vivid", n = 4, plot_type = "circle")
```

Supply `title` to override the default title (which is the palette name):

```{r preview-title}
preview_palette("qual_vivid", title = "My favourite palette")
```

---

### `palette_gallery()` — Browse all palettes at once

Renders a paged gallery of all palettes and returns a named list of ggplot
objects (one per page). Useful for picking colors interactively.

```{r gallery-all}
plots <- palette_gallery()
#> i Type sequential: 8 palettes -> 1 page(s)
#> v Built "sequential_page1"
#> i Type diverging: 6 palettes -> 1 page(s)
#> v Built "diverging_page1"
#> i Type qualitative: 16 palettes -> 1 page(s)
#> v Built "qualitative_page1"
```

Filter by type and control how many palettes appear per page:

```{r gallery-filter}
plots <- palette_gallery(type = "qualitative")
plots <- palette_gallery(type = c("sequential", "diverging"), max_palettes = 10)
```

Access individual pages from the returned list:

```{r gallery-access}
plots[["qualitative_page1"]]
```

Set `verbose = FALSE` to suppress progress messages when calling
`palette_gallery()` programmatically.

---

## 3 Managing Palettes

### `create_palette()` — Save a custom palette

Writes a named palette as a JSON file under `color_dir/<type>/<name>.json`.
The directory is created automatically if it does not exist.

```{r create-basic}
temp_dir <- file.path(tempdir(), "palettes")

create_palette("blues", "sequential", c("#deebf7", "#9ecae1", "#3182bd"),
               color_dir = temp_dir)
#> v Palette saved: /tmp/.../palettes/sequential/blues.json

create_palette("qual_vivid", "qualitative",
               c("#E64B35", "#4DBBD5", "#00A087"),
               color_dir = temp_dir)
#> v Palette saved: /tmp/.../palettes/qualitative/qual_vivid.json
```

By default, saving over an existing name raises an error. Pass
`overwrite = TRUE` to replace it:

```{r create-overwrite}
create_palette("blues", "sequential", c("#c6dbef", "#6baed6", "#2171b5"),
               color_dir = temp_dir, overwrite = TRUE)
#> i Overwriting existing palette: "blues"
#> v Palette saved: /tmp/.../palettes/sequential/blues.json
```

```{r create-error}
# Without overwrite = TRUE
create_palette("blues", "sequential", c("#deebf7", "#9ecae1", "#3182bd"),
               color_dir = temp_dir)
#> Error in `create_palette()`:
#> ! Palette "blues" already exists. Use `overwrite = TRUE` to replace.
```

The function invisibly returns a list with `path` and `info` so the result
can be captured when needed.

---

### `remove_palette()` — Delete a palette JSON

Removes a palette JSON file by name. If `type` is omitted, all three type
directories are searched in order.

```{r remove-basic}
remove_palette("blues", color_dir = temp_dir)
#> v Removed "blues" from sequential

# Specify type to skip the search
remove_palette("qual_vivid", type = "qualitative", color_dir = temp_dir)
#> v Removed "qual_vivid" from qualitative
```

If the palette is not found in any directory, a warning is issued and the
function returns `FALSE` invisibly:

```{r remove-missing}
remove_palette("nonexistent", color_dir = temp_dir)
#> ! Palette "nonexistent" not found in any type.
```

---

### `compile_palettes()` — Build the palette dataset

Reads all JSON files under `palettes_dir/sequential/`, `palettes_dir/diverging/`,
and `palettes_dir/qualitative/`, validates them, and returns a structured list.
This is the function used in `data-raw/palettes.R` to rebuild the `palettes`
package dataset via `usethis::use_data()`.

```{r compile}
compiled <- compile_palettes(
  palettes_dir = system.file("extdata", "palettes", package = "evanverse")
)
#> v Compiled 30 palettes: Sequential=8, Diverging=6, Qualitative=16
```

The return value is a named list with three elements — `sequential`,
`diverging`, and `qualitative` — each of which is a named list of HEX vectors:

```{r compile-access}
compiled$qualitative[["qual_vivid"]]
#> [1] "#E64B35" "#4DBBD5" "#00A087" "#3C5488" "#F39B7F" "#8491B4"
```

> Duplicate palette names within the same type emit a warning and the last
> file read wins. JSON files with missing or invalid fields are skipped with
> a warning rather than aborting the whole compile.

---

## 4 Color Conversion

### `hex2rgb()` — HEX to RGB data frame

Converts a character vector of HEX codes to a data frame with columns
`hex`, `r`, `g`, `b`. Both 6-digit (`#RRGGBB`) and 8-digit (`#RRGGBBAA`)
codes are accepted; the alpha channel is silently ignored.

```{r hex2rgb-basic}
hex2rgb("#FF8000")
#>       hex   r   g b
#> 1 #FF8000 255 128 0

hex2rgb(c("#FF8000", "#00FF00", "#3182bdB2"))
#>          hex   r   g   b
#> 1    #FF8000 255 128   0
#> 2    #00FF00   0 255   0
#> 3 #3182bdB2  49 130 189
```

The `#` prefix is required; codes that fail the pattern and `NA` values are
both reported as invalid:

```{r hex2rgb-error}
# Missing '#' prefix
hex2rgb("FF8000")
#> Error in `hex2rgb()`:
#> ! `hex` contains invalid HEX codes: "FF8000".

# NA is treated as an invalid code
hex2rgb(c("#FF8000", NA))
#> Error in `hex2rgb()`:
#> ! `hex` contains invalid HEX codes: NA.
```

---

### `rgb2hex()` — RGB to HEX color codes

The symmetric counterpart to `hex2rgb()`. Accepts either a numeric vector of
length 3 or the data frame returned by `hex2rgb()`. Non-integer values are
rounded before conversion.

```{r rgb2hex-vector}
# Single color as a length-3 vector
rgb2hex(c(255, 128, 0))
#> [1] "#FF8000"
```

```{r rgb2hex-df}
# Round-trip: HEX -> RGB -> HEX
rgb2hex(hex2rgb(c("#FF8000", "#00FF00")))
#> [1] "#FF8000" "#00FF00"
```

```{r rgb2hex-round}
# Non-integer values are rounded
rgb2hex(c(254.7, 128.2, 0.4))
#> [1] "#FF8000"
```

Typical failure cases for the vector form:

```{r rgb2hex-error-vector}
# Wrong length
rgb2hex(c(255, 128))
#> Error in `rgb2hex()`:
#> ! `rgb` must be a numeric vector of length 3.

# Value out of range
rgb2hex(c(255, 128, 300))
#> Error in `rgb2hex()`:
#> ! `rgb` values must be in [0, 255].
```

And for the data frame form:

```{r rgb2hex-error-df}
# Missing column
rgb2hex(data.frame(r = 255, g = 128))
#> Error in `rgb2hex()`:
#> ! `rgb` must have columns {"r", "g", "b"}. Missing: {"b"}.

# Column value out of range
rgb2hex(data.frame(r = 255, g = 128, b = 300))
#> Error in `rgb2hex()`:
#> ! Column "b" in `rgb` must be numeric with values in [0, 255].
```

---

## 5 A Combined Workflow

The palette and conversion functions compose naturally. The example below picks
a qualitative palette, converts its colors to RGB for downstream processing,
then rounds-trips back to HEX to verify no information was lost.

```{r workflow}
library(evanverse)

# 1. Retrieve a qualitative palette
colors <- get_palette("qual_vivid", type = "qualitative", n = 4)
colors
#> [1] "#E64B35" "#4DBBD5" "#00A087" "#3C5488"

# 2. Convert to RGB for numeric manipulation
rgb_df <- hex2rgb(colors)
rgb_df
#>       hex   r   g   b
#> 1 #E64B35 230  75  53
#> 2 #4DBBD5  77 187 213
#> 3 #00A087   0 160 135
#> 4 #3C5488  60  84 136

# 3. Lighten each color by blending 50% toward white (255)
rgb_light <- rgb_df
rgb_light$r <- (rgb_light$r + 255) / 2
rgb_light$g <- (rgb_light$g + 255) / 2
rgb_light$b <- (rgb_light$b + 255) / 2

# 4. Convert back to HEX and preview
light_hex <- rgb2hex(rgb_light)
light_hex
#> [1] "#F2A59A" "#A6DDEA" "#80CFC7" "#9EAAC4"

# 5. Save the new derived palette for reuse
temp_dir <- file.path(tempdir(), "palettes")
create_palette("qual_vivid_light", "qualitative", light_hex,
               color_dir = temp_dir)
#> v Palette saved: /tmp/.../palettes/qualitative/qual_vivid_light.json

# 6. Verify the new palette is retrievable
"qual_vivid_light" %in% list_palettes(type = "qualitative")$name
#> [1] TRUE

# 7. Clean up
unlink(temp_dir, recursive = TRUE)
```

---

## Getting Help

- `?get_palette`, `?list_palettes`
- `?preview_palette`, `?palette_gallery`
- `?create_palette`, `?remove_palette`, `?compile_palettes`
- `?hex2rgb`, `?rgb2hex`
- [GitHub Issues](https://github.com/evanbio/evanverse/issues)
