package app

import (
	. "kjol/wasmruntime/vdom"
)

// The C layer's documentation.
//
// One page, like the component pages: a reader looking for the arena API should not have
// to guess which of six pages it was filed under. The sidebar calls the sections
// SUBSYSTEMS, which is what they are — each is a directory of C, and each has a different
// idea of what it depends on.
//
// Everything here is read off the source. Where a subsystem needs something the reader has
// to supply — a unity translation unit, a vendored ImGui, a resource script pointed at his
// own product — the page says what it needs and what to write, rather than filing it as a
// defect. This is a base layer: it is COPIED INTO a project and compiled with it, so "what
// you have to provide" is not a caveat, it is the interface.

type subsystem struct {
	ID    string
	Label string
	Icon  string
	Blurb string
}

// cSubsystems is the single source for the page's sections AND the sidebar that jumps to
// them — so the sidebar cannot offer a jump to a section that does not exist.
func cSubsystems() []subsystem {
	return []subsystem{
		{ID: "base", Label: "base", Icon: "cube",
			Blurb: "The vocabulary: fixed-width types, a bump allocator, counted strings, and 2D rectangle math."},
		{ID: "build", Label: "build", Icon: "bolt",
			Blurb: "A single-header build system. Your build script is a C program that recompiles itself."},
		{ID: "platform", Label: "platform", Icon: "server",
			Blurb: "A window, its input, the clipboard. Win32, and it hands every message to Dear ImGui first."},
		{ID: "lexer", Label: "lexer", Icon: "code",
			Blurb: "Syntax highlighting. Five languages, thirteen themes, and a paint array rather than a token list."},
		{ID: "config", Label: "config", Icon: "file",
			Blurb: "An INI file beside the executable. Program-managed state, not user-authored settings."},
		{ID: "installer", Label: "installer", Icon: "download",
			Blurb: "A Win32 wizard that carries the product inside itself as a resource."},
	}
}

// cNav is the sidebar while you are reading /c. The group is called Subsystems.
func cNav() []docsGroup {
	items := make([]docsItem, 0, len(cSubsystems()))
	for _, s := range cSubsystems() {
		items = append(items, docsItem{
			Path:  "/c#" + s.ID,
			Label: s.Label,
			Icon:  s.Icon,
			Blurb: s.Blurb,
		})
	}

	return []docsGroup{
		{
			Title: "Introduction",
			Items: []docsItem{
				{Path: "/c", Label: "Overview", Icon: "book-open",
					Blurb: "What this layer is, and where it came from."},
				{Path: "/c#compiling", Label: "Compiling it", Icon: "bolt",
					Blurb: "The unity translation unit: what you write, and what each subsystem needs from you."},
			},
		},
		{Title: "Subsystems", Items: items},
	}
}

//gowasm:page /c static layout=app
func CPage(d Deps) func() *VNode {
	return func() *VNode {
		return docPage("Layers", "Kjøl C",
			"A base layer in C — a bump allocator, counted strings, a syntax highlighter, a Win32 window, "+
				"and a build system that is itself a C program. It was lifted out of codeMAX, a code editor, "+
				"which is why the pieces are the pieces an editor needs.",

			docSection("what-this-is", "What this is",
				prose("Not a library you link against. A set of headers and translation units you COPY INTO "+
					"a project and compile with it — the base-layer style you will recognise if you have read "+
					"Ryan Fleury's RAD Debugger or watched Handmade Hero. The headers say as much: base_core, "+
					"base_arena and base_strings all credit raddebugger, and the naming (U32, Str8, Rng2F32) "+
					"comes straight from it."),
				prose("There is no package manager, no version, and no ABI to keep stable — because there is "+
					"nothing to keep stable BETWEEN. The code and its consumer are compiled together. Which is "+
					"also why the next section is about the file YOU write: the layer does not build on its "+
					"own, by design. It builds as part of your program."),

				note("It arrived from codeMAX, and still carries its names",
					"The whole directory came into Kjøl in one commit, out of codeMAX, a code editor. Its "+
						"fingerprints are still on it and they are worth knowing before you go looking: the "+
						"Win32 window class is codemax_wc, the installer installs codeMAX, and installer.rc "+
						"names codeMAX's icon, manifest and payload. Those are the strings to change when you "+
						"adopt it — they are the product's name, not the layer's."),
			),

			cCompiling(),
			cBase(),
			cBuild(),
			cPlatform(),
			cLexer(),
			cConfig(),
			cInstaller(),
		)
	}
}

