import os
import ezdxf
from ezdxf.math import Matrix44, Vec3, BoundingBox, Vec2
import math
import argparse
import sys
import shutil
import configparser
from utils import check_environment_var, setup_logger
from pathlib import Path
import logging

def create_block_library(input_dir, output_file, config, logger=None):
    """
    Erstellt eine DXF-Block-Bibliothek aus einzelnen DXF-Dateien.
    
    Diese Funktion liest alle DXF-Dateien aus einem Verzeichnis und erstellt
    daraus eine Bibliothek mit Blöcken. Die Blöcke werden in einem Raster
    angeordnet und mit Beschriftungen versehen. Fehlerhafte Dateien werden
    protokolliert.
    
    Args:
        input_dir (str): Verzeichnis mit den zu verarbeitenden DXF-Dateien
        output_file (str): Pfad zur zu erstellenden Bibliotheks-DXF-Datei
        config: ConfigParser-Objekt mit den Konfigurationswerten
        logger: Optionaler Logger für Logging-Ausgaben
        
    Note:
        - Unterstützte Entity-Typen: LINE, LWPOLYLINE, POLYLINE, SPLINE, CIRCLE, ARC, INSERT
        - Blöcke werden zentriert und in einem 20x20 Raster angeordnet
        - Erstellt eine Log-Datei mit Zeitstempel für fehlerhafte Dateien
        - Automatische Erstellung des Output-Verzeichnisses falls nötig
    """
    doc = ezdxf.new()
    msp = doc.modelspace()

    x_offset = 0
    y_offset = 0
    blocks_in_row = 0
    
    error_files = []
    processed_files = []
    max_blockspacing_x = 0
    max_blockspacing_y = 0
    
    for filename in os.listdir(input_dir):

        if not filename.lower().endswith(".dxf"):
            continue

        filepath = os.path.join(input_dir, filename)
        name = os.path.splitext(filename)[0]

        try:
            src_doc = ezdxf.readfile(filepath)
            src_msp = src_doc.modelspace()
            entities = list(src_msp)
            allowed_types = {"LINE", "LWPOLYLINE", "POLYLINE", "SPLINE", "CIRCLE", "ARC", "INSERT","ATTDEF"}
            filtered_entities = []
            
            for e in entities:
                if  e.dxftype() in allowed_types:
                    filtered_entities.append(e)
                else:
                    logger.info(f"{e.dxftype()} is nicht in der erlaubten Liste. Diese befindet sich in {filename}")
                    
          
            entities = filtered_entities
            
            
        except Exception as e:
            error_msg = f"Fehler beim Lesen von {filename}: {e}"
            if logger:
                logger.error(error_msg)
            else:
                print(error_msg)
            error_files.append((filename, error_msg))
            continue

        # Sicherstellen, dass die im Quell-DXF verwendeten Layer im Zieldokument existieren
        try:
            used_layer_names = {e.dxf.layer for e in entities if hasattr(e.dxf, "layer")}
            for layer_name in used_layer_names:
                if layer_name and layer_name not in doc.layers:
                    try:
                        src_layer = src_doc.layers.get(layer_name)
                        doc.layers.add(
                            name=layer_name,
                            color=getattr(src_layer.dxf, "color", None),
                            linetype=getattr(src_layer.dxf, "linetype", None),
                            lineweight=getattr(src_layer.dxf, "lineweight", None),
                        )
                    except Exception:
                        # Fallback: Layer mit Standardwerten anlegen
                        doc.layers.add(name=layer_name)
        except Exception:
            pass
       
        boundingbox, ausdehnung, center = calculate_block_bounding_box(filtered_entities, doc,src_doc, filename,config)
        if center is None:
            error_msg = f"Keine gültige Geometrie in {filename}"
            if logger:
                logger.error(error_msg)
            else:
                print(error_msg)
            error_files.append((filename, error_msg))
            continue

        if name in doc.blocks:
            doc.blocks.delete_block(name)

        blk = doc.blocks.new(name=name, base_point=(0,0))

        for e in entities:
            cp = copy_entity(logger, error_files, filename, e, center)
            if cp:
                blk.add_entity(cp)
                # add_bounding_box_to_modelspace(blk, boundingbox, centered=True)
            # Platzierung in Reihen und Spalten
            # Attribut-Definition (ATTDEF) hinzufügen

    
         # Blockreferenz-Layer bestimmen
        blockref_layer = None
        try:
             #Konfiguration erlaubt expliziten Layernamen
            cfg_layer = None
            try:
                cfg_layer = config.get("dxf2lib", "blockref_layer")
            except Exception:
                cfg_layer = None

            if cfg_layer and cfg_layer.strip():
                blockref_layer = cfg_layer.strip()
            else:
                # Dominanten Ursprungs-Layer verwenden (Mehrheit der Entities)
                layer_counts = {}
                for e in entities:
                    ln = getattr(e.dxf, "layer", None)
                    if not ln:
                        continue
                    layer_counts[ln] = layer_counts.get(ln, 0) + 1
                if layer_counts:
                    blockref_layer = max(layer_counts.items(), key=lambda kv: kv[1])[0]

            if blockref_layer and blockref_layer not in doc.layers:
                # Sicherstellen, dass der Layer existiert
                try:
                    src_layer = src_doc.layers.get(blockref_layer)
                    doc.layers.add(
                        name=blockref_layer,
                        color=getattr(src_layer.dxf, "color", None),
                        linetype=getattr(src_layer.dxf, "linetype", None),
                        lineweight=getattr(src_layer.dxf, "lineweight", None),
                    )
                except Exception:
                    doc.layers.add(name=blockref_layer)
        except Exception:
            blockref_layer = None
        section = "dxf2lib"
        text_height = get_cfg_value(section, "text_height", DEFAULTS["text_height"])
        extra_block_space_x = get_cfg_value(section, "extra_block_space_x", DEFAULTS["extra_block_space_x"])
        blocks_per_row = get_cfg_value(section, "blocks_per_row", DEFAULTS["blocks_per_row"])
        extra_text_space_y = get_cfg_value(section, "extra_text_space_y", DEFAULTS["extra_text_space_y"])

        ausdehnung_x, ausdehung_y = ausdehnung[0], ausdehnung[1]
        
        block_spacing_y = ausdehung_y + 400
        block_spacing_x = ausdehnung_x + extra_block_space_x
        max_blockspacing_x  = max(max_blockspacing_x, block_spacing_x)
        max_blockspacing_y  = max(max_blockspacing_y, block_spacing_y)
        x_offset += max_blockspacing_x 
        # Blockreferenz mit optionalem Layer einfügen (Entity-Layer bleiben erhalten)
        if blockref_layer:
            msp.add_blockref(name, insert=(x_offset, y_offset), dxfattribs={"layer": blockref_layer})
        else:
            msp.add_blockref(name, insert=(x_offset, y_offset))
        # Text mit Blocknamen über dem Block

        # Werte aus Config holen (Block: [dxf2lib])
      
        msp.add_text(name, dxfattribs={'height': text_height, 'insert': (x_offset -ausdehnung_x/2 , y_offset + ausdehung_y/2 + extra_text_space_y)})

        processed_files.append(filename)
        blocks_in_row += 1
         # Abstand zwischen Blöcken in einer Reihe
        if blocks_in_row == blocks_per_row:
            
            blocks_in_row = 0
            x_offset = 0
            y_offset -= max_blockspacing_y  # Neue Zeile, nach unten versetzt
            max_blockspacing_y = 0
            max_blockspacing_x = 0

    if logger:
        logger.info(f"Bibliotheks-DXF gespeichert: {output_file}")
        logger.info(f"Verarbeitete Dateien: {len(processed_files)}")
        logger.info(f"Fehlerhafte Dateien: {len(error_files)}")
        
        if error_files:
            logger.warning(f"{len(error_files)} Dateien konnten nicht verarbeitet werden:")
            for filename, error_msg in error_files:
                logger.error(f"{filename}: {error_msg}")
        else:
            logger.info("Keine fehlerhaften Dateien gefunden.")
    else:
        print(f"Bibliotheks-DXF gespeichert: {output_file}")
        print(f"Verarbeitete Dateien: {len(processed_files)}")
        print(f"Fehlerhafte Dateien: {len(error_files)}")
        
        if error_files:
            print(f"Warnung: {len(error_files)} Dateien konnten nicht verarbeitet werden.")

    output_dir = output_file.parent
    if not output_dir.exists():
        output_dir.mkdir(parents=True, exist_ok=True)

    doc.saveas(output_file)


