/* ============================================================================= * A00_Common/design/design_pipeline_page_saved.ts * 페이지 묶음의 **저장 기준** — 저장 정본 지문(노선 번호 · 지반 표본) · 종단도가 내준 저장 기준 · 저장 노선 * 계획고 편집 · 저장 PVI · 횡단 입력(cross). `design_pipeline_page.ts` 에서 갈라냄(700줄 한계 · 472-5). * ========================================================================== */ import type { SectionDetailResponse } from "../../B06_Section/B06_Section_Api_Fetch"; import { savedSectionDetail } from "../../B06_Section/B06_Section_Section_Canon"; import { emptyEdits, toAlignmentBase, type AlignmentEdits, } from "../../B05_Profile/logic/B05_Profile_UI_Profile_Alignment"; import { readAlignmentDraft } from "../../B05_Profile/B05_Profile_UI_Profile_Edit"; import { readAlignment } from "../../B05_Profile/logic/B05_Profile_UI_Profile_Data"; import { pipelineCrossSource, type PipelineCrossSource } from "./design_pipeline_cross"; import type { PipelineStation } from "./logic/design_pipeline_types"; import type { PagePipelineOptions, ProfileLink } from "./design_pipeline_page_types"; export function createPageSaved( projectId: string, link: ProfileLink, options: PagePipelineOptions, /** 저장 노선 측점(열쇠) — 페이지가 저장분을 받을 때 셈. */ savedStations: () => PipelineStation[], ) { /** 저장 정본 지문(노선 번호 · 지반 표본 배열) — 측점 갈아끼움(swap)이 만든 새 상세는 지반 표본 배열을 * 그대로 둠 → 정본 아님(묶음 출력 → 상세 → 다시 묶음 고리를 끊음). 상세 객체는 상세 가게 칸(472-5). */ let canon: { routeId: unknown; samples: unknown } | null = null; /** 저장 정본 상세 = 상세 가게 칸(가게가 받거나 되돌린 객체 · 갈아끼움 제외). */ const canonDetail = (): SectionDetailResponse | null => typeof canon?.routeId === "number" ? savedSectionDetail(projectId, canon.routeId) : null; /** 마지막으로 모은 횡단 입력(시설 제원 — 새 노선 상세가 씀). */ let crossSource: PipelineCrossSource | null = null; /** 이 묶음이 지은 새 노선 상세들 — 횡단 보기 카드가 그것을 종단도에 그려도 저장 정본이 아님. */ const drafted = new WeakSet(); let lastSaved: ReturnType | null = null; /** 종단도가 내준 저장 기준 — 종단도가 묶음 새 노선 상세를 그리는 동안은 그 앞 저장 기준(고리 끊음 · * 새 노선 상세 → 정본으로 오인 → 처음부터 다시 → 새 상세 … 349-0g). */ const savedSource = (): ReturnType => { const now = link.pipelineSource(); if (now.detail && drafted.has(now.detail)) return lastSaved ?? { ...now, detail: null }; return (lastSaved = now); }; /** 저장 정본이 바뀌었나(새로 받은 상세 · 새 노선) — 바뀌었으면 잡아 둠. */ const canonChanged = (): boolean => { const { detail = null, routeId } = savedSource(); const samples = detail?.longitudinal.samples; if (canon && canon.routeId === routeId && canon.samples === samples) return false; canon = { routeId, samples }; return true; }; /** 저장 노선 계획고 편집(세션 초안 → 저장분) — 노선 초안을 버리면 이것으로 돌아옴. */ const savedEdits = (): AlignmentEdits => { const routeId = canon?.routeId; const longitudinal = canonDetail()?.longitudinal; return ( (typeof routeId === "number" ? readAlignmentDraft(routeId) : null) ?? (longitudinal && readAlignment(longitudinal)?.edits) ?? emptyEdits() ); }; const storedPvi = () => { const detail = canonDetail(); const stored = detail && readAlignment(detail.longitudinal); return stored ? toAlignmentBase(stored).basePvi : null; }; /** 횡단 입력 — 부를 때마다 저장 상세 + 세션 고침 · 표준을 모음(상세 객체마다 기억하면 고침이 안 감 · 365-2). */ const crossNow = (): PipelineCrossSource | null => { const routeId = canon?.routeId; const source = pipelineCrossSource( projectId, canonDetail(), savedStations(), typeof routeId === "number" ? routeId : null, options.stationKeys, // 초안 자리 측점 짝(457) ); if (source) crossSource = source; return source; }; return { savedSource, canonChanged, savedEdits, storedPvi, crossNow, drafted, /** 저장 정본 노선 번호(정본을 아직 안 잡았으면 undefined). */ canonRouteId: (): unknown => canon?.routeId, /** 초안을 버려 되돌린 상세 = 저장 정본(다시 하기도 같은 기준). */ adoptCanon(routeId: unknown, detail: SectionDetailResponse): void { canon = { routeId, samples: detail.longitudinal.samples }; }, /** 마지막 횡단 입력의 시설 제원(새 노선 상세가 씀). */ crossSpecs: () => crossSource?.specs ?? new Map(), /** 저장분을 다시 받음 — 횡단 입력도 다시 모음. */ forgetCross(): void { crossSource = null; }, }; }