// ---- compiling -----------------------------------------------------------

// How you actually compile the thing. It is second on the page, before any API, because a
// base layer's first question is "how does this get into my program" and the answer here is
// not the one a reader arriving from a package-manager language expects.
func cCompiling() *VNode {
	return docSection("compiling", "Compiling it",
		prose("One translation unit. You write a single .c that #includes the layer's .c files in order, "+
			"and hand THAT to the compiler — the whole layer is one TU, compiled with your program. "+
			"base/base_inc.c is the pattern in miniature: three lines, pulling in the arena and the "+
			"strings. Your own unity file is the same idea, one level up."),
		codeLang("app_inc.c — the file you write", "c", unityTUSnippet),
		prose("This is not a workaround for the absence of a build system; it IS the build system. There is "+
			"no per-file compilation, so there are no object files, no link order and no header guard "+
			"archaeology — and the compiler sees the whole layer at once, which is what makes every helper "+
			"in it `internal` and every call to one a candidate for inlining."),

		docSubheading("What each subsystem needs from you"),
		prose("They are not equally self-contained, and the differences are worth knowing before you pick "+
			"the ones you want:"),
		apiTable(
			apiRow{"base", "Nothing. It stands on its own — base_inc.c is a complete TU, and everything above depends on it."},
			apiRow{"lexer", "Include lexer.c AND the five backends in the same TU. Their helpers are `internal`, so separate compilation would leave lexer.c's dispatch with nothing to call."},
			apiRow{"config", "Include config.h above config.c. config.c uses Config without including its own header — which is exactly what a unity build lets it do."},
			apiRow{"platform", "Dear ImGui, vendored, and a C++ compiler for it. See the subsystem below — the window procedure hands it every message before it looks at any of them."},
			apiRow{"installer", "Its own program and its own .rc, pointed at your icon, your manifest and your payload."},
			apiRow{"build", "Nothing — it is the thing that runs the compiler. Write a build.c, compile it once, and never compile it again."},
		),

		note("There is no build.c in Kjøl yet, and that is the next job",
			"build.h is here, complete and self-contained, and nothing in the repository includes it — so "+
				"the layer currently has a build system and no build. Writing that build.c is what turns "+
				"this directory into something you can type one command at, and it is the single most "+
				"useful thing anyone could add to the C layer. The build subsystem below documents the "+
				"header it would be written against, and the snippet there is a working sketch of it."),
	)
}

// ---- base ----------------------------------------------------------------