def copy_entity(logger, error_files, filename, e, center):
    try:
        cp = e.copy()
        # Geometrie verschieben!
        cp.translate(-center.x, -center.y,0)
        # Ursprungs-Layer übernehmen
        if hasattr(e.dxf, "layer"):
            cp.dxf.layer = e.dxf.layer
        if (hasattr(e.dxf, "color")):
            cp.dxf.color = e.dxf.color
        return cp

    except Exception as err:
        error_msg = f"Fehler beim Verarbeiten von Entity {e.dxftype()} in {filename}: {err}"
        if logger:
            logger.error(error_msg)
        else:
            print(error_msg)
        error_files.append((filename, error_msg))
        return None
    


# Standardwerte (falls nicht in der Config)
DEFAULTS = {
    "text_height": 20,         # Schriftgröße des Texts (in DXF-Einheiten)
    "blocks_per_row": 20,      # Anzahl Blöcke pro Zeile im Raster
    "extra_block_space_x" : 50, # Extra Platz damit sich Blöcke nicht überlappen
    "extra_text_space_y" : 50  # Abstand der Überschrift über dem Symbol
    
}
def get_cfg_value(section, key, fallback):
    try:
        return int(config.get(section, key))
    except Exception:
        return fallback




def convert_dxf_to_block_with_inserts(input_filename, output_filename, block_name="CONVERTED_BLOCK"):
    """
    Konvertiert alle Entities einer DXF-Datei in einen neuen Block
    INSERTs werden als Referenzen beibehalten (nicht explodiert)
    """
    try:
        # Eingabe-DXF laden
        input_doc = ezdxf.readfile(input_filename)
        print(f"Lade DXF-Datei: {input_filename}")
        
        # Neue Ausgabe-DXF erstellen
        output_doc = ezdxf.new('R2010')
        output_doc.header['$INSUNITS'] = 4  # Millimeter
        
        # Zuerst alle Block-Definitionen kopieren
        copied_blocks = copy_block_definitions(input_doc, output_doc)
        print(f"Block-Definitionen kopiert: {len(copied_blocks)}")
        
        # Neuen Hauptblock erstellen
        new_block = output_doc.blocks.new(name=block_name)
        print(f"Erstelle neuen Block: {block_name}")
        
        # Alle Entities aus dem Modelspace kopieren
        msp = input_doc.modelspace()
        entity_count = 0
        insert_count = 0
        
        for entity in msp:
            if entity.dxftype() == 'INSERT':
                # INSERT direkt kopieren (nicht explodieren)
                copy_entity_to_block(entity, new_block)
                insert_count += 1
            else:
                # Normale Entity kopieren
                copy_entity_to_block(entity, new_block)
            
            entity_count += 1
        
        # Bounding Box berechnen
        bbox = calculate_block_bounding_box(new_block, output_doc)
        
        # Block im Modelspace der neuen Datei platzieren
        output_msp = output_doc.modelspace()
        output_msp.add_blockref(block_name, insert=(0, 0))
        
        # Bounding Box als Hilfslinien hinzufügen (optional)
        if bbox.has_data:
            add_bounding_box_to_modelspace(output_msp, bbox)
        
        # Speichern
        output_doc.saveas(output_filename)
        
        print(f"Konvertierung abgeschlossen:")
        print(f"  - {entity_count} Entities übertragen")
        print(f"  - {insert_count} INSERTs als Referenzen beibehalten")
        print(f"  - Bounding Box: {format_bounding_box(bbox)}")
        print(f"  - Ausgabe: {output_filename}")
        
        return bbox
        
    except FileNotFoundError:
        print(f"Fehler: Datei {input_filename} nicht gefunden")
        return None
    except ezdxf.DXFStructureError as e:
        print(f"DXF-Strukturfehler: {e}")
        return None
    except Exception as e:
        print(f"Unerwarteter Fehler: {e}")
        return None


