"""Конвертирует нативный .archimate (формат Archi) в The Open Group
ArchiMate Model Exchange File Format — его понимают сторонние инструменты.

Вложенность узлов на схемах разворачивается в плоский список с абсолютными
координатами: в обменном формате трактовка координат вложенных узлов
неоднозначна, а плоская раскладка даёт одинаковый результат везде.
Контейнер выводится раньше содержимого, поэтому визуально вложенность
сохраняется.
"""
import sys, io, pathlib
import xml.etree.ElementTree as ET
from xml.sax.saxutils import escape, quoteattr

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
XSI = "{http://www.w3.org/2001/XMLSchema-instance}type"
NS = "http://www.opengroup.org/xsd/archimate/3.0/"

src, dst = pathlib.Path(sys.argv[1]), pathlib.Path(sys.argv[2])
root = ET.parse(src).getroot()

def t(e):
    return (e.get(XSI) or "").split(":")[-1]

def text(e, tag):
    c = e.find(tag)
    return c.text if c is not None and c.text else ""

elements, relationships, views = [], [], []

for folder in root.findall("folder"):
    ftype = folder.get("type")
    for e in folder.findall("element"):
        et = t(e)
        if ftype == "diagrams":
            views.append(e)
        elif et.endswith("Relationship"):
            relationships.append((e.get("id"), et[:-len("Relationship")],
                                  e.get("source"), e.get("target")))
        else:
            elements.append((e.get("id"), et, e.get("name") or "", text(e, "documentation")))

def flatten(node, ox=0, oy=0, out=None, conns=None):
    """Возвращает плоский список узлов с абсолютными координатами и соединения."""
    out = [] if out is None else out
    conns = [] if conns is None else conns
    b = node.find("bounds")
    x = ox + int(b.get("x", 0)); y = oy + int(b.get("y", 0))
    # у надписей (Note) нет archimateElement — в обменном формате это узел типа Label
    out.append((node.get("id"), node.get("archimateElement"),
                x, y, int(b.get("width", 120)), int(b.get("height", 55)),
                text(node, "content")))
    for c in node.findall("sourceConnection"):
        conns.append((c.get("id"), c.get("relationship"),
                      c.get("source"), c.get("target")))
    for ch in node.findall("child"):
        flatten(ch, x, y, out, conns)
    return out, conns

out = ['<?xml version="1.0" encoding="UTF-8"?>',
       f'<model xmlns="{NS}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
       f'       xsi:schemaLocation="{NS} https://www.opengroup.org/xsd/archimate/3.1/archimate3_Diagram.xsd"',
       f'       identifier="{root.get("id")}">',
       f'  <name xml:lang="ru">{escape(root.get("name") or "model")}</name>']
purpose = root.find("purpose")
if purpose is not None and purpose.text:
    out.append(f'  <documentation xml:lang="ru">{escape(purpose.text)}</documentation>')

out.append("  <elements>")
for eid, etype, name, doc in elements:
    out.append(f'    <element identifier="{eid}" xsi:type="{etype}">')
    out.append(f'      <name xml:lang="ru">{escape(name)}</name>')
    if doc:
        out.append(f'      <documentation xml:lang="ru">{escape(doc)}</documentation>')
    out.append("    </element>")
out.append("  </elements>")

out.append("  <relationships>")
for rid, rtype, s, tg in relationships:
    out.append(f'    <relationship identifier="{rid}" source="{s}" target="{tg}" xsi:type="{rtype}"/>')
out.append("  </relationships>")

out.append("  <views>")
out.append("    <diagrams>")
for v in views:
    out.append(f'      <view identifier="{v.get("id")}" xsi:type="Diagram">')
    out.append(f'        <name xml:lang="ru">{escape(v.get("name") or "")}</name>')
    doc = text(v, "documentation")
    if doc:
        out.append(f'        <documentation xml:lang="ru">{escape(doc)}</documentation>')
    nodes, conns = [], []
    for child in v.findall("child"):
        flatten(child, 0, 0, nodes, conns)
    for nid, eref, x, y, w, h, content in nodes:
        if eref:
            out.append(f'        <node identifier="{nid}" elementRef="{eref}" xsi:type="Element" '
                       f'x="{x}" y="{y}" w="{w}" h="{h}"/>')
        else:
            out.append(f'        <node identifier="{nid}" xsi:type="Label" '
                       f'x="{x}" y="{y}" w="{w}" h="{h}">')
            out.append(f'          <label xml:lang="ru">{escape(content)}</label>')
            out.append('        </node>')
    for cid, rref, s, tg in conns:
        out.append(f'        <connection identifier="{cid}" relationshipRef="{rref}" '
                   f'xsi:type="Relationship" source="{s}" target="{tg}"/>')
    out.append("      </view>")
out.append("    </diagrams>")
out.append("  </views>")
out.append("</model>")

dst.write_text("\n".join(out) + "\n", encoding="utf-8", newline="\n")
print(f"записано: {dst}")
print(f"  элементов:     {len(elements)}")
print(f"  отношений:     {len(relationships)}")
print(f"  представлений: {len(views)}")
