#!/usr/bin/python3

"""
Conversión DXF -> SVG con corrección del capstyle de matplotlib.

El problema de los blobs (círculos/donuts grises, rojos, cyan) se debe a que
matplotlib usa por defecto 'round' caps en todas las líneas. Cuando el
stroke-width supera la longitud del segmento, los dos semicírculos de los
extremos se solapan formando un círculo. Con 'butt' caps los extremos se
cortan a escuadra y el problema desaparece.
"""

import sys
import ezdxf
import matplotlib.collections as mc
import matplotlib.patches as mp
import matplotlib.lines as ml
from ezdxf.addons.drawing import RenderContext, Frontend
from ezdxf.addons.drawing.matplotlib import MatplotlibBackend
from ezdxf.addons.drawing.config import (
    Configuration, LineweightPolicy, BackgroundPolicy
)
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

entrada = sys.argv[1] if len(sys.argv) > 1 else "plano.dxf"
salida  = sys.argv[2] if len(sys.argv) > 2 else "plano.svg"

doc = ezdxf.readfile(entrada)
msp = doc.modelspace()


def fix_dimpost_override(entity):
    if not entity.has_xdata("ACAD"):
        return False
    tags = list(entity.get_xdata("ACAD"))
    new_tags = []
    changed = False
    i = 0
    while i < len(tags):
        tag = tags[i]
        if (
            tag.code == 1070
            and tag.value == 3
            and i + 1 < len(tags)
            and tags[i + 1].code == 1000
        ):
            changed = True
            i += 2
            continue
        new_tags.append(tag)
        i += 1
    if changed:
        entity.set_xdata("ACAD", new_tags)
    return changed


dims = list(msp.query("DIMENSION"))
ok, fallidas = 0, []
for e in dims:
    try:
        e.render(); ok += 1
    except Exception as ex:
        if "dimpost" in str(ex).lower() and fix_dimpost_override(e):
            try:
                e.render(); ok += 1; continue
            except Exception as ex2:
                fallidas.append((e.dxf.handle, f"tras limpiar dimpost: {ex2}"))
                continue
        fallidas.append((e.dxf.handle, str(ex)))
print(f"Cotas renderizadas: {ok}/{len(dims)}")
for handle, msg in fallidas:
    print(f"  FALLO handle={handle}: {msg}")

# ── Render ────────────────────────────────────────────────────────────────────
fig = plt.figure(figsize=(20, 20))
ax  = fig.add_axes([0, 0, 1, 1])

ctx     = RenderContext(doc)
backend = MatplotlibBackend(ax)
config  = Configuration().with_changes(
    max_flattening_distance=0.00002,
    lineweight_scaling=0.3,
    min_lineweight=0.05,
    background_policy=BackgroundPolicy.WHITE,
    lineweight_policy=LineweightPolicy.RELATIVE_FIXED,
)
frontend = Frontend(ctx, backend, config=config)
frontend.draw_layout(msp, finalize=True)

# ── FIX: cambiar capstyle y joinstyle en todos los artists ───────────────────
n_fixed = 0
for artist in ax.get_children():

    # LineCollection (la mayoría de líneas de ezdxf van aquí)
    if isinstance(artist, mc.LineCollection):
        artist.set_capstyle("butt")
        artist.set_joinstyle("miter")
        n_fixed += 1

    # PathCollection (puntos, markers)
    elif isinstance(artist, mc.PathCollection):
        pass  # los markers no tienen capstyle

    # PatchCollection (áreas rellenas, hatches)
    elif isinstance(artist, mc.PatchCollection):
        artist.set_capstyle("butt")
        artist.set_joinstyle("miter")
        n_fixed += 1

    # Line2D individuales
    elif isinstance(artist, ml.Line2D):
        artist.set_solid_capstyle("butt")
        artist.set_solid_joinstyle("miter")
        n_fixed += 1

    # Patches individuales (Rectangle, Circle, FancyArrow…)
    elif isinstance(artist, mp.Patch):
        try:
            artist.set_capstyle("butt")
            artist.set_joinstyle("miter")
            n_fixed += 1
        except Exception:
            pass

print(f"Artists con capstyle corregido: {n_fixed}")

fig.savefig(salida, format="svg")
print(f"Guardado: {salida}")