def copy_block_definitions(source_doc, target_doc):
    """
    Kopiert alle Block-Definitionen vom Quell- zum Ziel-Dokument
    """
    copied_blocks = []
    
    for block_name in source_doc.blocks:
        # Standard-Blöcke (MODEL_SPACE, PAPER_SPACE) überspringen
        if block_name.startswith('*'):
            continue
            
        source_block = source_doc.blocks[block_name]
        
        # Prüfen ob Block bereits existiert
        if block_name in target_doc.blocks:
            print(f"Warnung: Block '{block_name}' existiert bereits, wird übersprungen")
            continue
        
        # Neuen Block in Ziel-Dokument erstellen
        target_block = target_doc.blocks.new(name=block_name)
        
        # Alle Entities des Quell-Blocks kopieren
        for entity in source_block:
            copy_entity_to_block(entity, target_block)
        
        copied_blocks.append(block_name)
    
    return copied_blocks


def calculate_block_bounding_box(block, doc, src_doc, filename,config):
    """
    Berechnet die Bounding Box eines Blocks inklusive aller INSERTs
    """
    
    bbox = BoundingBox()
    
    for entity in block:
        entity_bbox = get_entity_bounding_box(entity, doc,src_doc,filename,config)
        if entity_bbox and entity_bbox.has_data:
            bbox.extend(entity_bbox)
    
    
    return bbox, (bbox.extmax.x -bbox.extmin.x, bbox.extmax.y -bbox.extmin.y),bbox.center