func cBase() *VNode {
	return docSection("base", "base",
		prose("The vocabulary. Fixed-width integers with short names, a bump allocator, a counted string, "+
			"and the rectangle math a user interface actually spends its time on. Everything else in the "+
			"layer is written in these — which is why there is no malloc below this line, and no char* "+
			"pretending to be a string."),

		docSubheading("The types (base_core.h)"),
		prose("U8 through U64, S8 through S64, B32 for a boolean, F32 and F64 — upper case, not the u8/i32 "+
			"of the Rust-adjacent style. Sizes are written KB(4) and MB(64) rather than as a number of "+
			"zeroes you have to count. And the three meanings of `static` in C get three different names: "+
			"`internal` for a file-local function, `global` for a translation-unit variable, `local_persist` "+
			"for a local that survives the call. They all expand to `static`; they do not all mean the same "+
			"thing, and the code says which one it meant."),
		codeLang("c/base/base_core.h", "c", baseCoreSnippet),
		prose("There is more in the header than the types: Min/Max/Clamp, the nil-aware doubly and singly "+
			"linked-list macros from raddebugger (DLLPushBack, SLLQueuePush and friends), DeferLoop — which "+
			"is `defer` written as a for-loop, because C has no defer — and an Assert that traps on MSVC "+
			"with __debugbreak."),

		docSubheading("The arena (base_arena.h)"),
		prose("One allocator, and it is a bump pointer: arena_push moves a cursor and hands you the memory. "+
			"There is no free. You release the whole arena, or you roll it back to a saved position — which "+
			"is what Temp is. A function that wants scratch space opens a Temp, allocates as freely as it "+
			"likes, and closes it. Nothing has to be individually released, so nothing can be individually "+
			"forgotten."),
		codeLang("c/base/base_arena.h", "c", baseArenaSnippet),

		note("It is malloc-backed and fixed-capacity — and that has a sharp edge",
			"The header says so: this is raddebugger's arena with the virtual-memory reserve/commit taken "+
				"out. arena_alloc is one malloc of the capacity you asked for, and that capacity is final — "+
				"the arena does not grow and does not chain. Overflow hits Assert(!\"Arena overflow\") and "+
				"returns NULL. But Assert compiles to nothing unless _DEBUG is defined, so in a release build "+
				"an over-full arena hands you a silent NULL. Alignment is a hard-coded 8 bytes, so there is "+
				"no SIMD guarantee. And there is no thread-local scratch pool — if you came here expecting "+
				"raddebugger's scratch_begin, it is not in this snapshot."),

		docSubheading("Strings (base_strings.h)"),
		prose("Str8 is a pointer and a length. Not NUL-terminated, not owned, does not allocate — so a "+
			"substring is free, and a Str8 can point into the middle of a file you mapped. The two "+
			"operations that MUST allocate take the arena, and so they say so in their signature. That is "+
			"the whole API: eight functions. It is early, and there is no split, join, trim or "+
			"case-insensitive compare yet."),
		codeLang("c/base/base_strings.h", "c", baseStringsSnippet),

		docSubheading("Math (base_math.h)"),
		prose("Look at the shape of this file and it tells you what it is for. Vec3F32 has a constructor "+
			"and no operations. Rng2F32 — a rectangle — has nine: width, height, dim, center, contains, "+
			"pad, shift, intersect. There are no matrices, no quaternions, no Mat4. This is 2D interface "+
			"math, and a layout written in it is a sequence of rectangle operations rather than eight lines "+
			"of x + w arithmetic with an off-by-one hiding in them."),
		prose("The Axis2 / Side / Corner enums plus v2f32_axis are the raddebugger trick for axis-generic "+
			"code: one code path handles both X and Y by indexing into the vector instead of naming .x "+
			"and .y, so a horizontal and a vertical layout are the same function with a different argument."),

		apiTable(
			apiRow{"arena_alloc / arena_release", "One malloc, fixed capacity. It does not grow."},
			apiRow{"arena_push / arena_push_no_zero", "Bump the cursor. Zeroed by default; the fast one has to be asked for by name."},
			apiRow{"temp_begin / temp_end", "A scratch scope: save the position, allocate freely, roll it all back."},
			apiRow{"push_array(arena, T, n)", "Typed sugar over arena_push. The macro you will actually use."},
			apiRow{"str8 / str8_cstr / str8_lit", "Make a counted string. None of them allocate."},
			apiRow{"str8_pushf / str8_push_copy", "The two that DO allocate — and so they take the arena."},
			apiRow{"Rng2F32 + rng2f32_intersect / _pad / _shift", "Rectangles, and the three things a layout does to them."},
		),
	)
}

// ---- build ---------------------------------------------------------------

