using System;
using IslaApocalypse.Core;
namespace IslaApocalypse.Tools
{
///
/// ⭐⭐ THE LOCKED SHAPE — `terrain-shape-v1` (commit a59e52f), AS AN ASSERTION.
///
/// ═══ ⚠⚠ THIS TYPE INVERTED AT rivers/01. READ THIS BEFORE USING IT. ═══
///
/// It used to be a PRESET: Apply(cfg) stamped the locked shape onto a bare config, because
/// the bare defaults did not reproduce the terrain the developer had judged. That was the single
/// most expensive fact in the codebase — *"run the default generator" ≠ "the terrain the developer
/// locked"* — and a fresh chat comparing bare-default output against the locked renders would see
/// differences that were CONFIGURATION, not regression.
///
/// **rivers/01 re-baselined the defaults so that `new TerrainGenConfig()` IS the locked shape.**
/// So Apply is GONE — there is nothing left to apply, and re-stamping the values on top of
/// the defaults would mask a default drift instead of catching it.
///
/// > ### What survives is the OPPOSITE job: these constants are now the ASSERTION TARGET.
/// > They are the values the developer judged, written down once, and holds
/// > the live defaults against them. If a default is ever edited, the batch tools that claim to
/// > render the locked shape REFUSE TO RUN rather than quietly rendering something else.
///
/// ⚠ DO NOT "fix" a drift by editing these constants — they are the record of what was approved,
/// and `10_frag4_seed_gallery` / `11_erosion` are its pixels. A deliberate shape change moves the
/// defaults AND these constants AND re-runs the acceptance, in one task, as rivers/01 did.
///
/// ⚠ THE SHELF IS OFF, DELIBERATELY. The locked shape has no coast shelf; evaluating it (→ D-041)
/// is its own later task, once water renders. ⚠ THE ISLETS ARE OFF, PERMANENTLY (→ D-063): islands
/// are organic-only, made by the stretch + fragmentation and identified by the region layer.
///
/// ⚠ EROSION IS NOT PART OF THIS SHAPE. `terrain-shape-v1` is the pass-1/1c/2a field; erosion is
/// pass 2b, on top, render-only (`11_erosion` = `ea291ea`). It is asserted separately.
///
public static class TerrainShapeV1
{
/// The tagged commit this shape is defined by. ⚠ Reference the COMMIT — the tag is annotated and local-only until the developer pushes it.
public const string Commit = "a59e52f";
public const float FragmentAmp = 0.5f, FragmentFreq = 12f, BandCentre = 0.66f, BandHalfWidth = 0.18f;
public const bool BitesOnly = false;
public const float Stretch = 2f, BandStart = 0.70f, BandFeather = 0.05f;
public const bool StretchSinker = true;
public const float SpeckFrac = 2.5e-7f;
///
/// ⭐ THE DEFAULT-DRIFT GUARD. Throws unless a bare carries the
/// locked shape exactly. Every batch tool that renders or asserts `terrain-shape-v1` calls this
/// before it generates anything.
///
/// ⚠ It is a THROW, not a warning, for the same reason is: a batch
/// that renders the wrong terrain still produces beautiful, browsable, wrong PNGs, and a human
/// gate cannot see a default from a picture.
///
/// The calling tool, for the message.
public static void Assert(string who)
{
var d = new TerrainGenConfig();
string bad = null;
void Want(string name, object got, object want)
{
if (!Equals(got, want)) bad = (bad == null ? "" : bad + "; ") + $"{name} = {got}, expected {want}";
}
Want(nameof(d.SouthStretch), d.SouthStretch, Stretch);
Want(nameof(d.SouthBandStartFrac), d.SouthBandStartFrac, BandStart);
Want(nameof(d.SouthBandFeatherFrac), d.SouthBandFeatherFrac, BandFeather);
Want(nameof(d.StretchSinker), d.StretchSinker, StretchSinker);
Want(nameof(d.FragmentAmp), d.FragmentAmp, FragmentAmp);
Want(nameof(d.FragmentFreqPerMapWidth), d.FragmentFreqPerMapWidth, FragmentFreq);
Want(nameof(d.FragmentBandCentre), d.FragmentBandCentre, BandCentre);
Want(nameof(d.FragmentBandHalfWidth), d.FragmentBandHalfWidth, BandHalfWidth);
Want(nameof(d.FragmentBitesOnly), d.FragmentBitesOnly, BitesOnly);
Want(nameof(d.SpeckRevert), d.SpeckRevert, true);
Want(nameof(d.MinLandComponentFrac), d.MinLandComponentFrac, SpeckFrac);
Want(nameof(d.CoastShelf), d.CoastShelf, false);
Want(nameof(d.RegionLabeling), d.RegionLabeling, true);
Want("Offshore.Mode", d.Offshore.Mode, OffshoreMode.Off);
if (bad != null)
throw new InvalidOperationException(
$"[{who}] LOCKED-SHAPE DRIFT: the bare TerrainGenConfig no longer reproduces " +
$"terrain-shape-v1 ({Commit}) — {bad}. Refusing to run: this tool's output is only " +
"meaningful if the defaults ARE the locked shape. → Tools/Scripts/TerrainShapeV1.cs (rivers/01).");
}
///
/// The same guard for the EROSION default, kept separate because erosion is pass 2b and is not
/// part of the shape. Tools that render the erosion A/B set the flag per variant and call this
/// only if they rely on the default.
///
public static void AssertErosionDefaultOn(string who)
{
if (!new TerrainGenConfig().Erosion)
throw new InvalidOperationException(
$"[{who}] EROSION DEFAULT DRIFT: bare TerrainGenConfig.Erosion is false; rivers/01 " +
"made it true (the rivers baseline routes on the eroded render field). Refusing to run.");
}
///
/// One line for a run header, READ FROM THE LIVE DEFAULTS rather than from the constants —
/// so the header states what actually ran, and states whether that is
/// still the locked shape.
///
public static string Describe()
{
var d = new TerrainGenConfig();
return $"terrain-shape-v1 ({Commit}) from the BARE DEFAULTS: frag amp {d.FragmentAmp} freq {d.FragmentFreqPerMapWidth} " +
$"window {d.FragmentBandCentre}±{d.FragmentBandHalfWidth} · stretch {d.SouthStretch} " +
$"(band {d.SouthBandStartFrac}/{d.SouthBandFeatherFrac}, sinker {(d.StretchSinker ? "stretched" : "real-y")}) · " +
$"speck revert {d.MinLandComponentFrac:G2} · offshore {d.Offshore.Mode} · shelf {(d.CoastShelf ? "ON" : "OFF")} · labeling {(d.RegionLabeling ? "ON" : "OFF")}";
}
}
}