def get_entity_bounding_box(e, doc,src_doc,filename,config, transform_matrix=None):
    """
    Berechnet die Bounding Box einer einzelnen Entity
    Berücksichtigt INSERTs mit ihren Block-Inhalten
    """

    bbox = BoundingBox()      
    try:
        if e.dxftype() == 'LINE':
            start = Vec3(e.dxf.start)
            end = Vec3(e.dxf.end)
            if transform_matrix:
                start = transform_matrix.transform(start)
                end = transform_matrix.transform(end)
            bbox.extend([start])
            bbox.extend([end])
            
        elif e.dxftype() == 'CIRCLE':
            # Kreis durch Punktabtastung (robust bei Transformationen)
            center = Vec3(e.dxf.center)
            radius = float(e.dxf.radius)
            angles = (0.0, math.pi / 2, math.pi, 3 * math.pi / 2)
            points = []
            for ang in angles:
                px = center.x + radius * math.cos(ang)
                py = center.y + radius * math.sin(ang)
                p = Vec3(px, py, center.z)
                if transform_matrix:
                    p = transform_matrix.transform(p)
                points.append(p)
            if points:
                bbox.extend(points)
            
        elif e.dxftype() == 'ARC':
            # Bogen durch Punktabtastung zwischen Start- und Endwinkel
            center = Vec3(e.dxf.center)
            radius = float(e.dxf.radius)
            start_deg = float(e.dxf.start_angle)
            end_deg = float(e.dxf.end_angle)
            start = math.radians(start_deg % 360)
            end = math.radians(end_deg % 360)

            def in_sweep(a: float, s: float, e_: float) -> bool:
                # CCW sweep von s nach e_ mit Wrap-Handling
                if e_ >= s:
                    return s <= a <= e_
                return a >= s or a <= e_

            angles = [start, end]
            for ang in (0.0, math.pi / 2, math.pi, 3 * math.pi / 2):
                if in_sweep(ang, start, end):
                    angles.append(ang)

            points = []
            for ang in angles:
                px = center.x + radius * math.cos(ang)
                py = center.y + radius * math.sin(ang)
                p = Vec3(px, py, center.z)
                if transform_matrix:
                    p = transform_matrix.transform(p)
                points.append(p)
            if points:
                bbox.extend(points)
            
        elif e.dxftype() == 'LWPOLYLINE':
            # Nutze virtuelle Entities (Linien/Bögen), inkl. Bulge-Unterstützung
            for ve in e.virtual_entities():
                ve_bbox = get_entity_bounding_box(ve, doc, src_doc, filename, transform_matrix)
                if ve_bbox and ve_bbox.has_data:
                    bbox.extend(ve_bbox)
                
        elif e.dxftype() == 'POLYLINE':
            # 2D/3D Polylines ebenfalls über virtuelle Entities
            for ve in e.virtual_entities():
                ve_bbox = get_entity_bounding_box(ve, doc, src_doc, filename, transform_matrix)
                if ve_bbox and ve_bbox.has_data:
                    bbox.extend(ve_bbox)
                
        elif e.dxftype() == 'SPLINE':
            # Approximation der Spline-Kurve
            try:
                pts = list(e.approximate(60))
            except Exception:
                try:
                    pts = list(e.flattening(1.0))
                except Exception:
                    pts = []
            sampled = []
            for pt in pts:
                try:
                    vx, vy = pt.x, pt.y
                except Exception:
                    vx, vy = pt[0], pt[1]
                v = Vec3(vx, vy, 0)
                if transform_matrix:
                    v = transform_matrix.transform(v)
                sampled.append(v)
            if sampled:
                bbox.extend(sampled)
                
        elif e.dxftype() == 'ATTDEF':
            # Vereinfachung: Nur Insert-Point berücksichtigen
            insert_point = Vec3(e.dxf.insert)
            if transform_matrix:
                insert_point = transform_matrix.transform(insert_point)
          
            
        elif e.dxftype() == 'INSERT':
            # INSERT: Block-Inhalt mit Transformation berücksichtigen
            insert_bbox = calculate_insert_bounding_box(e, doc,src_doc,filename,config, transform_matrix)
            if insert_bbox and insert_bbox.has_data and e.dxf.layer not in config.get("dxf2lib","automation_layer"):
                bbox.extend(insert_bbox)
                
    except Exception as e:
        print(f"Fehler bei Bounding Box Berechnung für {e.dxftype()}: {e}")
   
 
    return bbox