func cBuild() *VNode {
	return docSection("build", "build",
		prose("The build system is a C program, and the build system's build system is a C compiler. "+
			"build.h is a single header in the stb style: define BUILD_IMPLEMENTATION in one file, include "+
			"it, and write your build as a main() that shells out to a compiler. The lineage is nob.h — the "+
			"self-rebuild macro here is called GO_REBUILD_URSELF, which is a straight nod to it."),

		docSubheading("Bootstrapping"),
		prose("You compile the build script once, by hand. After that you never compile it again, because "+
			"the first thing it does when it runs is compare its own source against its own executable — "+
			"and if the source is newer, it rebuilds itself, swaps the binary, and re-executes. Edit the "+
			"build script and just run it. It will notice."),
		codeLang("terminal", "sh", buildBootstrapSnippet),

		note("On Windows you cannot overwrite a running .exe — so it doesn't",
			"go_rebuild_urself RENAMES the current binary to build.exe.old, compiles the new one in its "+
				"place, and re-execs. If the compile FAILS, it renames the old one back. That is the whole "+
				"reason the dance exists, and it is the difference between a build script you can edit and "+
				"one that bricks itself the first time you make a typo."),

		docSubheading("Writing one"),
		prose("GO_REBUILD_URSELF is the first line of main. Cmd is a growable argv you append to and run. "+
			"needs_rebuild compares timestamps, so a target whose inputs have not changed is skipped. That "+
			"is the whole of it — no rule syntax, no DSL, no dependency graph, because a C program already "+
			"has if-statements and for-loops and you already know how to write them."),
		codeLang("build.c — the file you write", "c", buildUsageSnippet),

		note("What it deliberately does NOT do",
			"There is no compiler detection: the compiler is cl.exe on Windows and cc everywhere else, "+
				"hard-coded, and only for the self-rebuild — for your own targets you assemble the command "+
				"yourself. There is no parallelism: cmd_run is synchronous, start to finish. There is no "+
				"globbing and no header-dependency scanning: needs_rebuild compares mtimes against the input "+
				"list YOU pass it, so if you edit a header that is not in that list, nothing rebuilds. Know "+
				"that going in and it is a fine tool; expect make and you will be bitten."),

		docSubheading("What else is in the header"),
		prose("More than a build system strictly needs, and all of it is there because a real build wanted "+
			"it. A temp allocator — an 8 MB ring buffer that silently wraps, so temp_sprintf can hand you a "+
			"formatted path that you never free. A string builder. File I/O that reports its own errors. "+
			"And two functions that are squarely about shipping a graphical program: embed_file, which "+
			"turns any binary into a C array in a header, and compile_shader / embed_spirv, which run glslc "+
			"over a GLSL source and embed the SPIR-V the same way. That last pair is a fossil of a Vulkan "+
			"renderer, and it is the clearest evidence in the layer of what codeMAX was becoming."),

		apiTable(
			apiRow{"GO_REBUILD_URSELF(argc, argv)", "First line of main. Rebuilds and re-executes the script if its source changed."},
			apiRow{"Cmd + cmd_append + cmd_run", "A growable argv, appended variadically, run synchronously. cmd_run resets it for reuse."},
			apiRow{"needs_rebuild(out, inputs, n)", "1 if any input is newer than the output, 0 if up to date, -1 on error. Mtimes only."},
			apiRow{"temp_sprintf / temp_reset", "Formatted strings from a ring buffer. You never free them; you reset the ring."},
			apiRow{"sb_read_file / write_entire_file", "Whole-file I/O into a String_Builder, and back out."},
			apiRow{"embed_file(in, out, var)", "Turn a binary into a C header: an array, and its size."},
			apiRow{"compile_shader / embed_spirv", "glslc a .glsl to .spv, then embed the .spv as a C array."},
		),
	)
}

// ---- platform ------------------------------------------------------------

