/ ===== 成果导出:CSV / 报告 / 存档 / 截图 ===== /
window.Exporter = (function () {

function download(filename, content, mime) {

const blob = content instanceof Blob ? content : new Blob([content], {type:mime || "text/plain;charset=utf-8"});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url; a.download = filename;
document.body.appendChild(a); a.click(); a.remove();
setTimeout(() => URL.revokeObjectURL(url), 3000);

}

const cell = v => /[",\n]/.test(String(v)) ? '"' + String(v).replace(/"/g, '""') + '"' : String(v);
const toCsv = rows => "\uFEFF" + rows.map(r => r.map(cell).join(",")).join("\r\n");
const stamp = () => new Date().toLocaleDateString("zh-CN").replace(///g, "-");
const projName = () => ($("#projName").value.trim() || "未命名项目");

const SRC_LABEL = {book:"导入清单", manual:"手工填写", ref:"内置经验估值(无来源)", "":"未填写"};

/ ---------- 苗木表 ---------- /
function plantCsv() {

const d = Calc.summary();
const rows = [["苗木配置表 —— " + projName(), "", "", "", "", "", "", "", "", "", ""],
  ["单价声明:本表单价由使用者填写或导入;标注「内置经验估值」的行无定额与造价信息价来源,不得用于报价、投标、结算。"],
  [],
  ["序号","品种","拉丁名","类型","规格","冠幅(m)","常绿/落叶","乡土","计量单位",
   "数量/面积","密度(株/m²)","损耗(%)","含损耗数量","单价(元)","单价来源","小计(元)"]];
S.plants.forEach((r, i) => {
  PlantUI.derive(r);
  rows.push([i + 1, r.name, r.latin, r.type, r.spec, r.cw, r.ev ? "常绿" : "落叶", r.na ? "乡土" : "引种",
    r.unit, r.qty, r.unit === "m²" ? r.density : "", r.loss,
    fmt(r._final, r._unitLabel === "m²" ? 1 : 0) + r._unitLabel,
    r.price > 0 ? r.price : "", SRC_LABEL[r.priceSrc || ""], r.price > 0 ? Math.round(r._sub) : ""]);
});
rows.push([]);
rows.push(["合计", "", "", "", "", "", "", "", "", "", "", "", "", "", "", Math.round(d.st.cost)]);
if (d.st.missing) rows.push(["注意:有 " + d.st.missing + " 行未填单价,合计金额不完整。"]);
download(projName() + "_苗木表_" + stamp() + ".csv", toCsv(rows), "text/csv;charset=utf-8");
toast("苗木表已导出");

}

/ ---------- 用地平衡表 ---------- /
function landCsv() {

const d = Calc.summary();
const rows = [["用地平衡表 —— " + projName()], [],
  ["用地类型", "面积(m²)", "占比(%)", "性质"]];
LAND_CATS.forEach(c => {
  const a = +S.land[c.k] || 0;
  rows.push([c.n, a.toFixed(2), d.landTotal > 0 ? (a / d.landTotal * 100).toFixed(2) : "", c.nature]);
});
rows.push(["合计", d.landTotal.toFixed(2), "100.00", ""]);
rows.push([]);
rows.push(["总用地面积(m²)", d.siteArea.toFixed(2)]);
rows.push(["绿地面积(m²)", d.greenArea.toFixed(2)]);
rows.push(["绿地率(%)", d.greenRate.toFixed(2)]);
rows.push(["绿化覆盖率(%)", d.coverRate.toFixed(2)]);
rows.push([]);
rows.push(["量算图元明细"]);
rows.push(["序号", "类型", "用地类型", "面积(m²)", "周长/长度(m)"]);
S.shapes.forEach((s, i) => {
  rows.push([i + 1, s.kind === "poly" ? "面" : "线", s.kind === "poly" ? catByKey(s.cat).n : "长度测量",
    s.area ? s.area.toFixed(2) : "", s.len ? s.len.toFixed(2) : ""]);
});
download(projName() + "_用地平衡表_" + stamp() + ".csv", toCsv(rows), "text/csv;charset=utf-8");
toast("用地平衡表已导出");

}

/ ---------- 造价概算表 ---------- /
function costCsv() {

const d = Calc.summary();
const r = Calc.costRows(d);
const rows = [["景观工程造价估算表 —— " + projName()],
  ["声明:本表为方案阶段快速估算。综合单价与系数为经验估值占位,无定额来源;苗木单价由使用者录入。不得作为报价、投标或结算依据。"],
  [],
  ["费用项", "工程量", "金额(元)", "占比(%)"]];
r.rows.forEach(x => rows.push([x[0], x[1], Math.round(x[2]), r.total > 0 ? (x[2] / r.total * 100).toFixed(2) : ""]));
rows.push(["工程总造价", "", Math.round(r.total), "100.00"]);
rows.push(["单位面积造价(元/m²)", fmt(d.siteArea, 0) + " m²", r.perSqm.toFixed(2), ""]);
rows.push([]);
rows.push(["单价参数", "取值"]);
[["种植施工费系数", S.cost.planting], ["一年养护费系数", S.cost.maint],
 ["绿地整理(元/m²)", S.cost.soil], ["铺装(元/m²)", S.cost.pave], ["园路(元/m²)", S.cost.road],
 ["水体(元/m²)", S.cost.water], ["水电灌溉(元/m²)", S.cost.mep], ["小品(元/m²)", S.cost.furn],
 ["不可预见费系数", S.cost.risk]].forEach(x => rows.push(x));
if (d.st.missing) rows.push(["注意", "有 " + d.st.missing + " 行苗木未填单价,苗木费被低估"]);
download(projName() + "_造价估算表_" + stamp() + ".csv", toCsv(rows), "text/csv;charset=utf-8");
toast("造价估算表已导出");

}

/ ---------- 技术经济指标报告 ---------- /
function esc(s) {

return String(s == null ? "" : s).replace(/[&<>"']/g, c =>
  ({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[c]));

}

function report() {

const d = recalcAll();
const checks = Calc.buildChecks(d);
const cost = Calc.costRows(d);
const refUsed = S.plants.some(p => p.priceSrc === "ref");
const imported = Object.keys(S.priceBook || {}).length > 0;

let priceNote;
if (d.st.cost <= 0) priceNote = "本报告<b>未包含有效苗木单价</b>(尚未录入),造价部分不成立,仅工程量与指标可用。";
else if (d.st.missing > 0) priceNote = "有 <b>" + d.st.missing + " 行</b>苗木未填单价,苗木费及总造价被低估,<b>不可对外使用</b>。";
else if (refUsed) priceNote = "部分单价采用工具内置<b>经验估值(无定额或造价信息价来源)</b>,须替换为当地信息价后方可对外。";
else priceNote = "苗木单价来源:" + (imported ? "使用者导入的价格清单及手工填写" : "使用者手工填写") + "。请自行确认价格口径(含税 / 运输 / 种植)。";

const kpiRows = [
  ["总用地面积", fmt(d.siteArea, 2), "m²"], ["绿地面积", fmt(d.greenArea, 2), "m²"],
  ["绿地率", fmt(d.greenRate, 2), "%"], ["绿化覆盖率", fmt(d.coverRate, 2), "%"],
  ["硬质铺装面积", fmt(d.paveArea, 2), "m²"], ["水体面积", fmt(d.waterArea, 2), "m²"],
  ["建筑构筑物占地", fmt(d.buildArea, 2), "m²"],
  ["乔木数量", fmt0(d.st.tree), "株"], ["乔木密度", fmt(d.treeDen, 2), "株/100m²"],
  ["灌木数量", fmt0(d.st.shrubQty), "株"], ["地被 / 草坪面积", fmt(d.st.groundArea, 2), "m²"],
  ["三维绿量", fmt(d.st.gv, 0), "m³"], ["单位绿地绿量", fmt(d.gvPerSqm, 2), "m³/m²"],
  ["乡土植物比例", fmt(d.nativeRate, 1), "%"], ["常绿乔木占比", fmt(d.everRatio, 1), "%"],
  ["草坪占绿地比例", fmt(d.lawnRatio, 1), "%"], ["观赏月份覆盖", d.bloomMonths, "月"]
];
if (d.m.population > 0) kpiRows.push(["人均绿地面积", fmt(d.perCapita, 2), "m²/人"]);

const tr = (cells, tag) => "<tr>" + cells.map(c => "<" + (tag || "td") + ">" + c + "</" + (tag || "td") + ">").join("") + "</tr>";

const html = `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">

${esc(projName())} · 景观技术经济指标报告

${esc(projName())}

项目类型:${esc(d.rules.label)} | 气候区:${esc(d.m.climate)} | 编制日期:${stamp()} | 工具:景观方案量算工作台
单价与造价声明:${priceNote}
本报告全部阈值为常用参考值,须以项目所在地现行规范、规划条件及审批要求为准。

一、技术经济指标

${tr(["指标项", "数值", "单位"], "th")}${kpiRows.map(r => tr([esc(r[0]), '' + r[1] + "", esc(r[2])])).join("")}

二、用地平衡表

${tr(["用地类型", "面积 (m²)", "占比", "性质"], "th")} ${LAND_CATS.map(c => { const a = +S.land[c.k] || 0; return tr([esc(c.n), fmt(a, 2), d.landTotal > 0 ? pct(a / d.landTotal * 100) : "—", esc(c.nature)]); }).join("")} ${tr(["合计", "" + fmt(d.landTotal, 2) + "", "100.0%", ""])}

三、规范校核

${tr(["核查项", "计算值", "参考阈值", "结论", "优化建议"], "th")} ${checks.map(c => tr([esc(c.name), fmt(c.val, 2) + " " + esc(c.unit), esc(c.thr), '' + (c.res === "ok" ? "通过" : c.res === "warn" ? "关注" : "不足") + " " + esc(c.resText), esc(c.tip)])).join("")}

四、苗木配置表

${tr(["序号", "品种", "类型", "规格", "计量", "数量", "单价 (元)", "单价来源", "小计 (元)"], "th")} ${S.plants.map((r, i) => { PlantUI.derive(r); return tr([i + 1, esc(r.name), esc(r.type), esc(r.spec), esc(r.unit), fmt(r._final, r._unitLabel === "m²" ? 1 : 0) + esc(r._unitLabel), r.price > 0 ? fmt(r.price, 2) : "—", esc(SRC_LABEL[r.priceSrc || ""]), r.price > 0 ? fmt(r._sub, 0) : "—"]); }).join("")} ${tr(["", "苗木费合计", "", "", "", "", "", "", "" + fmt(d.st.cost, 0) + ""])}

五、造价估算

${tr(["费用项", "工程量", "金额 (元)", "占比"], "th")} ${cost.rows.map(x => tr([esc(x[0]), esc(x[1]), fmt(x[2], 0), cost.total > 0 ? pct(x[2] / cost.total * 100) : "—"])).join("")} ${tr(["工程总造价", "", "" + fmt(cost.total, 0) + "", "100.0%"])} ${tr(["单位面积造价", fmt(d.siteArea, 0) + " m²", fmt(cost.perSqm, 2) + " 元/m²", ""])}
本报告由「景观方案量算工作台」根据使用者录入的图纸量算结果、苗木配置与单价自动生成,属方案阶段辅助估算,不替代施工图算量、定额组价与造价咨询成果。

`;

// 以 Blob URL 打开;被弹窗拦截时降级为下载 HTML 文件
const blob = new Blob([html], {type:"text/html;charset=utf-8"});
const url = URL.createObjectURL(blob);
const w = window.open(url, "_blank");
if (!w) {
  URL.revokeObjectURL(url);
  download(projName() + "_指标报告_" + stamp() + ".html", blob);
  toast("弹窗被拦截,报告已改为下载文件(双击打开后可打印 / 存 PDF)");
  return;
}
setTimeout(() => URL.revokeObjectURL(url), 60000);

}

/ ---------- 存档 / 截图 ---------- /
function json() {

download(projName() + "_工程存档_" + stamp() + ".json",
  JSON.stringify(collectState(), null, 1), "application/json");
toast("工程存档已导出(含图元坐标与价格清单)");

}
function png() {

if (!S.image.dataUrl) { toast("尚未载入平面图"); return; }
Measure.draw();
download(projName() + "_量算底图_" + stamp() + ".png", dataUrlToBlob(Measure.snapshot()));
toast("量算底图已导出");

}
function dataUrlToBlob(u) {

const [head, b64] = u.split(",");
const mime = /:(.*?);/.exec(head)[1];
const bin = atob(b64);
const arr = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i);
return new Blob([arr], {type:mime});

}

function bind() {

$("#outPlantCsv").onclick = plantCsv;
$("#outLandCsv").onclick = landCsv;
$("#outCostCsv").onclick = costCsv;
$("#outReport").onclick = report;
$("#outJson").onclick = json;
$("#outSnap").onclick = png;

}

return {bind, download, plantCsv, landCsv, costCsv, report, json};
})();