def extract_scaling(matrix):
    sx = math.sqrt(matrix[0, 0]**2 + matrix[0, 1]**2 + matrix[0, 2]**2)
    sy = math.sqrt(matrix[1, 0]**2 + matrix[1, 1]**2 + matrix[1, 2]**2)
    return sx, sy, 

def calculate_insert_bounding_box(insert_entity, doc,src_doc,filename,config,parent_transform=None):
    """
    Berechnet die Bounding Box eines INSERTs inklusive Block-Inhalt
    """
    
    try:
         # Block-Definition finden
        block_name = insert_entity.dxf.name
        src_blk = src_doc.blocks[block_name]
  
        if block_name in doc.blocks:
            dst_blk = doc.blocks[block_name]

            new_insert = False
            
        else:
            dst_blk = doc.blocks.new(name=block_name)
            new_insert = True
        if block_name not in src_doc.blocks:
            print(f"Warnung: Block '{block_name}' ")
            return BoundingBox()
    
        block_def = src_blk

        
        # Transformation der INSERT-Entity berechnen
        insert_transform = get_insert_transform_matrix(insert_entity)
        
        # Mit übergeordneter Transformation kombinieren
        if parent_transform:
            combined_transform = parent_transform * insert_transform
        else:
            combined_transform = insert_transform
        
        # Bounding Box aller Entities im Block berechnen
        block_bbox = BoundingBox()
        
        for block_entity in block_def:
            
                entity_bbox= get_entity_bounding_box(block_entity, doc,src_doc,filename, config,combined_transform)
                if entity_bbox and entity_bbox.has_data:
                    if new_insert:
                        dst_blk.add_entity(block_entity.copy())
                    block_bbox.extend(entity_bbox)

        return block_bbox
        
    except Exception as e:
        print(f"Fehler bei INSERT Bounding Box: {e},{filename}")
        return BoundingBox()


def get_insert_transform_matrix(insert_entity):
    """
    Berechnet die Transformationsmatrix für einen INSERT
    """
    # Position
    insert_point = Vec3(insert_entity.dxf.insert)
    
    # Skalierung
    xscale = getattr(insert_entity.dxf, 'xscale', 1.0)
    yscale = getattr(insert_entity.dxf, 'yscale', 1.0)
    zscale = getattr(insert_entity.dxf, 'zscale', 1.0)
    
    # Rotation (in Radiant umwandeln)
    rotation = math.radians(getattr(insert_entity.dxf, 'rotation', 0.0))
    
    # Transformationsmatrix erstellen
    matrix = Matrix44.chain(
        Matrix44.scale(xscale, yscale, zscale),
        Matrix44.z_rotate(rotation),
        Matrix44.translate(insert_point.x, insert_point.y, insert_point.z)
    )
    
    return matrix