func cPlatform() *VNode {
	return docSection("platform", "platform",
		prose("The operating system, behind one header — and it is a narrower header than the name "+
			"suggests. There is no file I/O here, no clock, no threads, and no virtual memory (which is why "+
			"the arena is malloc-backed). What there is: a window, the input that arrives at it, the "+
			"clipboard, and a way to spawn a terminal."),
		prose("Input comes in two shapes on purpose. PlatformRawInput is what the OS actually said — UTF-16 "+
			"characters, virtual key codes, mouse coordinates. PlatformInput is what the application wants "+
			"to hear. platform_adapt_input converts one into the other, and that function is the seam a "+
			"second backend would be written behind."),

		note("It expects Dear ImGui in the build, and Windows underneath",
			"platform_win32.c is the only backend, and its window procedure forward-declares "+
				"ImGui_ImplWin32_WndProcHandler and calls it on every message BEFORE it looks at any of "+
				"them — so ImGui gets first refusal on the input, which is what makes an ImGui text field "+
				"inside the window behave like a text field rather than a hole the editor's keybindings "+
				"fall through. To use this subsystem you vendor ImGui and compile it (it is C++) alongside; "+
				"to use it WITHOUT ImGui you cut that one call, and everything else in the file is C and "+
				"stands. A second backend would replace the PKEY_ enum — whose values ARE Windows virtual "+
				"key codes, passed straight through — and find a home for platform_spawn_terminal, which "+
				"hard-codes cmd.exe /k."),

		docSubheading("Two decisions worth stealing"),
		prose("WM_SIZE calls the frame callback synchronously, from inside the window procedure. That looks "+
			"wrong and is the standard Win32 fix for a real problem: while you drag a window's edge, "+
			"Windows runs a modal resize loop that never returns to your main loop, so the window goes "+
			"blank or smears. Rendering from inside the message handler is the only way to keep drawing. "+
			"platform_set_frame_callback exists for exactly this."),
		prose("And platform_get_input drains its accumulator on read — it memsets the buffered keys and "+
			"characters to zero on the way out, and returns was_mouse_down beside mouse_down so the caller "+
			"can see an edge without keeping its own copy of last frame."),

		apiTable(
			apiRow{"platform_create_window / _destroy_window", "A window from a PlatformWindowDesc; and its teardown."},
			apiRow{"platform_poll_events", "Pump the message queue. False means the user wants to close."},
			apiRow{"platform_get_input / platform_adapt_input", "The raw OS input for this frame; and the conversion that is the portability seam."},
			apiRow{"platform_set_frame_callback", "Called per frame — including from inside a live resize, which is the point."},
			apiRow{"platform_get_dpi_scale", "GetDpiForWindow / 96. The layer is per-monitor DPI aware throughout."},
			apiRow{"platform_clipboard_get / _set", "Get returns a pointer into a static 64 KB buffer. Do not free it, do not keep it."},
			apiRow{"platform_get_native_handle", "The HWND, for the one place that genuinely needs it."},
		),
	)
}

// ---- lexer ---------------------------------------------------------------

func cLexer() *VNode {
	return docSection("lexer", "lexer",
		prose("Syntax highlighting, and the most complete thing in this layer — about 2,800 lines of it. "+
			"The interesting decision is the output shape, and the header credits it to the Focus editor: a "+
			"tokenizer here does not return a list of tokens. It takes the buffer and an out_tokens array "+
			"of the SAME LENGTH, and paints one token-type byte per source byte."),
		codeLang("c/lexer/lexer.h", "c", lexerSnippet),
		prose("That sounds wasteful and is the opposite. An editor never wants to know what the tokens are; "+
			"it wants to know what colour to draw the character at offset N — and with a parallel array "+
			"that is one indexed read, not a search through a token list. The enum value IS the index into "+
			"the theme's colour table, which is why TOK_DEFAULT has to be zero. Painting is a memset. The "+
			"cost is one byte per byte of source, for a file you already have in memory."),

		note("It lets you rewrite the past, which is why it can find function names",
			"All five backends share one trick. When the tokenizer emits a `(` and the PREVIOUS token was "+
				"an identifier, it goes back and re-paints that identifier as TOK_FUNCTION. With a token "+
				"list you would have to look ahead, or fix up afterwards. With a paint array the past is "+
				"just an array range, and repainting it costs a memset."),

		docSubheading("Languages"),
		prose("Five backends — C, Go, JavaScript, Lua, SQL — plus plain text, each a single .c file behind "+
			"the same LexerTokenizeFn signature, selected by a hard-coded switch. There is no registry and "+
			"no plugin: adding a language means editing four things in lexer.c, deliberately."),
		prose("They are not toys. The JavaScript one carries an explicit depth stack for nested tagged "+
			"template literals, so an html`…${css`…${x}`}…` lexes correctly at arbitrary nesting, and it "+
			"has a regex-versus-division heuristic — after a keyword or an operator a slash starts a regex, "+
			"after an identifier it is a divide. The Lua one counts the equals signs in a long bracket so "+
			"[==[ is closed by ]==] and not by ]]. The SQL one is case-insensitive and treats \"quoted\" as "+
			"an identifier rather than a string."),

		docSubheading("Themes"),
		prose("A theme is a colour per token type, and there are thirteen: Default, Default Light, Focus, "+
			"Handmade Hero, Witness Classic, Witness, VS Classic, RAD Debugger, 4coder, Ryan Fleury, Gruber "+
			"Darker, VS Dark, Freshcut Contrast. The file is pure data — not one function in five hundred "+
			"lines."),
		note("The Theme struct is a fossil, and you can date the rock",
			"Every theme carries a 24-bit colour table AND a 256-colour ANSI fallback, with a use_truecolor "+
				"flag — because this started life in a TERMINAL editor, where you cannot assume truecolor. "+
				"But the platform layer beside it is a DPI-aware Win32 GUI window driven by ImGui, and the "+
				"struct has since grown status_bg, minibuffer and file-browser colours that mean nothing to "+
				"a terminal. It is a terminal-era struct with GUI-era fields bolted on. That is a seam, not "+
				"a design."),

		apiTable(
			apiRow{"LexerTokenizeFn(data, len, out_tokens)", "The one signature every backend implements. One byte of token type per byte of source."},
			apiRow{"lexer_get_tokenize_fn(lang)", "The backend for a language. A switch, not a registry."},
			apiRow{"lexer_detect_lang(filename)", "Language from the file extension."},
			apiRow{"Tokenizer + tokenizer_init / _eat_whitespace", "The shared cursor the backends are written against."},
			apiRow{"g_themes / theme_active()", "Thirteen built-in themes, and the active one."},
		),
	)
}

