// surface-profile.el - Surface profile data and accessors.
//
// THE NATIVE EFFERENT SEAM: surface = a pluggable PROFILE, using the exact same
// slot-map mechanism as language-profile.el. A language profile tells the
// realizer HOW to shape a natural-language surface (word order, morphology); a
// SURFACE profile tells the realizer WHICH surface to project meaning onto
// (markdown, docx, html, plain, or a non-text medium like symbolic music).
//
// The generalization is exact: realize_lang(form, profile) already renders a
// SemForm parameterized by a [String] profile read via lang_get. Surface is one
// more axis of that same profile vector. One frame (sem_frame), one plan step
// (sem_to_spec), one render (realize) — the surface is DATA, not a code path,
// precisely as language is data. Adding a surface means adding a profile, no
// engine change. This is the multimodal projector, native: geometry -> any
// surface, the efferent twin of ingest.
//
// Surface slot keys:
//   surface     - "markdown" | "docx" | "html" | "plain" | "midi" | "image"
//   modality    - "text" | "audio" | "image" | "video"
//   media_type  - MIME type of the emitted surface
//   head_open   - string prepended to a heading   (e.g. "## " for markdown)
//   head_close  - string appended  to a heading   (e.g. "" for markdown, "</h2>" for html)
//   emph_open   - string opening emphasis          (e.g. "*")
//   emph_close  - string closing emphasis          (e.g. "*")
//   item_mark   - list-item marker                 (e.g. "- ")
//   para_sep    - paragraph separator              (e.g. "\n\n")
//
// For a TEXT modality the render composes these markers around the surface that
// the EXISTING realizer produces (realize_lang / sem_realize). For a non-text
// modality (audio/image) the profile declares modality + media_type and the
// render dispatches to the medium projector, which reads the SAME frame's
// geometry (its intent/affect/structure) and projects it onto sound or pixels —
// deterministic-from-meaning, nothing invented. That dispatch point is where a
// music profile or image profile conforms, native, no parallel layer.

// -- Constructor -------------------------------------------------------------

fn surface_profile(surface: String, modality: String, media_type: String, head_open: String, head_close: String, emph_open: String, emph_close: String, item_mark: String, para_sep: String) -> [String] {
    let r: [String] = native_list_empty()
    let r = native_list_append(r, "surface")
    let r = native_list_append(r, surface)
    let r = native_list_append(r, "modality")
    let r = native_list_append(r, modality)
    let r = native_list_append(r, "media_type")
    let r = native_list_append(r, media_type)
    let r = native_list_append(r, "head_open")
    let r = native_list_append(r, head_open)
    let r = native_list_append(r, "head_close")
    let r = native_list_append(r, head_close)
    let r = native_list_append(r, "emph_open")
    let r = native_list_append(r, emph_open)
    let r = native_list_append(r, "emph_close")
    let r = native_list_append(r, emph_close)
    let r = native_list_append(r, "item_mark")
    let r = native_list_append(r, item_mark)
    let r = native_list_append(r, "para_sep")
    let r = native_list_append(r, para_sep)
    return r
}

// -- Accessor (same convention as lang_get; standalone so this is a leaf) -----

fn surface_get(profile: [String], key: String) -> String {
    let n: Int = native_list_len(profile)
    let i: Int = 0
    while i < n - 1 {
        let k: String = native_list_get(profile, i)
        if str_eq(k, key) {
            return native_list_get(profile, i + 1)
        }
        let i = i + 2
    }
    return ""
}

fn surface_is_text(profile: [String]) -> Bool {
    return str_eq(surface_get(profile, "modality"), "text")
}

// -- Built-in TEXT surface profiles ------------------------------------------

// Markdown: headings with "## ", emphasis with "*", "- " list items.
fn surface_profile_markdown() -> [String] {
    return surface_profile("markdown", "text", "text/markdown", "## ", "", "*", "*", "- ", "\n\n")
}

// Plain text: no markup at all — headings become bare uppercase-free lines.
fn surface_profile_plain() -> [String] {
    return surface_profile("plain", "text", "text/plain", "", "", "", "", "  - ", "\n\n")
}

// HTML: block-level heading/emphasis tags.
fn surface_profile_html() -> [String] {
    return surface_profile("html", "text", "text/html", "<h2>", "</h2>", "<em>", "</em>", "<li>", "\n")
}

// docx: WordprocessingML is structural, not inline-markup; the head/emph slots
// carry the run/style intent that the OOXML emitter maps to <w:pStyle>. Declared
// here so docx is a first-class surface on the same seam.
fn surface_profile_docx() -> [String] {
    return surface_profile("docx", "text", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "Heading2:", "", "b:", "", "bullet:", "\n")
}

// -- Built-in NON-TEXT surface profiles (the multimodal seam) ----------------

// Symbolic music (MIDI): modality=audio. The render dispatches to the music
// projector, which reads the SAME frame's intent/affect and projects it to
// pitch/rhythm — deterministic-from-meaning. head/emph slots are empty because
// the medium is not textual; media_type names the surface. A music profile
// (scale/mode/instrument) is layered onto this by the audio agent, native.
fn surface_profile_midi() -> [String] {
    return surface_profile("midi", "audio", "audio/midi", "", "", "", "", "", "")
}

// Synthesized audio (WAV): modality=audio, peer to midi. The richer audio
// surface — the render SUPERPOSES ingested tonal primitives (sine at f0*n per an
// ingested instrument signature) into PCM, own-core, exactly as midi writes an
// SMF via struct. A music profile (scale/mode/instrument/adsr) layers onto this
// as its own [String] slot-map read by the same getter. Same frame -> midi OR
// audio, interchangeable; this is the audio agent's native conforming point.
fn surface_profile_audio() -> [String] {
    return surface_profile("audio", "audio", "audio/wav", "", "", "", "", "", "")
}

// Image (raster): modality=image. Documented seam — the render dispatches to the
// image projector, the efferent twin of image ingest, reading the same frame.
fn surface_profile_image() -> [String] {
    return surface_profile("image", "image", "image/png", "", "", "", "", "", "")
}

// -- Composition helpers: wrap realized TEXT with the surface's markers -------
//
// These take text the EXISTING realizer already produced and shape it for the
// surface. They add NO content — pure surface typography over faithful text,
// exactly as the language profile adds no content, only linguistic form.

fn surface_heading(profile: [String], text: String) -> String {
    let o: String = surface_get(profile, "head_open")
    let c: String = surface_get(profile, "head_close")
    return o + text + c
}

fn surface_emph(profile: [String], text: String) -> String {
    let o: String = surface_get(profile, "emph_open")
    let c: String = surface_get(profile, "emph_close")
    return o + text + c
}

// A section: a heading + a paragraph separator + the (already realized) body.
fn surface_section(profile: [String], heading: String, body: String) -> String {
    let sep: String = surface_get(profile, "para_sep")
    return surface_heading(profile, heading) + sep + body
}