def copy_entity_to_block(entity, target_block):
    """
    Kopiert eine Entity in einen Zielblock
    """
    try:
        # Entity kopieren und zum Block hinzufügen
        entity_copy = entity.copy()
        target_block.add_entity(entity_copy)
        
    except Exception as e:
        print(f"Fehler beim Kopieren von {entity.dxftype()}: {e}")


def add_bounding_box_to_modelspace(msp, bbox, centered=False):
    """
    Fügt die Bounding Box als Hilfslinien zum Modelspace hinzu.
    Wenn centered=True, wird das Rechteck um den Ursprung (0,0) zentriert und
    ein roter Punkt in die Mitte gesetzt.
    """
    if not bbox.has_data:
        return
    
    # Bounding Box Rechteck zeichnen
    min_pt = bbox.extmin
    max_pt = bbox.extmax
    width = max_pt.x - min_pt.x
    height = max_pt.y - min_pt.y
    
    if centered:
        # Rechteck um (0,0) zentriert
        left = -width / 2.0
        right = width / 2.0
        bottom = -height / 2.0
        top = height / 2.0
        bbox_points = [
            (left, bottom),
            (right, bottom),
            (right, top),
            (left, top),
            (left, bottom)
        ]
        # Roter Punkt in der Mitte
        msp.add_circle(center=(0.0, 0.0), radius=max(0.5, min(width, height) * 0.01),
                       dxfattribs={"layer": "BOUNDING_BOX", "color": 1})
    else:
        # Ursprüngliche, nicht-zentrierte Bounding Box
        bbox_points = [
            (min_pt.x, min_pt.y),
            (max_pt.x, min_pt.y),
            (max_pt.x, max_pt.y),
            (min_pt.x, max_pt.y),
            (min_pt.x, min_pt.y)
        ]
    
    bbox_poly = msp.add_lwpolyline(bbox_points)
    bbox_poly.dxf.layer = "BOUNDING_BOX"
    bbox_poly.dxf.color = 1  # Rot
    
    # Text mit Abmessungen
    text_pos = Vec3(bbox_points[0][0], (bbox_points[2][1] if centered else max_pt.y) + 5, 0)
    msp.add_text(f"Breite: {width:.2f} mm", height=3,
                 dxfattribs={'insert': text_pos, 'layer': "BOUNDING_BOX", 'color': 1})
    
    text_pos2 = Vec3(bbox_points[0][0], (bbox_points[2][1] if centered else max_pt.y) + 10, 0)
    msp.add_text(f"Höhe: {height:.2f} mm", height=3,
                 dxfattribs={'insert': text_pos2, 'layer': "BOUNDING_BOX", 'color': 1})


def format_bounding_box(bbox):
    """
    Formatiert Bounding Box Information für Ausgabe
    """
    if not bbox.has_data:
        return "Keine gültigen Geometriedaten gefunden"
    
    min_pt = bbox.extmin
    max_pt = bbox.extmax
    width = max_pt.x - min_pt.x
    height = max_pt.y - min_pt.y
    depth = max_pt.z - min_pt.z
    
    return (f"Min: ({min_pt.x:.2f}, {min_pt.y:.2f}, {min_pt.z:.2f}) "
            f"Max: ({max_pt.x:.2f}, {max_pt.y:.2f}, {max_pt.z:.2f}) "
            f"Größe: {width:.2f} × {height:.2f} × {depth:.2f} mm")