// ---- config --------------------------------------------------------------

func cConfig() *VNode {
	return docSection("config", "config",
		prose("An INI file — [sections], key = value — that lives NEXT TO THE EXECUTABLE rather than in a "+
			"home directory, which makes the program portable: copy the folder, keep your settings. It "+
			"parses into one global Config struct and saves back out of it."),
		codeLang("c/config/config.h", "c", configSnippet),

		prose("The struct is the schema, and it is a fixed-size one: ten recent directories, every path a "+
			"flat 1024 bytes. Nothing in this subsystem allocates — which is what lets it be loaded before "+
			"an arena exists. If the file is absent, config_load writes a default one."),

		note("It stores what the PROGRAM knows, not what the user wants",
			"The header is explicit about the split, and it is a good one: this file is managed by the "+
				"program — your window size, your recent folders, your active project — and per-project "+
				"settings belong in .editorconfig, where other tools can read them. A config file that tries "+
				"to be both ends up being neither."),

		note("It needs a writable directory beside the executable",
			"That is the one requirement the design carries, and it is easy to miss: the file is written "+
				"NEXT TO the .exe, and config_save does not check whether the write succeeded. Run out of a "+
				"folder you can write to — which is what \"copy the folder, keep your settings\" means — and "+
				"it does exactly what it says. Install the same binary into C:\\Program Files and a "+
				"non-elevated process cannot write there, so the save quietly does nothing. If you ship it "+
				"through the installer, either keep the config beside the exe and expect an elevated write, "+
				"or point config_get_path at %APPDATA% and give up the portability."),

		apiTable(
			apiRow{"config_load / config_save", "Read the INI into g_config; write g_config back out. Writes defaults if the file is absent."},
			apiRow{"config_get_path", "Beside the executable — GetModuleFileNameA, then strip the filename."},
			apiRow{"config_push_recent_dir", "Dedupes case-insensitively, moves the entry to the front, caps at ten, and saves itself."},
			apiRow{"g_config", "The one global. There is no second one."},
		),
	)
}

// ---- installer -----------------------------------------------------------

