import xml.etree.ElementTree as ET

def make_qspi_quad_io_mxfile():
    # ========== 基础参数定义 ==========
    # Y坐标（高/低电平）：严格遵循垂直间距100px规范
    y_cs_high, y_cs_low = 80, 110
    y_sclk_high, y_sclk_low = 180, 210
    y_io0_high, y_io0_low = 280, 310
    y_io1_high, y_io1_low = 380, 410
    y_io2_high, y_io2_low = 480, 510
    y_io3_high, y_io3_low = 580, 610
    
    # 时序参数：50px/周期，总周期数=8(命令)+3(地址)+2(空周期)+2(数据)=15
    cycle_w = 50
    start_x = 100
    n_cycles = 15  
    end_x = start_x + n_cycles * cycle_w  # 100 + 15*50 = 850
    tail_x = end_x + 50  # 900
    
    # 时钟边沿计算（上升沿=采样沿，下降沿=输出沿）
    rise_edges = [start_x + i*cycle_w + cycle_w//2 for i in range(n_cycles)]  # 125,175...825
    fall_edges = [start_x + (i+1)*cycle_w for i in range(n_cycles)]          # 150,200...850
    
    # ========== 数据定义 ==========
    # 1. 命令阶段：IO0传输0xEB (11101011)，MSB first；IO1-IO3 idle(0)
    cmd_bits_io0 = [1,1,1,0,1,0,1,1]  # 0xEB = b11101011
    cmd_bits_io1 = [0]*8
    cmd_bits_io2 = [0]*8
    cmd_bits_io3 = [0]*8
    
    # 2. 地址阶段：3个周期，4线并行传输地址位（示例地址0x123456的高12位）
    # 地址0x123456 = 24位：0001 0010 0011 0100 0101 0110
    # A23=0,A22=0,A21=0,A20=1 | A19=0,A18=0,A17=1,A16=0 | A15=0,A14=0,A13=1,A12=1
    addr_bits_io0 = [0, 0, 0]  # A23, A19, A15
    addr_bits_io1 = [0, 0, 0]  # A22, A18, A14
    addr_bits_io2 = [0, 1, 1]  # A21, A17, A13
    addr_bits_io3 = [1, 0, 1]  # A20, A16, A12
    
    # 3. 空周期：2个周期，所有IO idle(0)
    dummy_bits = [0]*2
    
    # 4. 数据阶段：2个周期传输1字节(0xAA=10101010)，4线并行
    # D0=1,D1=0,D2=1,D3=0 | D4=1,D5=0,D6=1,D7=0
    data_bits_io0 = [1, 1]  # D0, D4
    data_bits_io1 = [0, 0]  # D1, D5
    data_bits_io2 = [1, 1]  # D2, D6
    data_bits_io3 = [0, 0]  # D3, D7
    
    # 合并所有阶段的bit序列
    all_io0 = cmd_bits_io0 + addr_bits_io0 + dummy_bits + data_bits_io0
    all_io1 = cmd_bits_io1 + addr_bits_io1 + dummy_bits + data_bits_io1
    all_io2 = cmd_bits_io2 + addr_bits_io2 + dummy_bits + data_bits_io2
    all_io3 = cmd_bits_io3 + addr_bits_io3 + dummy_bits + data_bits_io3

    # ========== 波形构建函数 ==========
    def build_wave_pts(bits, y_hi, y_lo):
        """生成波形点，确保无斜线、边沿对齐"""
        y0 = y_hi if bits[0] else y_lo
        pts = [(80, y0), (start_x, y0)]  # 起始过渡段
        for i in range(len(bits)-1):
            curr_fall = fall_edges[i]
            y_curr = y_hi if bits[i] else y_lo
            y_next = y_hi if bits[i+1] else y_lo
            
            # 添加当前bit的水平段终点（下降沿）
            pts.append((curr_fall, y_curr))
            # 电平变化则在下降沿垂直跳变
            if y_next != y_curr:
                pts.append((curr_fall, y_next))
        # 最后一个bit的终点 + 尾部过渡段
        last_fall = fall_edges[len(bits)-1]
        pts.append((last_fall, y_hi if bits[-1] else y_lo))
        pts.append((tail_x, pts[-1][1]))
        return pts

    # ========== 生成XML内容 ==========
    lines = []
    # 文件头
    lines.append('<?xml version="1.0" encoding="UTF-8"?>')
    lines.append('<mxfile host="app.diagrams.net" modified="2024-05-25T00:00:00.000Z" agent="AI" version="21.0.0" type="device">')
    lines.append('  <diagram name="QSPI Quad I/O Fast Read (0xEB)" id="qspi_quad_io">')
    lines.append('    <mxGraphModel dx="1000" dy="700" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1000" pageHeight="700" math="0" shadow="0">')
    lines.append('      <root>')
    lines.append('        <mxCell id="0"/>')
    lines.append('        <mxCell id="1" parent="0"/>')
    
    # 背景网格
    lines.append('        <mxCell id="grid" value="" style="points=[];gridColor=#e0e0e0;gridSize=20;spacingTop=20;spacingLeft=20;spacingBottom=20;spacingRight=20;html=1;" vertex="1" parent="1">')
    lines.append(f'          <mxGeometry x="0" y="0" width="1000" height="700" as="geometry"/>')
    lines.append('        </mxCell>')
    
    # 标题
    lines.append('        <mxCell id="title" value="QSPI Quad I/O Fast Read (命令 0xEB)" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1;" vertex="1" parent="1">')
    lines.append('            <mxGeometry x="300" y="20" width="400" height="30" as="geometry"/>')
    lines.append('        </mxCell>')
    
    # MSB/LSB标记
    lines.append('        <mxCell id="msb" value="MSB" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontColor=#666666;fontStyle=1;" vertex="1" parent="1">')
    lines.append(f'            <mxGeometry x="{start_x+10}" y="60" width="40" height="20" as="geometry"/>')
    lines.append('        </mxCell>')
    lines.append('        <mxCell id="lsb" value="LSB" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontColor=#666666;fontStyle=1;" vertex="1" parent="1">')
    lines.append(f'            <mxGeometry x="{end_x-40}" y="60" width="40" height="20" as="geometry"/>')
    lines.append('        </mxCell>')
    
    # 信号标签（左侧）
    signal_labels = [
        ("CS", 85, "label_cs"),
        ("SCLK", 185, "label_sclk"),
        ("IO0", 275, "label_io0"),
        ("IO1", 375, "label_io1"),
        ("IO2", 475, "label_io2"),
        ("IO3", 575, "label_io3")
    ]
    for label, y, eid in signal_labels:
        lines.append(f'        <mxCell id="{eid}" value="{label}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontStyle=1;" vertex="1" parent="1">')
        lines.append(f'                <mxGeometry x="20" y="{y}" width="50" height="20" as="geometry"/>')
        lines.append('        </mxCell>')
    
    # 阶段分界虚线（命令/地址/空周期/数据）
    boundaries = [
        (start_x + 8*cycle_w, "cmd_addr"),   # 命令结束: 100+400=500
        (start_x + 11*cycle_w, "addr_dummy"),# 地址结束: 100+550=650
        (start_x + 13*cycle_w, "dummy_data"),# 空周期结束:100+650=750
    ]
    for x, eid in boundaries:
        lines.append(f'        <mxCell id="vl_{eid}" value="" style="endArrow=none;html=1;strokeWidth=1;strokeColor=#CCCCCC;dashed=1;" edge="1" parent="1">')
        lines.append('          <mxGeometry width="50" height="50" relative="1" as="geometry">')
        lines.append(f'            <mxPoint x="{x}" y="180" as="sourcePoint"/>')
        lines.append(f'            <mxPoint x="{x}" y="620" as="targetPoint"/>')
        lines.append('          </mxGeometry>')
        lines.append('        </mxCell>')
    
    # ========== CS 波形 ==========
    cs_pts = [(80, y_cs_high), (90, y_cs_high), (90, y_cs_low), 
              (tail_x, y_cs_low), (tail_x, y_cs_high), (tail_x+10, y_cs_high)]
    lines.append('        <mxCell id="cs" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#FF5722;rounded=0;endArrow=none;" edge="1" parent="1">')
    lines.append('          <mxGeometry relative="0" as="geometry">')
    lines.append(f'            <mxPoint x="{cs_pts[0][0]}" y="{cs_pts[0][1]}" as="sourcePoint"/>')
    lines.append(f'            <mxPoint x="{cs_pts[-1][0]}" y="{cs_pts[-1][1]}" as="targetPoint"/>')
    lines.append('            <Array as="points">')
    for x, y in cs_pts[1:-1]:
        lines.append(f'              <mxPoint x="{x}" y="{y}"/>')
    lines.append('            </Array>')
    lines.append('          </mxGeometry>')
    lines.append('        </mxCell>')
    
    # ========== SCLK 波形 ==========
    sclk_pts = [(80, y_sclk_low), (start_x, y_sclk_low)]
    for i in range(n_cycles):
        rise = rise_edges[i]
        fall = fall_edges[i]
        sclk_pts.extend([
            (rise, y_sclk_low),
            (rise, y_sclk_high),
            (fall, y_sclk_high),
            (fall, y_sclk_low),
        ])
    sclk_pts.append((tail_x, y_sclk_low))
    
    lines.append('        <mxCell id="sclk" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#2196F3;rounded=0;endArrow=none;" edge="1" parent="1">')
    lines.append('          <mxGeometry relative="0" as="geometry">')
    lines.append(f'            <mxPoint x="{sclk_pts[0][0]}" y="{sclk_pts[0][1]}" as="sourcePoint"/>')
    lines.append(f'            <mxPoint x="{sclk_pts[-1][0]}" y="{sclk_pts[-1][1]}" as="targetPoint"/>')
    lines.append('            <Array as="points">')
    for x, y in sclk_pts[1:-1]:
        lines.append(f'              <mxPoint x="{x}" y="{y}"/>')
    lines.append('            </Array>')
    lines.append('          </mxGeometry>')
    lines.append('        </mxCell>')
    
    # ========== IO0 波形 ==========
    io0_pts = build_wave_pts(all_io0, y_io0_high, y_io0_low)
    lines.append('        <mxCell id="io0" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#4CAF50;rounded=0;endArrow=none;" edge="1" parent="1">')
    lines.append('          <mxGeometry relative="0" as="geometry">')
    lines.append(f'            <mxPoint x="{io0_pts[0][0]}" y="{io0_pts[0][1]}" as="sourcePoint"/>')
    lines.append(f'            <mxPoint x="{io0_pts[-1][0]}" y="{io0_pts[-1][1]}" as="targetPoint"/>')
    lines.append('            <Array as="points">')
    for x, y in io0_pts[1:-1]:
        lines.append(f'              <mxPoint x="{x}" y="{y}"/>')
    lines.append('            </Array>')
    lines.append('          </mxGeometry>')
    lines.append('        </mxCell>')
    
    # ========== IO1 波形 ==========
    io1_pts = build_wave_pts(all_io1, y_io1_high, y_io1_low)
    lines.append('        <mxCell id="io1" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#9C27B0;rounded=0;endArrow=none;" edge="1" parent="1">')
    lines.append('          <mxGeometry relative="0" as="geometry">')
    lines.append(f'            <mxPoint x="{io1_pts[0][0]}" y="{io1_pts[0][1]}" as="sourcePoint"/>')
    lines.append(f'            <mxPoint x="{io1_pts[-1][0]}" y="{io1_pts[-1][1]}" as="targetPoint"/>')
    lines.append('            <Array as="points">')
    for x, y in io1_pts[1:-1]:
        lines.append(f'              <mxPoint x="{x}" y="{y}"/>')
    lines.append('            </Array>')
    lines.append('          </mxGeometry>')
    lines.append('        </mxCell>')
    
    # ========== IO2 波形 ==========
    io2_pts = build_wave_pts(all_io2, y_io2_high, y_io2_low)
    lines.append('        <mxCell id="io2" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#FFC107;rounded=0;endArrow=none;" edge="1" parent="1">')
    lines.append('          <mxGeometry relative="0" as="geometry">')
    lines.append(f'            <mxPoint x="{io2_pts[0][0]}" y="{io2_pts[0][1]}" as="sourcePoint"/>')
    lines.append(f'            <mxPoint x="{io2_pts[-1][0]}" y="{io2_pts[-1][1]}" as="targetPoint"/>')
    lines.append('            <Array as="points">')
    for x, y in io2_pts[1:-1]:
        lines.append(f'              <mxPoint x="{x}" y="{y}"/>')
    lines.append('            </Array>')
    lines.append('          </mxGeometry>')
    lines.append('        </mxCell>')
    
    # ========== IO3 波形 ==========
    io3_pts = build_wave_pts(all_io3, y_io3_high, y_io3_low)
    lines.append('        <mxCell id="io3" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#607D8B;rounded=0;endArrow=none;" edge="1" parent="1">')
    lines.append('          <mxGeometry relative="0" as="geometry">')
    lines.append(f'            <mxPoint x="{io3_pts[0][0]}" y="{io3_pts[0][1]}" as="sourcePoint"/>')
    lines.append(f'            <mxPoint x="{io3_pts[-1][0]}" y="{io3_pts[-1][1]}" as="targetPoint"/>')
    lines.append('            <Array as="points">')
    for x, y in io3_pts[1:-1]:
        lines.append(f'              <mxPoint x="{x}" y="{y}"/>')
    lines.append('            </Array>')
    lines.append('          </mxGeometry>')
    lines.append('        </mxCell>')
    
    # ========== 阶段名称标注 ==========
    stage_labels = [
        (start_x + 4*cycle_w, "命令 (0xEB)", "#4CAF50"),          # 命令阶段中心
        (start_x + 9.5*cycle_w, "地址 (Quad)", "#666666"),       # 地址阶段中心
        (start_x + 12*cycle_w, "空周期 (2)", "#666666"),         # 空周期中心
        (start_x + 14*cycle_w, "数据 (Quad 0xAA)", "#2196F3"),   # 数据阶段中心
    ]
    for x, txt, color in stage_labels:
        lines.append(f'        <mxCell id="stage_{txt[:4]}" value="{txt}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=13;fontStyle=1;fontColor={color};" vertex="1" parent="1">')
        lines.append(f'            <mxGeometry x="{x-60}" y="235" width="120" height="20" as="geometry"/>')
        lines.append('        </mxCell>')
    
    # ========== 数据位标签 ==========
    # 1. 命令阶段IO0 bit标签
    for i in range(8):
        cx = rise_edges[i]
        lines.append(f'        <mxCell id="cmd_io0_{i}" value="{cmd_bits_io0[i]}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontColor=#4CAF50;" vertex="1" parent="1">')
        lines.append(f'                <mxGeometry x="{cx-10}" y="255" width="20" height="20" as="geometry"/>')
        lines.append('        </mxCell>')
    
    # 2. 地址阶段bit标签
    addr_io_labels = [
        (8, "A23", "A19", "A15", "#4CAF50"),  # IO0
        (8, "A22", "A18", "A14", "#9C27B0"),  # IO1
        (8, "A21", "A17", "A13", "#FFC107"),  # IO2
        (8, "A20", "A16", "A12", "#607D8B"),  # IO3
    ]
    for io_idx, label1, label2, label3, color in addr_io_labels:
        # 地址阶段第1个周期（总第9个周期）
        cx = rise_edges[io_idx]
        lines.append(f'        <mxCell id="addr_io{io_idx-8}_{0}" value="{label1}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontColor={color};" vertex="1" parent="1">')
        lines.append(f'                <mxGeometry x="{cx-15}" y="{255+100*(io_idx-8)}" width="30" height="20" as="geometry"/>')
        lines.append('        </mxCell>')
        # 地址阶段第2个周期（总第10个周期）
        cx = rise_edges[io_idx+1]
        lines.append(f'        <mxCell id="addr_io{io_idx-8}_{1}" value="{label2}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontColor={color};" vertex="1" parent="1">')
        lines.append(f'                <mxGeometry x="{cx-15}" y="{255+100*(io_idx-8)}" width="30" height="20" as="geometry"/>')
        lines.append('        </mxCell>')
        # 地址阶段第3个周期（总第11个周期）
        cx = rise_edges[io_idx+2]
        lines.append(f'        <mxCell id="addr_io{io_idx-8}_{2}" value="{label3}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontColor={color};" vertex="1" parent="1">')
        lines.append(f'                <mxGeometry x="{cx-15}" y="{255+100*(io_idx-8)}" width="30" height="20" as="geometry"/>')
        lines.append('        </mxCell>')
    
    # 3. 数据阶段bit标签
    data_io_labels = [
        (13, "D0", "D4", "#4CAF50"),  # IO0
        (13, "D1", "D5", "#9C27B0"),  # IO1
        (13, "D2", "D6", "#FFC107"),  # IO2
        (13, "D3", "D7", "#607D8B"),  # IO3
    ]
    for io_idx, label1, label2, color in data_io_labels:
        # 数据阶段第1个周期（总第14个周期）
        cx = rise_edges[io_idx]
        lines.append(f'        <mxCell id="data_io{io_idx-13}_{0}" value="{label1}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontColor={color};" vertex="1" parent="1">')
        lines.append(f'                <mxGeometry x="{cx-10}" y="{255+100*(io_idx-13)}" width="20" height="20" as="geometry"/>')
        lines.append('        </mxCell>')
        # 数据阶段第2个周期（总第15个周期）
        cx = rise_edges[io_idx+1]
        lines.append(f'        <mxCell id="data_io{io_idx-13}_{1}" value="{label2}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontColor={color};" vertex="1" parent="1">')
        lines.append(f'                <mxGeometry x="{cx-10}" y="{255+100*(io_idx-13)}" width="20" height="20" as="geometry"/>')
        lines.append('        </mxCell>')
    
    # 文件尾
    lines.append('      </root>')
    lines.append('    </mxGraphModel>')
    lines.append('  </diagram>')
    lines.append('</mxfile>')
    
    return "\n".join(lines)

# 生成并保存文件
xml_content = make_qspi_quad_io_mxfile()
output_path = "SPI_Quad.drawio"
with open(output_path, "w", encoding="utf-8") as f:
    f.write(xml_content)

print(f"波形图文件已生成: {output_path}")