def analyze_source_dxf_with_blocks(filename):
    """
    Analysiert die Quell-DXF inklusive Block-Definitionen
    """
    try:
        doc = ezdxf.readfile(filename)
        msp = doc.modelspace()
        
        entity_types = {}
        layer_count = {}
        insert_blocks = {}
        block_definitions = {}
        
        # Modelspace analysieren
        for entity in msp:
            entity_type = entity.dxftype()
            entity_types[entity_type] = entity_types.get(entity_type, 0) + 1
            
            layer = getattr(entity.dxf, 'layer', '0')
            layer_count[layer] = layer_count.get(layer, 0) + 1
            
            if entity_type == 'INSERT':
                block_name = entity.dxf.name
                insert_blocks[block_name] = insert_blocks.get(block_name, 0) + 1
        
        # Block-Definitionen analysieren
        for block_name in doc.blocks:
            if not block_name.startswith('*'):  # Keine Standard-Blöcke
                block_def = doc.blocks[block_name]
                entity_count = len(list(block_def))
                block_definitions[block_name] = entity_count
        
        print(f"\nAnalyse von {filename}:")
        print("=" * 50)
        print("Entity-Typen im Modelspace:")
        for etype, count in sorted(entity_types.items()):
            print(f"  {etype}: {count}")
        
        print(f"\nLayer ({len(layer_count)}):")
        for layer, count in sorted(layer_count.items()):
            print(f"  {layer}: {count} entities")
            
        if insert_blocks:
            print(f"\nINSERT-Verwendungen ({sum(insert_blocks.values())} total):")
            for block, count in sorted(insert_blocks.items()):
                print(f"  {block}: {count}× verwendet")
        
        if block_definitions:
            print(f"\nBlock-Definitionen ({len(block_definitions)}):")
            for block, count in sorted(block_definitions.items()):
                print(f"  {block}: {count} entities")
        
        return entity_types, layer_count, insert_blocks, block_definitions
        
    except Exception as e:
        print(f"Fehler bei der Analyse: {e}")
        return {}, {}, {}, {}

if __name__ == "__main__":
    # Argumentparser für Kommandozeilenoptionen
    parser = argparse.ArgumentParser(description="SVG/XML zu DXF Konverter")
    parser.add_argument('-i', '--input', type=str, help='Input-Verzeichnis mit SVG/XML-Dateien')
    parser.add_argument('-n', '--name', required=False, type=str, help='Name der zu erzeugenden Bibliothek (optional, wird sonst abgefragt)', default="test")
    
    if len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"):
        parser.print_help()
        sys.exit(0)
        
    args = parser.parse_args()

    if not args.name:
        args.name = input("Bitte Namen der zu erzeugenden Bibliothek eingeben: ").strip()
        if not args.name:
            print("Fehler: Kein Name angegeben. Beende.")
            sys.exit(1)

     # Verzeichnisse über Umgebungsvariablen oder Fallback
    if args.input:
        INPUT_DIR = Path(args.input)
        print(f"Verwende Input-Verzeichnis: {INPUT_DIR} \n")
    else:
        INPUT_DIR = check_environment_var("PROJECT_DATA") / "omniflo"
        print(f"Kein Input-Verzeichnis angegeben, verwende Standard: {INPUT_DIR} \n")

    OUTPUT_FILE = check_environment_var("PROJECT_DATA") / "block_libraries" / f"{args.name}.dxf"

    # Prüfe und erstelle log-Verzeichnis falls nötig
    log_dir = check_environment_var("PROJECT_LOG")
    if not log_dir.exists():
        log_dir.mkdir(parents=True, exist_ok=True)
        print(f"Log-Verzeichnis erstellt: {log_dir}")

    # Logger Setup
    log_file = Path(os.environ['PROJECT_LOG']) / 'dxf2lib.log'
    file_handler = logging.FileHandler(str(log_file), 'a', 'utf-8')
    logger = setup_logger(log_dir, name='dxf2lib')
    logger.info("=== DXF2LIB Verarbeitung gestartet ===")
    logger.info(f"Input-Verzeichnis: {INPUT_DIR}")
    logger.info(f"Output-Datei: {OUTPUT_FILE}")
    logger.addHandler(file_handler)

    # Lade Config-Datei
    config = configparser.ConfigParser()
    config_path = check_environment_var("PROJECT_CFG") / "allgemein.cfg"
    config.read(config_path, encoding="utf-8")
    logger.info(f"Config-Datei geladen: {config_path}")

    # Erstelle die Block-Bibliothek
    logger.info(f"Erstelle Block-Bibliothek aus {INPUT_DIR}...")
    create_block_library(INPUT_DIR, OUTPUT_FILE, config, logger)
    logger.info("=== DXF2LIB Verarbeitung abgeschlossen ===")