func cInstaller() *VNode {
	return docSection("installer", "installer",
		prose("A Windows installer AND uninstaller, in one executable, with the thing it installs embedded "+
			"inside it as a resource. No NSIS, no WiX, no MSI: it is a Win32 property-sheet wizard — "+
			"welcome, directory, progress, finish — that extracts the payload, copies itself to "+
			"uninstall.exe, optionally appends itself to the system PATH, writes a Start Menu shortcut via "+
			"COM, and registers under HKLM so it shows up in Add/Remove Programs."),
		prose("installer.manifest is what makes Windows ask for elevation UP FRONT rather than failing on "+
			"the first write to Program Files. installer.h exists solely so the resource script and the C "+
			"agree on the resource ids."),

		note("The wizard has no dialog resources — it writes the DLGTEMPLATEs by hand, at runtime",
			"There is a small serializer in installer.c that assembles the Win32 DLGTEMPLATE and "+
				"DLGITEMTEMPLATE binary layout byte by byte, alignment padding and all, and hands the result "+
				"to the property sheet with PSP_DLGINDIRECT. Which means the installer's entire user "+
				"interface needs no resource compiler: only the icon, the manifest and the payload go "+
				"through the .rc. That is either magnificent or deranged, and it is certainly deliberate."),

		note("A running .exe cannot delete itself, so the uninstaller doesn't",
			"It spawns a detached cmd.exe that waits two seconds, deletes uninstall.exe, and removes the "+
				"directory. The process outlives the program that started it, which is the only way this can "+
				"be done on Windows."),

		docSubheading("Pointing it at your own product"),
		prose("The .rc is the part you edit. It names codeMAX's icon, codeMAX's manifest and codeMAX's "+
			"payload, at the paths the original repository had them at — so adopting the installer means "+
			"pointing those three lines at your icon, your manifest and your executable, and changing the "+
			"product name and the HKLM key the uninstall entry is written under. Nothing else in installer.c "+
			"knows what it is installing."),
		prose("There is also visible residue of a two-binary product that was collapsed into one, and it is "+
			"worth recognising rather than copying: IDR_EXE_TERMINAL is still declared though nothing embeds "+
			"or extracts it, do_install's numbered steps skip step 3, and the uninstaller still removes a "+
			"second executable and a shortcut that the installer no longer creates. Those are the lines to "+
			"delete on the way in."),
	)
}

// ---- helpers -------------------------------------------------------------

// docSubheading is a heading INSIDE a section. The subsystems each have several parts, and
// six sections with no internal structure is a wall.
func docSubheading(text string) *VNode {
	return H3(Attr("class", "mt-8 text-base font-semibold text-text-heading"), Text(text))
}

// ---- snippets ------------------------------------------------------------
//
// Every one of these is copied from the source, except two — unityTUSnippet and
// buildUsageSnippet, which are the two files the layer expects its CONSUMER to write and
// so cannot be copied from a layer that has no consumer yet. Both are written against the
// real API and both are captioned as the file you write, not as a file that is here.
//
// If you change the source, change these. A snippet that has drifted from the code it
// claims to show is the most expensive documentation there is, because it is believed.

const unityTUSnippet = `// The whole layer, as one translation unit — the only .c you hand to the
// compiler. Order matters: it is textual inclusion, not linking.
#include "base/base_inc.c"     // core, arena, strings. Everything below needs it.

// The five backends declare their helpers ` + "`internal`" + ` (file-static), so they
// belong in the SAME TU as the dispatch that calls them.
#include "lexer/lexer.c"
#include "lexer/lexer_c.c"
#include "lexer/lexer_go.c"
#include "lexer/lexer_js.c"
#include "lexer/lexer_lua.c"
#include "lexer/lexer_sql.c"

// config.c uses Config without including its own header — which is exactly the
// thing a unity build is for. Put the header above it.
#include "config/config.h"
#include "config/config.c"

// ...and then your own program, compiled with all of it:
#include "app/app.c"`

const baseCoreSnippet = `// The three meanings of ` + "`static`" + ` in C, given three names.
#define internal      static   // a function private to this file
#define global        static   // a variable owned by this translation unit
#define local_persist static   // a local that survives the call

typedef uint8_t  U8;   typedef int8_t   S8;
typedef uint32_t U32;  typedef int32_t  S32;
typedef uint64_t U64;  typedef int64_t  S64;
typedef S32      B32;  // a boolean, sized so it packs predictably
typedef float    F32;  typedef double   F64;

#define KB(n) (((U64)(n)) << 10)
#define MB(n) (((U64)(n)) << 20)`

const baseArenaSnippet = `typedef struct Arena { U8 *base; U64 pos; U64 cap; } Arena;
typedef struct Temp  { Arena *arena; U64 pos; } Temp;

// One malloc, of exactly cap. It does not grow and it does not chain.
Arena *arena_alloc(U64 cap);
void  *arena_push(Arena *arena, U64 size);          // zeroed
void  *arena_push_no_zero(Arena *arena, U64 size);  // not
void   arena_pop_to(Arena *arena, U64 pos);

#define push_array(arena, T, count) ((T *)arena_push((arena), sizeof(T) * (count)))

// A scratch scope. Allocate as freely as you like inside it; none of it needs
// releasing, because temp_end rolls the cursor back over all of it at once.
Temp scratch = temp_begin(arena);
Node *nodes  = push_array(arena, Node, 1024);
temp_end(scratch);`

const baseStringsSnippet = `// A pointer and a length. Not NUL-terminated, not owned, does not allocate —
// so a substring is free, and a Str8 can point into a file you mapped.
typedef struct Str8 { const char *str; U64 size; } Str8;

static inline Str8 str8_lit(const char *s);
static inline B32  str8_match(Str8 a, Str8 b);
static inline B32  str8_is_empty(Str8 s);

// The two that must allocate take the arena, and so they say so:
Str8 str8_pushf(Arena *arena, const char *fmt, ...);
Str8 str8_push_copy(Arena *arena, Str8 s);`

const buildBootstrapSnippet = `# Once, by hand. (On Windows, from a Visual Studio developer prompt —
# it shells out to cl.exe.)
cl /nologo build.c        # Windows
cc build.c -o build       # macOS / Linux

# Ever after, just run it. If build.c is newer than the binary, the binary
# rebuilds itself, swaps in the new one, and re-executes:
./build`

const buildUsageSnippet = `#define BUILD_IMPLEMENTATION
#include "build.h"

int main(int argc, char **argv) {
    // Renames the running binary to .old, recompiles, and re-execs. If the
    // compile fails it puts the old one back — so a typo in your build
    // script cannot brick your build script.
    GO_REBUILD_URSELF(argc, argv);

    mkdir_if_not_exists("out");

    // needs_rebuild compares MTIMES against the list you pass it. There is
    // no header scanning: if a .h you depend on is not in this list, editing
    // it will not trigger a rebuild.
    const char *srcs[] = {"base/base_inc.c", "lexer/lexer.c"};
    if (needs_rebuild("out/app.exe", srcs, ARRAY_LEN(srcs)) > 0) {
        Cmd cmd = {0};
        cmd_append(&cmd, "cl", "/nologo", "/I.", "/Fe:out/app.exe");
        cmd_append(&cmd, srcs[0], srcs[1]);
        if (!cmd_run(&cmd)) return 1;   // synchronous; resets cmd for reuse
        cmd_free(&cmd);
    }

    build_log(LOG_INFO, "done");
    return 0;
}`

const lexerSnippet = `// A tokenizer does not RETURN tokens. It paints one token-type byte per
// source byte, into an array the same length as the buffer.
//
// The editor never asks "what are the tokens". It asks "what colour is the
// character at offset N" — and this answers that with one indexed read.
// The enum value IS the index into the theme's colour table, which is why
// TOK_DEFAULT has to be 0.
typedef void (*LexerTokenizeFn)(const char *data, S32 length, U8 *out_tokens);

typedef enum Lang { LANG_PLAIN_TEXT, LANG_C, LANG_GO, LANG_JS, LANG_LUA, LANG_SQL } Lang;

LexerTokenizeFn lexer_get_tokenize_fn(Lang lang);   // a switch, not a registry
Lang            lexer_detect_lang(const char *filename);`

const configSnippet = `// The struct IS the schema, and it is fixed-size throughout: this subsystem
// allocates nothing, so it can be loaded before an arena exists.
typedef struct Config {
    char theme[64];
    char recent_dirs[CONFIG_MAX_RECENT_DIRS][CONFIG_PATH_MAX];  // most recent first
    S32  recent_dir_count;
    B32  show_line_numbers;
    B32  syntax_enabled;
    F32  ui_scale;
    char editor_font[64];
    char active_project[CONFIG_PATH_MAX];
} Config;

extern Config g_config;          // the one global

static void config_load(void);   // <exe dir>/config.ini — not $HOME
static void config_save(void);`
