"""
placeblocks.py
Erzeugt DXF-Elemente aus einer RuleDesigner-CSV.
"""

import os
import sys
import csv
import json
import re
import argparse
import configparser
import ezdxf
from ezdxf import units
from ezdxf.entities import Line
from ezdxf.addons import importer
from ezdxf.math import Matrix44, X_AXIS,Y_AXIS,Z_AXIS
from ezdxf import bbox
from pathlib import Path
import math
from pydantic import BaseModel, Field, field_validator
from typing import Optional
from utils import check_environment_var, setup_logger
from Elemente import Kreisel, VarioFoerderer,Gefaehllestrecke,Angetriebene_Kurve,Bt_element,Omniflo
import as_es_methoden



# --------------------------------------------------------- CFG-Leser für shapes.cfg
def get_shape_cfg(teileart, cfg_path, logger=None):
    parser = configparser.ConfigParser()
    try:
        with open(cfg_path, encoding='utf-8') as f:
            parser.read_file(f)
    except Exception as e:
        msg = f"Fehler beim Lesen der Config-Datei {cfg_path}: {e}"
        if logger:
            logger.error(msg)
        else:
            print(msg)
        return []
    section = teileart
    if section not in parser:
        return []
    # Blöcke
    items = parser.get(section, "items", fallback="").replace('"', '').split(",")
    blocks = [item.strip() for item in items if item.strip()]
    symbols = []
    for i, name in enumerate(blocks):
        # Offset
        offset_key = f"offset_symb{i+1}"
        offset_str = parser.get(section, offset_key, fallback="0,0")
        try:
            ox, oy = [float(x) for x in offset_str.split(",")]
        except Exception:
            ox, oy = 0.0, 0.0
        # Rotation
        rot_key = f"rot_symb{i+1}"
        rot_str = parser.get(section, rot_key, fallback="0.0")
        try:
            rot = float(rot_str)
        except Exception:
            rot = 0.0
        symbols.append({
            "name": name,
            "offset": (ox, oy),
            "rotation": rot
        })
    return symbols
# --------------------------------------------------------- Konstante Parameter
ATTR_TAG = "TeileId"     # Attributtag im Block
RADIUS   = 400           # Radius der Kreiselkreise (mm)

# --------------------------------------------------------- Hilfsfunktionen
def extract_coords(planquadrat: str) -> tuple[float, float]:
    """Extrahiert X/Y Koordinaten aus PlanquadratString."""
    m = re.search(r"X:(\d+[\.,]?\d*)\s+Y:(\d+[\.,]?\d*)", planquadrat)
    if not m:
        raise ValueError(f"Koordinaten nicht gefunden in: '{planquadrat}'")
    x, y = m.groups()
    return float(x.replace(",", ".")), float(y.replace(",", "."))

def parse_merkmale(merkmale_str: str) -> dict:
    """Parst Merkmale-JSON-String in dict; bei Fehler → leeres Dict."""
    try:
        return json.loads(merkmale_str)
    except json.JSONDecodeError:
        return {}

def import_block(block_name: str, from_doc, to_doc, winkel = None) -> None:
    """Importiert Blockdefinition block_name von from_doc nach to_doc.

    - Kopiert alle Entities des Blocks
    - Stellt sicher, dass benutzte Layer im Ziel existieren (mit Eigenschaften)
    - Übernimmt Basis­punkt und Block-Layer, falls vorhanden
    """
    msp2 = from_doc.modelspace()
    src = from_doc.blocks[block_name]
    att_def = {}
    if block_name == "Pinbereich":
        imp = importer.Importer(from_doc, to_doc)

        # Alle Linientypen importieren
        imp.import_table("linetypes")
    if ((block_name in to_doc.blocks)):
        # speichern der attdef elemente in eine Liste falks diese verhanden sind und gibt diese zurück
        for ent in src:
            copy = ent.copy()

            if ent.dxftype() == "ATTDEF":
                att_def[ent.dxf.tag] =ent.dxf.text
            if ent.dxftype() == "INSERT":

                import_block(ent.dxf.name,from_doc, to_doc,None)

        if att_def != {}:
            return att_def
        return

    if block_name not in from_doc.blocks:
        raise ValueError(f"Block '{block_name}' nicht in Bibliothek gefunden.")
    # Sicherstellen, dass alle verwendeten Layer existieren
    try:
        used_layer_names = {e.dxf.layer for e in src if hasattr(e.dxf, "layer")}
        for layer_name in used_layer_names:
            if layer_name and layer_name not in to_doc.layers:
                try:
                    src_layer = from_doc.layers.get(layer_name)
                    to_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
                    to_doc.layers.add(name=layer_name)
    except Exception:
        pass
    tgt = to_doc.blocks.new(name=block_name)
    # Basis­punkt/Layer des Blocks übernehmen, wenn vorhanden
    try:
        tgt.block.dxf.base_point = src.block.dxf.base_point
    except Exception:
        pass
    try:
        tgt.block.dxf.layer = src.block.dxf.layer
    except Exception:
        pass
    # kopiert die elemente von dem element aus dem block und speichert diese in den block für dass Modelspace auf und # speichern der attdef elemente in eine Liste falks diese verhanden sind und gibt diese zurück
    
    for ent in src:
        copy = ent.copy()

        if ent.dxftype() == "ATTDEF":
            att_def[ent.dxf.tag] =ent.dxf.text
        if ent.dxftype() == "INSERT":
            import_block(ent.dxf.name,from_doc, to_doc,None)
        tgt.add_entity(copy)
    
    if att_def != {}:
        return att_def
 
def dreh_block(block_name: str, to_doc,lib_doc, winkel) :
    """Nimmt ein schon importierten Block und erstellt einen neuen der an der y_axis oder x_axis gedreht wird
    """
    dreh_block_name = block_name +f"_{math.degrees(winkel)}"
    layer, color = get_insert_color_layer(lib_doc,block_name)
    if (dreh_block_name in to_doc.blocks):
       return dreh_block_name
    block_zwischen = to_doc.blocks.new(name=block_name + f"zwischenschrit_{winkel}", base_point=(0,0,0))
    block = to_doc.blocks.new(name=dreh_block_name, base_point=(0,0,0))
    if block_name == "200000146_ES-Element_90_rechts" or block_name =="400102632_ES-Element_90_links" or block_name =="200000241_AS-Element_90_rechts" or block_name =="200000217_AS-Element_90_links":
        rotation_matrix = Matrix44.axis_rotate(X_AXIS, winkel)
    else:    
        rotation_matrix = Matrix44.axis_rotate(Y_AXIS, winkel)

    block_zwischen.add_blockref(block_name,(0,0,0),dxfattribs={"layer":layer,"color": color})
    for e in block_zwischen:
        copy = e.copy()
        copy.transform(rotation_matrix)
        block.add_entity(copy)
    return dreh_block_name

def handle_ils_2_0_kreisel(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols,strecken_nachbarn,config,config_allgemein):
    """Erstellt ein Kreisel in der neuen Dxf"""
    kreisel = Kreisel.Kreisel.from_merkmale(teileid, x, y, merkmale)
    block_scanner = "SCAN"
    block_separatoren = "S-LP"
    scanner_x = kreisel.x
    scanner_y = kreisel.y
    separatoren_x = kreisel.x
    separatoren_y = kreisel.y + 160
    import_block(block_scanner,lib_doc,doc)
    import_block(block_separatoren,lib_doc,doc)
    i = 0
    while i < kreisel.anzahl_scanner:
        msp.add_blockref(block_scanner, (scanner_x,scanner_y, kreisel.hoehe))
        scanner_x = scanner_x + 300
        i = i+1
    
    i = 0
    while i < kreisel.anzahl_separatoren:
        msp.add_blockref(block_separatoren, (separatoren_x,separatoren_y, kreisel.hoehe))
        separatoren_x = separatoren_x + 300
        i = i +1
    
    # Die Koordinaten (x, y) sind die Mitte zwischen den beiden Blöcken (bereits transformiert)
    pos1 = kreisel.pos1
    pos2 = kreisel.pos2
    positions = [pos1, pos2]
    for i, sym in enumerate(symbols):
        blockname = sym["name"]
        offset = sym["offset"]
        rotation = sym["rotation"]
        if i < len(positions):
            pos = (positions[i][0] + offset[0], positions[i][1] + offset[1], kreisel.hoehe)
            import_block(blockname, lib_doc, doc)
            blockref_layer, color = get_insert_color_layer(lib_doc, blockname)
            bref = msp.add_blockref(blockname, pos, dxfattribs={"layer" : blockref_layer})
            bref.add_auto_attribs({ATTR_TAG: teileid})
            if verbose:
                print(f"[INFO] Block '{blockname}' (Kreisel) → {teileid} "
                      f"({pos[0]:.1f}, {pos[1]:.1f}), rot={rotation}")
    # Linien zeichnen
    import_block("Pinbereich",lib_doc,doc)
    Kreisel.Kreisel.draw_kreisel_lines(msp, pos1, pos2, kreisel)
    Kreisel.Kreisel.draw_kreisel_drehrichtung_markierung(msp, pos1, pos2, kreisel, lib_doc, doc, verbose)

def handle_ils_2_0_eckrad(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, strecken_nachbarn,config,config_allgemein):
    """Erstellt ein Eckrad in der neuen Dxf"""
    import_block("AN8",lib_doc,doc)
    import_block("Richtungspfeil",lib_doc,doc)
    eckrad_rechts = "eckrad_UZS"
    eckrad_links = "eckrad_GUZS"
    hight = float(merkmale.get("Höhe in m"))
    if  eckrad_rechts not in doc.blocks:
        block_rechts = doc.blocks.new(name= eckrad_rechts,base_point=(0,0,0))
        block_links = doc.blocks.new(name= eckrad_links,base_point=(0,0,0))
        block_rechts.add_blockref("AN8",(0,0,0))
        block_links.add_blockref("AN8",(0,0,0))
        block_rechts.add_blockref("Richtungspfeil",(0+200,0+ RADIUS,0))
        block_rechts.add_blockref("Richtungspfeil",(0-200,0- RADIUS,0),dxfattribs={"rotation": 180})
        block_links.add_blockref("Richtungspfeil",(0+200,0- RADIUS,0))
        block_links.add_blockref("Richtungspfeil",(0-200,0+ RADIUS,0),dxfattribs={"rotation": 180})

    if merkmale.get("Drehrichtung") == "UZS":
        msp.add_blockref(eckrad_rechts,(x,y,hight))
    elif merkmale.get("Drehrichtung") == "GUZS":
        msp.add_blockref(eckrad_links,(x,y,hight))
    else:
        msp.add("AN8",x,y,hight)

def handle_ils_2_0_gefaellestrecke(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, strecken_nachbarn,config,config_allgemein):
    """Erstellt eine Gefällestrecke in der neuen Dxf"""
    #Vorbereitung der attributen
    gefaelle_objekt = Gefaehllestrecke.Gefaellestrecke.from_merkmale(teileid,x,y,merkmale)
    asoffset = float(config.get("ILS 2.0 Gefällestrecke", "asoffset"))
    esoffset = float(config.get("ILS 2.0 Gefällestrecke", "esoffset"))
    upper_hoehe_gefaehlle= gefaelle_objekt.h1
    lower_hoehe_gefaehlle = gefaelle_objekt.h0
    hoehe_gefaehlle = gefaelle_objekt.hight_zwischen
    laenge = gefaelle_objekt.laenge

    halbe_laenge = laenge / 2
    rotation= gefaelle_objekt.drehung
    winkel = math.radians(float(rotation))
    dx = halbe_laenge *math.sin(winkel * -1)
    dy = halbe_laenge * math.cos(winkel)
    start = x +dx, y + dy,upper_hoehe_gefaehlle
    if "6-SP" not in doc.layers:
            doc.layers.add(name="6-SP", color=7)
    gefaelle_layer = "6-SP"
    verbunden_am_einen = False

    richtung2 ="DEFAULT"
    unterschiedlich = False

    anzahl_seperatoren_oder_scan(msp, x, y, doc, lib_doc, gefaelle_objekt, hoehe_gefaehlle, rotation)
    for nachbarn in strecken_nachbarn:
        if teileid == nachbarn.get("Id"):
            gefaellestrecke_nachbarn = nachbarn
            break
    if "Drehung0" in gefaellestrecke_nachbarn and "Drehung1" not in gefaellestrecke_nachbarn:
        
        drehung0 =gefaellestrecke_nachbarn.get("Drehung0")
        x0_kreisel = float(gefaellestrecke_nachbarn.get("x0"))
        y0_kreisel = float(gefaellestrecke_nachbarn.get("y0"))

        hoehe0 = float(gefaellestrecke_nachbarn.get("Hoehe0"))
        richtung0 = float(gefaellestrecke_nachbarn.get("rotation0"))
        richtung_rad0= math.radians(richtung0)
        abstand0 = float(gefaellestrecke_nachbarn.get("abstand0")) * 1000

        if upper_hoehe_gefaehlle < lower_hoehe_gefaehlle:
            hoehe2 = upper_hoehe_gefaehlle
            upper_hoehe_gefaehlle = lower_hoehe_gefaehlle
            lower_hoehe_gefaehlle = hoehe2
            rotation = rotation -180

        hat_motor_0, hat_umlenk_0, hat_motor_1, hat_umlenk_1, tefkurve_0, tefkurve_1 = Gefaehllestrecke.Gefaellestrecke.hat_motor_umlenk_station (doc, lib_doc, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, gefaellestrecke_nachbarn)

        verbunden_am_einen = True
        am_kreisel, kreisel_verbunden =as_es_methoden.am_kreisel_direct_verbunden(x, y, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, x0_kreisel, y0_kreisel, richtung_rad0, abstand0, dx, dy, None, None, None, None)
        if am_kreisel == 1:
            if hat_motor_0 == False and hat_umlenk_0 == False:
                dx = halbe_laenge *math.sin(winkel * -1)
                dy = halbe_laenge * math.cos(winkel)
                start = x +dx, y + dy,upper_hoehe_gefaehlle
                ende  = x -dx, y - dy,lower_hoehe_gefaehlle
                line =msp.add_line(start,ende)
                line.dxf.layer = "6-SP"

                return
            else:   
                
                dy = halbe_laenge * math.cos(0)
                start = [x , y + dy ,upper_hoehe_gefaehlle]
                ende  = [x , y - dy ,lower_hoehe_gefaehlle]
                blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{hoehe_gefaehlle}_{hat_umlenk_0}_{hat_motor_0}_{tefkurve_0}"
                if blockname not in doc.blocks:
                    block = doc.blocks.new(name=blockname,base_point= (0,0,0))
                    if hat_motor_0 == True:
                        if tefkurve_0 == "rechts":
                            block.add_blockref(block_Vario_Motorstation_500mm, (start[0]-x,start[1] - 250* math.cos(math.radians(3))-y,start[2] - 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
                        else:
                            block.add_blockref(blockname_motor_links, (start[0]-x,start[1] - 250* math.cos(math.radians(3))-y,start[2] - 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})

                        start [1]= start[1] - 500* math.cos(math.radians(3))
                        start[2] = start[2] - 500* math.sin(math.radians(3))
                    if hat_umlenk_0 == True:
                        if tefkurve_0 == "rechts":
                            block.add_blockref(block_Vario_Umlenkstation_500mm, (ende[0]-x,ende[1] + 250* math.cos(math.radians(3))-y,ende[2] + 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
                        else:
                            block.add_blockref(blockname_umlenk_links, (ende[0]-x,ende[1] + 250* math.cos(math.radians(3))-y,ende[2] + 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})

                        ende [1]= ende[1] + 500* math.cos(math.radians(3))
                        ende[2] = ende[2] + 500* math.sin(math.radians(3))
                    line = Line.new(dxfattribs={"start":start,"end":ende})
                    line.translate(-x,-y,-hoehe_gefaehlle)
                    block.add_entity(line)
                msp.add_blockref(blockname,(x,y,hoehe_gefaehlle),dxfattribs={"rotation": rotation,"layer": gefaelle_layer})
                return
        if  upper_hoehe_gefaehlle == hoehe0:
            hight ="higher"
        else:
            hight = "lower"
        if hat_motor_0 == False and hat_umlenk_0 == False:
            blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{drehung0}_{hoehe_gefaehlle}_{verbunden_am_einen}_{hight}"
        else: 
            blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{drehung0}_{hoehe_gefaehlle}_{verbunden_am_einen}_{hight}_{hat_umlenk_0}_{hat_motor_0}_{tefkurve_0}"
        
        if blockname in doc.blocks:
            bref =msp.add_blockref(blockname,(x,y,hoehe_gefaehlle),dxfattribs={"rotation": rotation,"layer": gefaelle_layer})
            a =bref.add_attrib(
                tag= "NAME",
                text= merkmale.get("bezeichner"),
                insert = (x,y)
            )
            a.is_invisible = True
            return
        if laenge > asoffset or laenge > esoffset:
            block = doc.blocks.new(name=blockname,base_point= (0,0,0))
        else:
            block = None
     
        dy = halbe_laenge * math.cos(0)
        start = [x , y + dy ,upper_hoehe_gefaehlle]
        ende  = [x , y - dy ,lower_hoehe_gefaehlle]
        if hat_motor_0 == True:
            if tefkurve_0 == "rechts":
                block.add_blockref(block_Vario_Motorstation_500mm, (start[0]-x,start[1] - 250* math.cos(math.radians(3))-y,start[2] - 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
            else:
                block.add_blockref(blockname_motor_links, (start[0]-x,start[1] - 250* math.cos(math.radians(3))-y,start[2] - 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})

            start [1]= start[1] - 500* math.cos(math.radians(3))
            start[2] = start[2] - 500* math.sin(math.radians(3))
        if hat_umlenk_0 == True:
            if tefkurve_0 == "rechts":
                block.add_blockref(block_Vario_Umlenkstation_500mm, (ende[0]-x,ende[1] - 250* math.cos(math.radians(3))-y,ende[2] - 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
            else:
                block.add_blockref(blockname_umlenk_links, (ende[0]-x,ende[1] + 250* math.cos(math.radians(3))-y,ende[2] + 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
            ende [1]= ende[1] + 500* math.cos(math.radians(3))
            ende[2] = ende[2] + 500* math.sin(math.radians(3))


        only_es_or_as = as_es_methoden.erstellung_gefaelle_block_verbunenden_am_einen(msp,x, y, doc, lib_doc, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, hoehe_gefaehlle, drehung0, laenge, blockname,config,hight,None,None,None,block,start,ende)
        
        if only_es_or_as == False:
            bref =msp.add_blockref(blockname,(x,y,hoehe_gefaehlle),dxfattribs={"rotation": rotation,"layer": gefaelle_layer})
            a =bref.add_attrib(
                tag= "NAME",
                text= merkmale.get("bezeichner"),
                insert = (x,y)
            )
            a.is_invisible = True

    elif "Drehung0" in gefaellestrecke_nachbarn and "Drehung1" in gefaellestrecke_nachbarn:
        dx = halbe_laenge *math.sin(winkel * -1)
        dy = halbe_laenge * math.cos(winkel)
        drehung0 =gefaellestrecke_nachbarn.get("Drehung0")
        drehung1 = gefaellestrecke_nachbarn.get("Drehung1")
        hoehe0 = gefaellestrecke_nachbarn.get("Hoehe0")
        hoehe1 = gefaellestrecke_nachbarn.get("Hoehe1")
        x0_kreisel = float(gefaellestrecke_nachbarn.get("x0"))
        y0_kreisel = float(gefaellestrecke_nachbarn.get("y0"))
        x1_kreisel = float(gefaellestrecke_nachbarn.get("x1"))
        y1_kreisel = float(gefaellestrecke_nachbarn.get("y1"))
        richtung0 = float(gefaellestrecke_nachbarn.get("rotation0"))
        richtung1 = float(gefaellestrecke_nachbarn.get("rotation1"))
        richtung2 ="DEFAULT"
        richtung_rad0= math.radians(richtung0)
        richtung_rad1 = math.radians(richtung1)
        abstand0 = float(gefaellestrecke_nachbarn.get("abstand0")) * 1000
        abstand1 = float(gefaellestrecke_nachbarn.get("abstand1"))  * 1000
        rotation = float(merkmale.get("Drehung"))

        #ausrechnung position kreisel
        am_kreisel, kreisel_verbunden= as_es_methoden.am_kreisel_direct_verbunden(x, y, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, x0_kreisel, y0_kreisel, richtung_rad0, abstand0, dx, dy, x1_kreisel, y1_kreisel, richtung_rad1, abstand1)
        # falls gefälle strecke mit zwei kreiseln verbunden zeichne eine einfache linie (später braucht man hier auch eine logik sobald man die richtigen es und as elemente hat)
        if kreisel_verbunden == 2:
            start = x +dx, y + dy,upper_hoehe_gefaehlle
            ende  = x -dx, y - dy,lower_hoehe_gefaehlle
            line = msp.add_line(start,ende)
            line.dxf.layer = "6-SP"
            return

        #umbezeichnung von den geraden zu strings ob es Vertikal oder Horizontal ist
        if richtung0 == 90.0 or richtung0 ==270:
            richtung0= "Vertikal"
        else:
            richtung0 = "Horinzontal"
        if richtung1 == 90.0 or richtung1 ==270:
            richtung1= "Vertikal"
        else:
            richtung1 = "Horinzontal"
        richtung2,unterschiedlich = as_es_methoden.am_kreisel_direct_verbunden(x, y, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, x0_kreisel, y0_kreisel, richtung_rad0, abstand0, dx, dy, x1_kreisel, y1_kreisel, richtung_rad1, abstand1,kreisel_verbunden, richtung0 , richtung1 , richtung2 )
        #Berechnung des Gefälles
        if hoehe0 > hoehe1:
            hight_position = "higher"
        else:
            hight_position = "lower"
        if richtung2 == "DEFAULT":
            if richtung0 == "Vertikal":
                if x0_kreisel < x1_kreisel:
                    position = hight_position + "_links"
                else:
                    position = hight_position + "_rechts"
            else:
                if y0_kreisel > y1_kreisel:
                    position = hight_position + "_higher"
                else:
                    position = hight_position + "_lower"

            if richtung0 == "Vertikal":
                if position == "lower_rechts" or position == "higher_links":
                    gefaelle = "links"
                else:
                    gefaelle = "rechts"
            elif richtung0 == "Horinzontal":
                    if position == "lower_lower" or position == "higher_higher":
                        gefaelle = "oben"
                    else:
                        gefaelle = "unten"
            # vertausch der drehung und der höhe für die namens gebung des blockes
            if (position == "higher_rechts" or position == "lower_rechts" or position=="higher_lower" or position== "lower_lower") and drehung0 != drehung1 and am_kreisel == 0:
                drehung_2 = drehung0
                drehung0 = drehung1
                drehung1= drehung_2
                if hight_position == "higher":
                    hight_position = "lower"
                else:
                    hight_position = "higher"

            # austausch der werte damit immer davon ausgehen dass der 1 kreisel in unserer Liste am Kreisel verbuden ist
            if kreisel_verbunden == 1 and am_kreisel ==2:
                am_kreisel == 1
                drehung_2 = drehung0
                drehung0 = drehung1
                drehung1= drehung_2
                if hight_position == "higher":
                    hight_position = "lower"
                else:
                    hight_position = "higher"
        else:
            if richtung2 == "Vertikal":
                if x0_kreisel < x1_kreisel:
                    position = hight_position + "_links"
                else:
                    position = hight_position + "_rechts"
            else:
                if y0_kreisel > y1_kreisel:
                    position = hight_position + "_higher"
                else:
                    position = hight_position + "_lower"

            if richtung2 == "Vertikal":
                if position == "lower_rechts" or position == "higher_links":
                    gefaelle = "links"
                else:
                    gefaelle = "rechts"
            elif richtung2 == "Horinzontal":
                    if position == "lower_lower" or position == "higher_higher":
                        gefaelle = "oben"
                    else:
                        gefaelle = "unten"
            # austausch der werte damit immer davon ausgehen dass der 1 kreisel in unserer Liste am Kreisel verbuden ist
            if am_kreisel == 2:
                am_kreisel = 1
                drehung_2 = drehung0
                drehung0 = drehung1
                drehung1= drehung_2
                if hight_position == "higher":
                    hight_position = "lower"
                else:
                    hight_position = "higher"

        if gefaelle == "oben":
            rotation = 0
        elif gefaelle == "unten" :
            rotation = 180
        elif gefaelle == "links" :
            rotation = 90
        elif gefaelle == "rechts" :
            rotation = 270

        #geben der richtung2 eines wertes außer default wenn beide kreisel die gleiche richung haben
        if (kreisel_verbunden == 1  and richtung2 =="DEFAULT"):
            richtung2 = richtung0

        if richtung2 == "DEFAULT":
            blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{hoehe_gefaehlle}_{drehung0}_{drehung1}_{hight_position}_{verbunden_am_einen}"
        else:
            blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{hoehe_gefaehlle}_{drehung0}_{drehung1}_{hight_position}_{unterschiedlich}_{richtung2}_{verbunden_am_einen}"
        as_es_methoden.gefaellegerade_erstellung(x, y, doc, lib_doc, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, hoehe_gefaehlle,richtung2,drehung0, drehung1, laenge, hight_position,blockname,config)
        bref =msp.add_blockref(blockname,(x,y,hoehe_gefaehlle),dxfattribs={"rotation": rotation, "layer": gefaelle_layer})
        a = bref.add_attrib(
            tag= "NAME",
            text= merkmale.get("bezeichner"),
            insert = (x,y)
        )
        a.is_invisible = True
    #falls eine gefällestrecke nicht mit einem kreisel verbunden ist
    else:
        rotation= float(merkmale.get("Drehung"))
        if upper_hoehe_gefaehlle < lower_hoehe_gefaehlle:
            hoehe2 = upper_hoehe_gefaehlle
            upper_hoehe_gefaehlle = lower_hoehe_gefaehlle
            lower_hoehe_gefaehlle = hoehe2
            rotation = rotation -180

        block_Vario_Umlenkstation_500mm ="Vario_Umlenkstation_500mm"
        block_Vario_Motorstation_500mm = "Vario_Motorstation_500mm"
        blockname_motor_links = block_Vario_Motorstation_500mm +"links"
        blockname_umlenk_links = block_Vario_Umlenkstation_500mm + "links"
        hat_motor_0, hat_umlenk_0, hat_motor_1, hat_umlenk_1, tefkurve_0, tefkurve_1 = Gefaehllestrecke.Gefaellestrecke.hat_motor_umlenk_station(doc, lib_doc, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, gefaellestrecke_nachbarn)
        blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{hoehe_gefaehlle}_{hat_motor_0}_{hat_umlenk_0}_{tefkurve_0}_{hat_motor_1}_{hat_umlenk_1}_{tefkurve_1}"
        dy = halbe_laenge * math.cos(0)
        start = [x , y + dy ,upper_hoehe_gefaehlle]
        ende  = [x , y - dy ,lower_hoehe_gefaehlle]
        if hat_motor_0 == False and hat_umlenk_0 == False and hat_motor_1 == False and hat_umlenk_1 == False:
            dx = halbe_laenge *math.sin(winkel * -1)
            dy = halbe_laenge * math.cos(winkel)
            start = x +dx, y + dy,upper_hoehe_gefaehlle
            ende  = x -dx, y - dy,lower_hoehe_gefaehlle
            line = msp.add_line(start,ende)
            line.dxf.layer = "6-SP"
        elif (hat_motor_0 == True and hat_umlenk_0 == None) and tefkurve_1 == None:
            if blockname not in doc.blocks:
                block = doc.blocks.new(name=blockname,base_point = (0,0,0))
                if (hat_motor_0 == True and tefkurve_0 == "rechts") or (hat_motor_1 == True and tefkurve_1 == "rechts"):
                    block.add_blockref(block_Vario_Motorstation_500mm, (start[0]-x,start[1] - 250* math.cos(math.radians(3))-y,start[2] - 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
                else:
                    block.add_blockref(blockname_motor_links, (start[0]-x,start[1] - 250* math.cos(math.radians(3))-y,start[2] - 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
                start [1]= start[1] - 500* math.cos(math.radians(3))
                start[2] = start[2] - 500* math.sin(math.radians(3))
                line = Line.new(dxfattribs={"start":start,"end":ende})
                line.translate(-x,-y,-hoehe_gefaehlle)
                block.add_entity(line)
            msp.add_blockref(blockname,(x,y,hoehe_gefaehlle),dxfattribs={"rotation": rotation,"layer": gefaelle_layer})
        elif hat_motor_0 == None and hat_umlenk_0 == True and tefkurve_1 == None:
            if blockname not in doc.blocks:
                block = doc.blocks.new(name=blockname,base_point = (0,0,0))
                if (hat_umlenk_0 == True and tefkurve_0 == "rechts") or (hat_umlenk_1 == True and tefkurve_1 == "rechts"):
                    block.add_blockref(block_Vario_Umlenkstation_500mm, (ende[0]-x,ende[1] + 250* math.cos(math.radians(3))-y,ende[2] + 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
                else:
                    block.add_blockref(blockname_umlenk_links, (ende[0]-x,ende[1] + 250* math.cos(math.radians(3))-y,ende[2] + 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
                ende[2] = ende[2] + 500* math.sin(math.radians(3))  
                line = Line.new(dxfattribs={"start":start,"end":ende})
                line.translate(-x,-y,-hoehe_gefaehlle)
                block.add_entity(line)
            msp.add_blockref(blockname,(x,y,hoehe_gefaehlle),dxfattribs={"rotation": rotation,"layer": gefaelle_layer})
        else:
            if blockname not in doc.blocks:
                block = doc.blocks.new(name=blockname,base_point = (0,0,0))
                if (hat_motor_0 == True and tefkurve_0 == "rechts") or (hat_motor_1 == True and tefkurve_1 == "rechts"):
                    block.add_blockref(block_Vario_Motorstation_500mm, (start[0]-x,start[1] - 250* math.cos(math.radians(3))-y,start[2] - 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
                else:
                    block.add_blockref(blockname_motor_links, (start[0]-x,start[1] - 250* math.cos(math.radians(3))-y,start[2] - 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})                
                start[2] = start[2] - 500* math.sin(math.radians(3))
                if (hat_umlenk_0 == True and tefkurve_0 == "rechts") or (hat_umlenk_1 == True and tefkurve_1 == "rechts"):
                    block.add_blockref(block_Vario_Umlenkstation_500mm, (ende[0]-x,ende[1] + 250* math.cos(math.radians(3))-y,ende[2] -+250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})
                else:
                    block.add_blockref(blockname_umlenk_links, (ende[0]-x,ende[1] + 250* math.cos(math.radians(3))-y,ende[2] + 250* math.sin(math.radians(3))-hoehe_gefaehlle),dxfattribs={"rotation": 270})               
                ende[2] = ende[2] + 500* math.sin(math.radians(3))  
                line = Line.new(dxfattribs={"start":start,"end":ende})
                line.translate(-x,-y,-hoehe_gefaehlle)
                block.add_entity(line)
            msp.add_blockref(blockname,(x,y,hoehe_gefaehlle),dxfattribs={"rotation": rotation,"layer": gefaelle_layer})

def anzahl_seperatoren_oder_scan(msp, x, y, doc, lib_doc, klassen_objekt, hoehe, rotation):
    """Importiert alle seperatoren und/oder scanner für das nötige objekt"""
    separatoren = klassen_objekt.anzahl_separatoren
    scanner = klassen_objekt.anzahl_scanner
    if rotation == 0 or rotation == -180: 
        ausrichtung = "V"
    else:
        ausrichtung = "H"
    if ausrichtung == "V":
        einsatz_fest = [x + 300, y,hoehe]
        modular = 2
    else:
        einsatz_fest = [x,y - 150,hoehe]
        modular = 3
    block_scanner = "SCAN"
    block_separatoren = "S-LP"
    import_block(block_scanner,lib_doc,doc)
    import_block(block_separatoren,lib_doc,doc)
    layer_scan, color_scan = get_insert_color_layer(lib_doc, block_scanner)
    layer_separatioren, color_separatioren = get_insert_color_layer(lib_doc, block_separatoren)
    einsatz_zwischen =[ einsatz_fest[0],einsatz_fest[1],einsatz_fest[2]]
    anzahl =0
    while anzahl < separatoren:
        anzahl = anzahl + 1
        msp.add_blockref(block_separatoren,einsatz_zwischen, dxfattribs={"layer": layer_separatioren,"color": color_separatioren})
        if anzahl % modular == 0:
            einsatz_fest[1] = einsatz_fest[1] - 150
            einsatz_zwischen = einsatz_fest.copy()
        else:
            einsatz_zwischen[0] = einsatz_zwischen[0]+ 300

    if anzahl % modular != 0:
        einsatz_fest[1] = einsatz_fest[1] - 150
    einsatz_zwischen = einsatz_fest.copy()
    anzahl =0
    while anzahl < scanner:
        anzahl = anzahl + 1
        msp.add_blockref(block_scanner,einsatz_zwischen,dxfattribs={"layer": layer_scan,"color": color_scan})
        if anzahl % modular == 0:
            einsatz_fest[1] = einsatz_fest[1] - 150
            einsatz_zwischen = einsatz_fest.copy()
        else:
            einsatz_zwischen[0] = einsatz_zwischen[0]+ 300


        
def add_attributes_to_block(block, attributes):
    
    """
    Fügt einem bestehenden ezdxf-Block Attribut-Definitionen hinzu.
    
    Parameter:
        block       – ein ezdxf.blocks.BlockLayout Objekt
        attributes  – Liste von Tupeln [(tag, value), ...]
    """
    for tag, value in attributes.items():

        tag = tag
        value = value
        a =block.add_attrib(
            tag=tag,
            text=value,
        )
        a.is_invisible = True
        
def handle_ils_2_0_variofoerderer(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, strecken_nachbarn,config,config_allgemein):
    """Erstellt ein Vario Förderer in der neuen Dxf"""
    foerderer = VarioFoerderer.VarioFoerderer.from_merkmale(teileid,x,y,merkmale)
    # für spätere Namens benenung der Vario forderer
    motor_vorhanden = foerderer.hat_motor
    umlenk_vorhanden = foerderer.hat_umlenk
    gefahellewinkel = foerderer.gefaelle_winkel
    gefaelle = foerderer.gefaelle_laenge
    # Offsets für die Vario Linie für ab 3 bogen, da es in diesem Fall keine bögen hat die sich mit den nachbarn Bögen für andere Vario Förderer verbinden kann 
    winkel_VP_offset_vorne = None
    winkel_VP_offset_hinten = None
    # erstellung des Layers falls nicht vorhanden
    if "VARIO" not in doc.layers:
            doc.layers.add(name="VARIO", color=3)
    
    if "6-SP" not in doc.layers:
            doc.layers.add(name="6-SP", color=7)
    # Vorbereitung der Werte 
    voerder_richtung = foerderer.foerderer_richtung
    winkel = int(foerderer.winkel)
    erster_kreisel_höher = False
    ein_kreisel_höher = False
    richtung2 ="DEFAULT"
    rotation = foerderer.drehung
    upper_hoehe_vario= foerderer.h1
    lower_hoehe_vario = foerderer.h0
    hoehe_vario= foerderer.hight_zwischen
    anzahl_seperatoren_oder_scan(msp, x, y, doc, lib_doc, foerderer, hoehe_vario, rotation)
    # Umstellung der Höhen falls nötig für die Konsistenz der spätere Erstellung, ist nur notwendig für ab Vario Förderer
    if upper_hoehe_vario< lower_hoehe_vario:
        hight = upper_hoehe_vario
        upper_hoehe_vario = lower_hoehe_vario
        foerderer.h1 = upper_hoehe_vario
        lower_hoehe_vario = hight
        foerderer.h0 = lower_hoehe_vario
        # Korrektur der rotation mit der Umstellung der Höhe
        rotation = rotation -180
        foerderer.drehung = rotation

    
    # Hollen der Information der Nachbarn strukturen ob diese Kreisel sind
    for nachbarn in strecken_nachbarn:
        if teileid == nachbarn.get("Id"):
            gefaellestrecke_vario = nachbarn
            break
    laenge = foerderer.laenge
# Ausrechnung der nötigen Offset falls der Vario Förderer ab mit drei grad mit einem anderen Verbunden ist
    winkel_VP_offset_vorne, winkel_VP_offset_hinten = VarioFoerderer.VarioFoerderer.get_offset_of_Vario_line(doc, lib_doc,foerderer, gefaellestrecke_vario)
                        
    #  Für spätere berechnung schauen ob der erste Kreis in der Liste höher ist
    if upper_hoehe_vario == gefaellestrecke_vario.get("Hoehe0"):
        ein_kreisel_höher = True
    # Aufruf falls nur mit einem Kreisel verbunden
    if "Drehung0" in gefaellestrecke_vario and "Drehung1" not in gefaellestrecke_vario:
        # Vorbereitund der Daten des Kreisel
        halbe_laenge = laenge / 2
        zwischen_winkel = float(merkmale.get("Drehung"))
        richtung_rad = math.radians(zwischen_winkel)
        dx = halbe_laenge * math.sin(-1 *richtung_rad)
        dy = halbe_laenge * math.cos(richtung_rad)
        drehung0 = gefaellestrecke_vario.get("Drehung0")
        abstand0 = float(gefaellestrecke_vario.get("abstand0")) * 1000
        x0_kreisel = float(gefaellestrecke_vario.get("x0"))
        y0_kreisel = float(gefaellestrecke_vario.get("y0"))
        richtung0 = float(gefaellestrecke_vario.get("rotation0"))
        richtung_rad0= math.radians(richtung0)
        # Herausfinden ob der Förderere direkt mit dem Kreisel verbunden ist, die Werte für den Zweiten Kreisel wird auf None gesetzt weil diese nicht existieren

        mit_horizontal_verbunden = None
        if voerder_richtung == "Horizontal":
            if rotation == 0.0:
                if y> y0_kreisel:
                    mit_horizontal_verbunden = "unten_drehung_0_or_-90"
                else:
                    mit_horizontal_verbunden = "oben_drehung_0_or_-90"
            if rotation == -180.0:
                if y> y0_kreisel:
                    mit_horizontal_verbunden = "unten_drehung_-180_or_-270"
                else:
                    mit_horizontal_verbunden = "oben_drehung_-180_or_-270"
            if rotation == -90.0:
                if x> x0_kreisel:
                    mit_horizontal_verbunden = "unten_drehung_0_or_-90"
                else:
                    mit_horizontal_verbunden = "oben_drehung_0_or_-90"
            if rotation == -270.0:
                if x> x0_kreisel:
                    mit_horizontal_verbunden = "unten_drehung_-180_or_-270"
                else:
                    mit_horizontal_verbunden = "oben_drehung_-180_or_-270"
        am_kreisel,kreseil_verbunden =as_es_methoden.am_kreisel_direct_verbunden(x, y, upper_hoehe_vario, lower_hoehe_vario, x0_kreisel, y0_kreisel, richtung_rad0, abstand0, dx, dy, None, None, None, None )
        if am_kreisel == 1:
            # Erstellung der blockname für wenn die Motorstation rechts und links
            blockname =  (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{ein_kreisel_höher}_{drehung0}_True_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}")
            block_name_links =  (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{ein_kreisel_höher}_{drehung0}_True_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}")
            # Falls der Block bereits in dem doc ist platziere diesen einfach
            if blockname  in doc.blocks:
                if merkmale.get("Motorseite")== "links":
                    msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                    return
                elif merkmale.get("Motorseite")== "rechts":
                    msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                    return
            # Erstellung des Blocks und diesen in die Modelspace tuen. Die Linke version wird bei der vario erstellung selber am ende gemacht
            block_vario = doc.blocks.new(blockname, base_point=(0,0,0))
            dy_true = halbe_laenge * math.cos (0)
            start = (x, y + dy_true,upper_hoehe_vario)
            ende = (x,y -dy_true,lower_hoehe_vario)
            # Erstellung einer Gefällestrecke von 500 mm in der Vario rein, wo es verbunden ist
            if ein_kreisel_höher == True and voerder_richtung != "Horizontal":

                l = (start[0],start[1],upper_hoehe_vario)
                if voerder_richtung =="Auf":
                    m = (start[0],start[1] - 500*math.cos(math.radians(3)),upper_hoehe_vario +500* math.sin(math.radians(3)))
                    start = m
                elif voerder_richtung == "Ab":
                    m = (start[0],start[1] - 500*math.cos(math.radians(3)),upper_hoehe_vario - 500* math.sin(math.radians(3)))
                    start = m
                else:
                    if mit_horizontal_verbunden == "unten_drehung_0_or_-90" or mit_horizontal_verbunden == "oben_drehung_-180_or_-270":
                        m = (start[0],start[1] - 500*math.cos(math.radians(3)),upper_hoehe_vario+500* math.sin(math.radians(3)))
                        start = m
                    else:
                        en = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario- 500* math.sin(math.radians(3)) )
                        ende = en

                line = Line.new(dxfattribs={"start": l,"end":m })
                line.dxf.layer = "6-SP"
                copy= line.copy()
                copy.translate(-x,-y,-hoehe_vario)
                block_vario.add_entity(copy)


            elif ein_kreisel_höher == False and voerder_richtung != "Horizontal":
                l = (ende[0],ende[1],lower_hoehe_vario)
                if voerder_richtung =="Auf":
                    m = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario - 500* math.sin(math.radians(3)))
                    ende = m
                elif voerder_richtung == "Ab":
                    m = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario + 500* math.sin(math.radians(3)))
                    ende = m
                else:
                    m = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario )
                    ende = m
                line = Line.new(dxfattribs={"start": l,"end":m })
                line.dxf.layer = "6-SP"
                copy= line.copy()
                copy.translate(-x,-y,-hoehe_vario)
                block_vario.add_entity(copy)
            elif voerder_richtung == "Horizontal":
                if mit_horizontal_verbunden == "oben_drehung_0_or_-90" or mit_horizontal_verbunden == "unten_drehung_-180_or_-270":
                    l = (start[0],start[1],upper_hoehe_vario)
                    m = (start[0],start[1] - 500*math.cos(math.radians(3)),upper_hoehe_vario)
                    start = m
                    line = Line.new(dxfattribs={"start": l,"end":m })
                    line.dxf.layer = "6-SP"
                    copy= line.copy()
                    copy.translate(-x,-y,-hoehe_vario)
                    block_vario.add_entity(copy)
                else:
                    end = (ende[0],ende[1],lower_hoehe_vario)
                    en = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario )
                    ende = en
                    line = Line.new(dxfattribs={"start": end,"end":en })
                    line.dxf.layer = "6-SP"
                    copy= line.copy()
                    copy.translate(-x,-y,-hoehe_vario)
                    block_vario.add_entity(copy)
        
            # Erstellung des Vario_förderes selber
            VarioFoerderer.VarioFoerderer.vario_erstellung(foerderer, doc, lib_doc, config, block_vario,block_name_links, start, ende,voerder_richtung ,winkel_VP_offset_vorne,winkel_VP_offset_hinten)
            # reintuen des förderes in den Modelspace
            if merkmale.get("Motorseite")== "links":
                msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                return
            elif merkmale.get("Motorseite")== "rechts":
                msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                return
        # Abschnitt falls der Vario nur mit einem Kreisel verbunden ist und nicht an dem Kreisel direkt verbunden ist
        
        # Erstellung der blockname für wenn die Motorstation rechts und links
        blockname =  (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{ein_kreisel_höher}_{drehung0}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}")
        block_name_links =  (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{ein_kreisel_höher}_{drehung0}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}")
        # Falls der Block bereits in dem doc ist platziere diesen einfach
        if blockname  in doc.blocks:
            if merkmale.get("Motorseite")== "links":
                msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                return
            elif merkmale.get("Motorseite")== "rechts":
                msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                return
        # Erstellung des Blocks und diesen in die Modelspace tuen. Die Linke version wird bei der vario erstellung selber am ende gemacht
        block_vario = doc.blocks.new(blockname, base_point=(0,0,0))
        dy_true = halbe_laenge * math.cos (0)
        # Schauen welches as oder es element man wo verbinden muss und bereits in den block tuen, der None wert ist ein Wert der für die Gefällestrecke notwendig ist
        # Entnehmen von start und end Werte für spätere vario erstellung
        
        start, ende = as_es_methoden.erstellung_gefaelle_block_verbunenden_am_einen(msp,x, y, doc, lib_doc, upper_hoehe_vario, lower_hoehe_vario, hoehe_vario, drehung0, laenge, blockname,config,None ,block_vario, voerder_richtung, ein_kreisel_höher,None,None,None,mit_horizontal_verbunden)
        # Erstellung des Varios selber
        VarioFoerderer.VarioFoerderer.vario_erstellung(foerderer, doc, lib_doc, config, block_vario,block_name_links, start, ende,voerder_richtung ,winkel_VP_offset_vorne,winkel_VP_offset_hinten)
        # reintuen des förderes in den Modelspace
        if merkmale.get("Motorseite")== "links":
            msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
        if merkmale.get("Motorseite")== "rechts":
            msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})

    # Erstellung einer Vario förderes wenn es mit zwei Kreisel verbunden ist
    elif "Drehung0" in gefaellestrecke_vario and "Drehung1" in gefaellestrecke_vario:
        #  Vorbereitung der Werte für beide Kreisel                                                               
        halbe_laenge = laenge / 2
        zwischen_winkel = float(merkmale.get("Drehung"))
        richtung_rad = math.radians(zwischen_winkel)
        dx = halbe_laenge * math.sin(-1 *richtung_rad)
        dy = halbe_laenge * math.cos(richtung_rad)
        drehung0 = gefaellestrecke_vario.get("Drehung0")
        abstand0 = float(gefaellestrecke_vario.get("abstand0")) * 1000
        x0_kreisel = float(gefaellestrecke_vario.get("x0"))
        y0_kreisel = float(gefaellestrecke_vario.get("y0"))
        richtung0 = float(gefaellestrecke_vario.get("rotation0"))
        richtung_rad0= math.radians(richtung0)
        kreisel_hoehe0 =float(gefaellestrecke_vario.get("Hoehe0"))

        drehung1 = gefaellestrecke_vario.get("Drehung1")
        abstand1 = float(gefaellestrecke_vario.get("abstand1")) * 1000
        x1_kreisel = float(gefaellestrecke_vario.get("x1"))
        y1_kreisel = float(gefaellestrecke_vario.get("y1"))
        richtung1 = float(gefaellestrecke_vario.get("rotation1"))
        richtung_rad1= math.radians(richtung1)
        kreisel_hoehe1= float(gefaellestrecke_vario.get("Hoehe1"))
        # Anpassung der Kreisel, damit der Höhere Kreisel immer zuerst ist
        if kreisel_hoehe0< kreisel_hoehe1:
            drehung2 = drehung0
            drehung0 = drehung1
            drehung1 = drehung2

        # Schauen ob der Förderer direkt am Kreisel verbunden ist
        am_kreisel,kreisel_verbunden = as_es_methoden.am_kreisel_direct_verbunden(x, y, upper_hoehe_vario, lower_hoehe_vario, x0_kreisel, y0_kreisel, richtung_rad0, abstand0, dx, dy, x1_kreisel, y1_kreisel, richtung_rad1, abstand1)
        # Falls der Förder mit beiden Kreisel verbunden ist
        if kreisel_verbunden == 2:
                # Erstellung der blockname für wenn die Motorstation rechts und links
                blockname =  (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{drehung0}_{drehung1}_{kreisel_verbunden}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}")
                block_name_links = (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{drehung0}_{drehung1}_{kreisel_verbunden}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}")
                # Falls der Block bereits in dem doc ist platziere diesen einfach
                if blockname  in doc.blocks:
                    if merkmale.get("Motorseite")== "links":
                        msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                        return
                    elif merkmale.get("Motorseite")== "rechts":
                        msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                        return
                # Erstellung des Blocks und diesen in die Modelspace tuen. Die Linke version wird bei der vario erstellung selber am ende gemacht
                block_vario = doc.blocks.new(blockname, base_point=(0,0,0))
                dy_true = halbe_laenge * math.cos (0)
                start = (x, y + dy_true,upper_hoehe_vario)
                ende = (x,y -dy_true,lower_hoehe_vario)
                l = (start[0],start[1],upper_hoehe_vario)
                # Eine 500 mm gefällestrecke an anfang und am ende reintuen
                if voerder_richtung =="Auf":
                    m = (start[0],start[1] - 500*math.cos(math.radians(3)),upper_hoehe_vario +500* math.sin(math.radians(3)))
                    start = m

                elif voerder_richtung == "Ab":
                    m = (start[0],start[1] - 500*math.cos(math.radians(3)),upper_hoehe_vario -500* math.sin(math.radians(3)))
                    start = m

                else:
                    m = (start[0],start[1] - 500*math.cos(math.radians(3)),upper_hoehe_vario )
                    start = m

                line = Line.new(dxfattribs={"start": l,"end":m })
                line.dxf.layer = "6-SP"
                copy= line.copy()
                copy.translate(-x,-y,-hoehe_vario)
                block_vario.add_entity(copy)

                l = (ende[0],ende[1],lower_hoehe_vario)
                if voerder_richtung =="Auf":
                    m = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario - 500* math.sin(math.radians(3)))
                    ende = m

                elif voerder_richtung == "Ab":
                    m = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario + 500* math.sin(math.radians(3)))
                    ende = m
                else:
                    m = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario )
                    ende = m
                line = Line.new(dxfattribs={"start": l,"end":m })
                line.dxf.layer = "6-SP"
                copy= line.copy()
                copy.translate(-x,-y,-hoehe_vario)
                block_vario.add_entity(copy)
                # Die Vario erstellung selber
                VarioFoerderer.VarioFoerderer.vario_erstellung(foerderer,doc, lib_doc, config,  block_vario,block_name_links, start, ende,voerder_richtung ,winkel_VP_offset_vorne,winkel_VP_offset_hinten)
                # reintuen des förderes in den Modelspace
                if merkmale.get("Motorseite")== "links":
                    msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                    return
                elif merkmale.get("Motorseite")== "rechts":
                    msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                    return
        # Falls der Förderer nur mit einem Kreisel direkt verbunden ist
        if am_kreisel != 0:
            #schauen ob der erste Kreisel höher ist
            if upper_hoehe_vario == kreisel_hoehe0:
                erster_kreisel_höher = True
            else:
                erster_kreisel_höher = False
            # Erstellung der blockname für wenn die Motorstation rechts und links
            blockname =  (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{am_kreisel}_{erster_kreisel_höher}_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}")
            block_name_links =  (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{am_kreisel}_{erster_kreisel_höher}_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}")

            # Falls der Block bereits in dem doc ist platziere diesen einfach
            if blockname  in doc.blocks:
                if merkmale.get("Motorseite")== "links":
                    msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                    return
                elif merkmale.get("Motorseite")== "rechts":
                    msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                    return
            # Erstellung des Blocks und diesen in die Modelspace tuen. Die Linke version wird bei der vario erstellung selber am ende gemacht
            block_vario = doc.blocks.new(blockname, base_point=(0,0,0))
            dy_true = halbe_laenge * math.cos(0)
            
            # schauen ob der Förderer mit welchen Kreisel der Förderer verbunden ist, und dem entsprechend eine Gefällestrecke dort reintuern
            if (am_kreisel == 1 and erster_kreisel_höher == True)or (am_kreisel == 2 and erster_kreisel_höher == False):
                start = (x, y + dy_true,upper_hoehe_vario)
                ende = (x,y -dy_true,lower_hoehe_vario)
                l = (start[0],start[1],upper_hoehe_vario)
                if voerder_richtung == "Auf":
                    m = (start[0],start[1] - 500*math.cos(math.radians(3)),upper_hoehe_vario +500* math.sin(math.radians(3)))
                    z1 = m[2]
                elif voerder_richtung == "Ab":
                    m = (start[0],start[1] - 500*math.cos(math.radians(3)),upper_hoehe_vario -500* math.sin(math.radians(3)))
                    z1 = m[2]
                else:
                    m= (x,y -dy_true,lower_hoehe_vario)
                    z1 = m[2]


                line = Line.new(dxfattribs={"start": l,"end":m })
                line.dxf.layer = "6-SP"
                copy= line.copy()
                copy.translate(-x,-y,-hoehe_vario)
                block_vario.add_entity(copy)
                y1 =  start[1] - math.cos(math.radians(3)) *500

            else:
                start = (x, y + dy_true,upper_hoehe_vario)
                ende = (x,y -dy_true,lower_hoehe_vario)
                l = (ende[0],ende[1],lower_hoehe_vario)
                if voerder_richtung =="Auf":
                    m = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario - 500* math.sin(math.radians(3)))
                    z1 = m[2]
                elif voerder_richtung == "Ab":
                    m = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario + 500* math.sin(math.radians(3)))
                    z1 = m[2]
                else:
                    m = (ende[0],ende[1] +  500*math.cos(math.radians(3)) ,lower_hoehe_vario )
                    z1 = m[2]

                line = Line.new(dxfattribs={"start": l,"end":m })
                line.dxf.layer = "6-SP"
                copy= line.copy()
                copy.translate(-x,-y,-hoehe_vario)
                block_vario.add_entity(copy)
                y1 =  ende[1] + math.cos(math.radians(3)) *500

            # Schauen welche es oder as element man braucht man braucht und diese in den block einfügen
            # Entnehmen von start und end Werte für spätere vario erstellung
            start, ende = as_es_methoden.gefaellegerade_erstellung(x, y, doc, lib_doc, upper_hoehe_vario, lower_hoehe_vario, hoehe_vario,richtung2, drehung0, drehung1, laenge, None, blockname,config,block_vario,voerder_richtung, am_kreisel,erster_kreisel_höher,y1,z1)
            # Erstellung der Vario gefälle selber
            VarioFoerderer.VarioFoerderer.vario_erstellung(foerderer, doc, lib_doc, config, block_vario,block_name_links, start, ende,voerder_richtung ,winkel_VP_offset_vorne,winkel_VP_offset_hinten)
            # Reintuen des endblockes in den Modelspace
            if merkmale.get("Motorseite")== "links":
                msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
            elif merkmale.get("Motorseite")== "rechts":
                msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})

        # Falls es nicht mit dem Kreisel direkt verbunden ist
        else:
            # Erstellung der blockname für wenn die Motorstation rechts und links
            blockname =  (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}")
            block_name_links =  (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}")

            # Falls der Block bereits in dem doc ist platziere diesen einfach
            if blockname  in doc.blocks:
                if merkmale.get("Motorseite")== "links":
                    msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                    return
                elif merkmale.get("Motorseite")== "rechts":
                    msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                    return

            # Erstellung des Blocks und diesen in die Modelspace tuen. Die Linke version wird bei der vario erstellung selber am ende gemacht
            block_vario = doc.blocks.new(blockname, base_point=(0,0,0))
            # Schauen welche es oder as element man braucht man braucht und diese in den block einfügen
            # Entnehmen von start und end Werte für spätere vario erstellung

            start, ende = as_es_methoden.gefaellegerade_erstellung(x, y, doc, lib_doc, upper_hoehe_vario, lower_hoehe_vario, hoehe_vario,richtung2, drehung0, drehung1, laenge, None, blockname,config,block_vario,voerder_richtung)
            # Erstellung des Vario selber
            VarioFoerderer.VarioFoerderer.vario_erstellung(foerderer, doc, lib_doc, config, block_vario,block_name_links, start, ende,voerder_richtung,winkel_VP_offset_vorne,winkel_VP_offset_hinten )
            # Reintuen des endblockes in den Modelspace
            if merkmale.get("Motorseite")== "links":
                msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
            elif merkmale.get("Motorseite")== "rechts":
                msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
    
    # Erstellung des Varios falls es nicht mit einem Kreisel verbunden ist
    else:
        halbe_laenge = laenge/2
        dy = halbe_laenge * math.cos(0)
        # Erstellung der blockname für wenn die Motorstation rechts und links
        blockname = (f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}")
        block_name_links =(f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}")
        # Falls der Block bereits in dem doc ist platziere diesen einfach
        if blockname  in doc.blocks:
            if merkmale.get("Motorseite")== "links":
                msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                return
            elif merkmale.get("Motorseite")== "rechts":
                msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
                return
       
        # Erstellung des Blocks und diesen in die Modelspace tuen. Die Linke version wird bei der vario erstellung selber am ende gemacht
        block = doc.blocks.new(blockname, base_point=(0,0,0))

        # Erstellung von start und ende
        start = (x,y +dy, upper_hoehe_vario)
        ende = (x ,y -dy, lower_hoehe_vario)
        # Erstellung des Förderes selber

        VarioFoerderer.VarioFoerderer.vario_erstellung(foerderer, doc, lib_doc, config, block,block_name_links, start, ende,voerder_richtung ,winkel_VP_offset_vorne,winkel_VP_offset_hinten)
        # Reintuen des endblockes in den Modelspace
        if merkmale.get("Motorseite")== "links":
            msp.add_blockref(block_name_links,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})
            
        if merkmale.get("Motorseite")== "rechts":
            msp.add_blockref(blockname,(x,y,hoehe_vario),dxfattribs={"rotation": rotation})

def handle_ils_2_0_kurve_angetrieben(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, strecken_nachbarn,config,config_allgemein):
    """Erstellt eine Angetriebene Kurve (Förderer Kurve) in der neuen Dxf"""
    voerder_kurve = Angetriebene_Kurve.Angetriebene_Kurve.from_merkmale(teileid,x,y,merkmale)
    kurvenwinkel = voerder_kurve.winkel
    h_zwischen = voerder_kurve.hight_zwischen
    antriebNebenStrecke = voerder_kurve.antrieb
    kurvenrichtung = voerder_kurve.kurvenrichtung
    rotation = voerder_kurve.drehung                  
    blockname = (f"Vario_Kurve_{kurvenrichtung}_{kurvenwinkel}°_TEF_{antriebNebenStrecke}")
    import_block(blockname,lib_doc,doc)
    layer, color = get_insert_color_layer(lib_doc, blockname)
    msp.add_blockref(blockname,(x,y,h_zwischen),dxfattribs={"rotation": rotation,"layer": layer, "color":color})
    

def handle_ils_2_0_kurve(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, strecken_nachbarn,config,config_allgemein):
    """Erstellt eine Kurve (Gefälle Kurve) in der neuen Dxf"""
    rotation= float(merkmale.get("Drehung"))
    h0 = float(merkmale.get("Höhe Anfang")) * 1000
    h1 = float(merkmale.get("Höhe Ende")) * 1000
    hz = (h0 + h1)/2
    kurvenrichtung = merkmale.get("Kurvenrichtung")
    kurvenwinkel = merkmale.get("Kurvenwinkel")
    blockname = (f"")
    import_block("AN8",lib_doc,doc)
    msp.add_blockref("AN8",(x,y,hz),dxfattribs={"rotation": rotation})

def handle_bt___beladung(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, strecken_nachbarn,config,config_allgemein):
    """Erstellt ein BT Element in der neuen Dxf"""
    bt_element = Bt_element.Bt_element.from_merkmale(teileid,x,y,merkmale)
    rotation = bt_element.drehung
    hight = bt_element.hoehe
    blockname = "AN8"
    import_block(blockname,lib_doc,doc)
    msp.add_blockref(blockname,(x,y,hight),dxfattribs={"rotation": rotation})

def handle_bt___entladung(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, strecken_nachbarn,config,config_allgemein):
    """Erstellt ein BT Element in der neuen Dxf"""
    bt_element = Bt_element.Bt_element.from_merkmale(teileid,x,y,merkmale)
    rotation = bt_element.drehung
    hight = bt_element.hoehe
    blockname = "AN8"
    import_block(blockname,lib_doc,doc)
    msp.add_blockref(blockname,(x,y,hight),dxfattribs={"rotation": rotation})


def handle_omniflo(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, strecken_nachbarn,config,config_allgemein):
    """
    Für Omniflo Gerade: zeichnet eine Linie (Mitte = Koordinate, Länge und Winkel aus Merkmale).
    Für alle anderen Omniflo-Typen: Block mit SivasNummer an den Koordinaten.
    """
    # Prüfen, ob es sich um eine Gerade handelt
    omnisivas = config.get("Omniflo","OFgeradesivas")
    tefsivas = config.get("Omniflo","Tefgeradesivas")
    foerderer = config.get("Omniflo","OFfoerderer")
    omniflo_objekt = Omniflo.Omniflo.from_merkmale(teileid,x,y,merkmale)
    rotation = omniflo_objekt.drehung
    if omniflo_objekt.sivasnummer == omnisivas or omniflo_objekt.sivasnummer == tefsivas:
        Omniflo.Omniflo.Omniflo_geraden_erstellung(msp, x, y, doc, tefsivas, omniflo_objekt)
     
    elif omniflo_objekt.sivasnummer == foerderer:
        Omniflo.Omniflo.omniflo_foerdererstellung(msp, x, y, doc, lib_doc, omniflo_objekt, rotation)
    # Sonst wie gehabt: Block mit SivasNummer
    else:
        if not lib_doc:
            print("[WARN] lib_doc nicht verfügbar, Block wird nicht eingefügt.")
            return
        blockname = merkmale.get("SivasNummer")
        if not blockname:
            print(f"[WARN] Keine SivasNummer für {teileid}, überspringe.")
            return
        if blockname not in lib_doc.blocks:
            print(f"[WARN] Omniflo-Block '{blockname}' nicht in Bibliothek {lib_doc.filename}. Überspringe {teileid}.")
            return
        blockname = blockname
        import_block(blockname, lib_doc, doc)
        layer, color = get_insert_color_layer(lib_doc, omniflo_objekt.sivasnummer)
        msp.add_blockref(blockname, (x, y,omniflo_objekt.hoehe), dxfattribs={"rotation": rotation,"layer": layer, "color": color})


def get_insert_color_layer(lib_doc, blockname):
    """Gibt den Layer und die Color für den Jeweiligen block in der Libary datei zurück"""    
    msp_lib = lib_doc.modelspace()
    color = 0
    layer = 0
    for insert in msp_lib.query("INSERT"):
        if insert.dxf.name == blockname:
            color = insert.dxf.color
            layer = insert.dxf.layer
    return layer, color


def normalize_func_name(name):
    return (
        name.replace('ä', 'ae')
            .replace('ö', 'oe')
            .replace('ü', 'ue')
            .replace('ß', 'ss')
            .replace(' ', '_')
            .replace('.', '_')
            .replace('-', '_')
            .lower()
    )

def get_libfile_cfg(teileart, cfg_path):
    """Liest den Bibliotheksdateinamen für eine TeileArt aus der allgemein.cfg."""
    parser = configparser.ConfigParser()
    with open(cfg_path, encoding='utf-8') as f:
        parser.read_file(f)
    # Teileart kann z.B. "ILS 2.0 Kreisel" sein, wir nehmen den ersten Teil vor erstem Leerzeichen oder Punkt
    # oder suchen iterativ nach Sektionen, die im Teileart-Namen vorkommen
    for section in parser.sections():
        if section in teileart:
            return parser.get(section, "libfile", fallback=None)
    return None

def get_nachbar_information(csv_path:Path) -> dict:
    """Gibt die Art und nötige Elemente den Nachbardateien zurück""" 
    geraden = []
    kreisel =[]
    strecken_nachbarn = []
    angetriebene_kurve= []
    anweisungen = 0
    """Gib für jede gefällestrecke zurück welche Drehrichtung die benachbarten Kreisel haben """
    with csv_path.open(newline="", encoding="utf-8") as fh:
        reader = csv.DictReader(fh, delimiter=';')
        for row in reader:
            bezeichner = row["TeileArt"].strip()
            if bezeichner == "ILS 2.0 Gefällestrecke":
                Id = row["TeileId"].strip()
                NachbarIds = row["NachbarIds"].strip()
                geraden.append({"Id": Id, "NachbarIds": NachbarIds})
            if bezeichner == "ILS 2.0 Kreisel" or bezeichner == "ILS 2.0 Kreisel mit Pin":
                Id = row["TeileId"].strip()
                planquadrat = row["Planquadrat"]
                x, y = extract_coords(planquadrat)
                merkmale = parse_merkmale(row.get("Merkmale", ""))
                # Erstelle Kreisel-Objekt
                kreisel_obj = Kreisel.Kreisel.from_merkmale(Id, x, y, merkmale)
                # Für Kompatibilität auch als Dict speichern (für bestehende Code-Stellen)
                kreisel.append({
                    "Id": Id, 
                    "drehung": kreisel_obj.drehrichtung, 
                    "höhe": kreisel_obj.hoehe,
                    "x": kreisel_obj.x, 
                    "y": kreisel_obj.y, 
                    "rotation": kreisel_obj.drehung,
                    "abstand": str(kreisel_obj.abstand / 1000)  # Zurück in Meter als String
                })
            if bezeichner =="ILS 2.0 VarioFoerderer":
                Id = row["TeileId"].strip()
                NachbarIds = row["NachbarIds"].strip()
                merkmale = parse_merkmale(row.get("Merkmale", ""))
                planquadrat = row["Planquadrat"]
                x, y = extract_coords(planquadrat)
                foerderer_objekt = VarioFoerderer.VarioFoerderer.from_merkmale(Id, x,y,merkmale)
                winkel = foerderer_objekt.winkel
                h0 = foerderer_objekt.h0
                h1 = foerderer_objekt.h1
                foerderrichtung = foerderer_objekt.foerderer_richtung
                geraden.append({"Id": Id,"NachbarIds":NachbarIds, "Winkel":winkel, "h0": h0,"h1": h1,"Foerderrichtung":foerderrichtung,"X_foerderer": x,"Y_foerderer": y
                                })
            if bezeichner =="ILS 2.0 Kurve angetrieben":
                Id = row["TeileId"].strip()
                planquadrat = row["Planquadrat"]
                x, y = extract_coords(planquadrat)
                merkmale = parse_merkmale(row.get("Merkmale", ""))
                kurve_angetrieben = Angetriebene_Kurve.Angetriebene_Kurve.from_merkmale(Id,x,y,merkmale)
                h0 = kurve_angetrieben.hoehe0
                h1 = kurve_angetrieben.hoehe1
                kurvenrichtung = kurve_angetrieben.kurvenrichtung
                tefkurve = kurve_angetrieben.antrieb
                kurve_winkel = kurve_angetrieben.winkel
                angetriebene_kurve.append({"Id": Id,"H0": h0,"H1":h1,"kurvenrichtung":kurvenrichtung,"Tefkurve": tefkurve,"Kurvenwinkel": kurve_winkel,"X_angetrieben":x,"Y_angetrieben":y})
        for gerade in geraden:
            anweisungen = 0
            voerder_anweisung = 0
            geraden_anweisung = 0
            eintrag = {"Id": gerade["Id"]}
            for foerderer in angetriebene_kurve:
                if foerderer["Id"] in gerade["NachbarIds"]:
                    if voerder_anweisung == 0:
                        eintrag["X_angetrieben"] = foerderer.get("X_angetrieben")
                        eintrag["Y_angetrieben"] = foerderer.get("Y_angetrieben")
                        eintrag["vario_hoehe_0"] = foerderer.get("H0")
                        eintrag["vario_hoehe_1"] = foerderer.get("H1")
                        eintrag["Kurvenrichtung"] = foerderer.get("kurvenrichtung")
                        eintrag["Tefkurve"] = foerderer.get("Tefkurve")
                        eintrag["Kurvenwinkel"] = foerderer.get("Kurvenwinkel")
                        voerder_anweisung = 1
                    elif voerder_anweisung ==1:
                        eintrag["X_angetrieben_1"] = foerderer.get("X_angetrieben")
                        eintrag["Y_angetrieben_1"] = foerderer.get("Y_angetrieben")
                        eintrag["vario_hoehe_0_1"] = foerderer.get("H0")
                        eintrag["vario_hoehe_1_1"] = foerderer.get("H1")
                        eintrag["Kurvenrichtung_1"] = foerderer.get("kurvenrichtung")
                        eintrag["Tefkurve_1"] = foerderer.get("Tefkurve")
                        eintrag["Kurvenwinkel_1"] = foerderer.get("Kurvenwinkel")

            for kreis in kreisel:
                if kreis["Id"] in gerade["NachbarIds"]:
                    if anweisungen == 0:
                        eintrag["Drehung0"] = kreis.get("drehung")
                        eintrag["Hoehe0"]  = kreis.get("höhe")
                        eintrag["x0"] = kreis.get("x")
                        eintrag["y0"] = kreis.get("y")
                        eintrag["rotation0"] = kreis.get("rotation")
                        eintrag["abstand0"] = kreis.get("abstand")
                        anweisungen = 1
                    elif anweisungen == 1:
                        eintrag["Drehung1"] = kreis.get("drehung")
                        eintrag["Hoehe1"]  = kreis.get("höhe")
                        eintrag["x1"] = kreis.get("x")
                        eintrag["y1"] = kreis.get("y")
                        eintrag["rotation1"] = kreis.get("rotation")
                        eintrag["abstand1"] = kreis.get("abstand")
                        break
            if(gerade.get("Winkel") != None ):
                for vario_gerade in geraden:

                    if vario_gerade["Id"] in gerade["NachbarIds"]  and vario_gerade.get("Winkel") != None:
                        if geraden_anweisung == 0:
                            eintrag["X_foerderer"] = vario_gerade.get("X_foerderer")
                            eintrag["Y_foerderer"] = vario_gerade.get("Y_foerderer")
                            eintrag["Winkel"] = vario_gerade.get("Winkel")
                            eintrag["h0"] = vario_gerade.get("h0")
                            eintrag["h1"] = vario_gerade.get("h1")
                            eintrag["Foerderrichtung"] = vario_gerade.get("Foerderrichtung")
                            geraden_anweisung =1
                        elif geraden_anweisung == 1:
                            eintrag["X_foerderer_2"] = vario_gerade.get("X_foerderer")
                            eintrag["Y_foerderer_2"] = vario_gerade.get("Y_foerderer")
                            eintrag["Winkel_2"] = vario_gerade.get("Winkel")
                            eintrag["h0_2"] = vario_gerade.get("h0")
                            eintrag["h1_2"] = vario_gerade.get("h1")
                            eintrag["Foerderrichtung_2"] = vario_gerade.get("Foerderrichtung")
                            break
                    
            strecken_nachbarn.append(eintrag)

        return strecken_nachbarn

# --------------------------------------------------------- Hauptfunktion
def main(csv_path: Path, lib_path: Path, cfg_path: Path, allgemein_cfg_path: Path,
         output_path: Path, output_path_jason: Path, verbose=False, logger=None ):
    data_dir   = check_environment_var("PROJECT_DATA")
    # Bibliothek nur laden, wenn Datei existiert
    check_dxflibrary_path(lib_path, verbose, logger)
    parser_cfg_path = configparser.ConfigParser()
    try:
        with open(cfg_path, encoding='utf-8') as f:
            parser_cfg_path.read_file(f)
    except Exception as e:
        msg = f"Fehler beim Lesen der Config-Datei {cfg_path}: {e}"
    # Neue Ziel­zeichnung (DXF R2018)
    config =parser_cfg_path
    parser_allgemein_path = configparser.ConfigParser()
    try:
        with open(allgemein_cfg_path, encoding='utf-8') as f:
            parser_allgemein_path.read_file(f)
    except Exception as e:
        msg = f"Fehler beim Lesen der Config-Datei {cfg_path}: {e}"
    # Neue Ziel­zeichnung (DXF R2018)
    config_allgemein =parser_allgemein_path
    doc = ezdxf.new(dxfversion="R2018", setup=True)
    doc.units = units.M
    doc.header['$INSUNITS'] = 4  # Millimeter
    msp = doc.modelspace()

    blocklib_dir = data_dir / "block_libraries"
    lib_docs = dict()

    # gibt zu jeder ShapeId einer Gefällestrecke zurück, ob sich der jeweilige Kreisel im UZ oder GUZ dreht
    # rot_of_gf["shape_3ae53a7b-efb8-f66b-eadc-20b99f949ef1"] = ('UZ', 'GUZ')
    strecken_nachbarn = get_nachbar_information(csv_path)

    # Verarbeitung der Blöcke
   
    with csv_path.open(newline="", encoding="utf-8") as fh:
        reader = csv.DictReader(fh, delimiter=';')
        for row in reader:
            bezeichner = row["Bezeichnung"].strip()
            teileart = row["TeileArt"].strip()
            teileid  = row["TeileId"].strip()
            planquadrat = row["Planquadrat"]
            merkmale = parse_merkmale(row.get("Merkmale", ""))
            merkmale["bezeichner"] = bezeichner
            try:
                x, y = extract_coords(planquadrat)
               
            except Exception as e:
                msg = f"[WARN] {teileid}: {e}"
                if logger:
                    logger.warning(msg)
                else:
                    print(msg)
                continue

            # Bibliotheksdatei bestimmen
            libfile = get_libfile_cfg(teileart, allgemein_cfg_path)
            if libfile:
                lib_path = blocklib_dir / libfile
            else:
                lib_path = default_lib_path

            # Bibliothek laden (mit Cache)
            lib_doc = None
            if lib_path in lib_docs:
                lib_doc = lib_docs[lib_path]
            elif lib_path.exists():
                try:
                    lib_doc = ezdxf.readfile(lib_path)
                    lib_docs[lib_path] = lib_doc
                    if verbose:
                        print(f"[INFO] Bibliothek geladen: {lib_path}")
                except Exception as e:
                    print(f"[WARN] Fehler beim Lesen der Bibliothek '{lib_path}': {e}")
            else:
                print(f"[INFO] Keine Bibliothek gefunden unter {lib_path}. Komplexe Formen werden übersprungen.")

            # Funktions-Dispatch: handle_<teileart> (mit _ statt Leerzeichen und Punkten, alles klein)
            func_name = f'handle_{normalize_func_name(teileart)}'
            handler = globals().get(func_name)
            symbols = get_shape_cfg(teileart, cfg_path, logger=logger)
            # Mapping für Omniflo-Typen
            if func_name.startswith('handle_omniflo') or func_name.startswith('handle_tef'):
                handler = globals().get('handle_omniflo')
            if func_name.startswith("handle_ils_2_0_kreisel"):
                handler = globals().get("handle_ils_2_0_kreisel")
            if handler:
                handler(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, strecken_nachbarn, config,config_allgemein)
            else:
                msg = f"[WARN] Keine Routine für TeileArt '{teileart}'. Überspringe '{teileid}'."
                if logger:
                    logger.warning(msg)
                else:
                    print(msg)

    doc.saveas(output_path)
    if logger:
        logger.info(f"[DONE] DXF gespeichert unter: {output_path}")
    else:
        print(f"[DONE] DXF gespeichert unter: {output_path}")

def check_dxflibrary_path(lib_path, verbose, logger):
    lib_doc = None
    if lib_path.exists():
        try:
            lib_doc = ezdxf.readfile(lib_path)
            if verbose:
                logger.info(f"[INFO] Bibliothek geladen: {lib_path}") if logger else print(f"[INFO] Bibliothek geladen: {lib_path}")
        except Exception as e:
            msg = f"Fehler beim Lesen der Bibliothek '{lib_path}': {e}"
            if logger:
                logger.error(msg)
            else:
                print(msg)
            sys.exit(1)
    else:
        msg = f"[INFO] Keine Bibliothek gefunden unter {lib_path}."
        if logger:
            logger.error(msg)
        else:
            print(msg)
        sys.exit(1)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Plaziert Anlagenkomponenten aus RuleDesigner CSV.")
    parser.add_argument("-f", "--file", required=True, help="CSV-Datei (Name oder Pfad)", metavar="input.csv")
    parser.add_argument("-c", "--config", help="CFG mit einfachen Formen", metavar="shapes.cfg")
    parser.add_argument("-l", "--lib", help="DXF-Bibliothek mit Blöcken", metavar="bibliothek.dxf")
    parser.add_argument("-o", "--output", help="Ziel-DXF (Standard: PROJECT_WORK/anlage.dxf)", metavar="anlage.dxf")
    parser.add_argument("-v", "--verbose", action="store_true", help="mehr Ausgaben anzeigen")

    args = parser.parse_args()
 
    # Verzeichnisse aus Umgebungs­variablen
    log_dir    = check_environment_var("PROJECT_LOG")
    data_dir   = check_environment_var("PROJECT_DATA")
    work_dir   = check_environment_var("PROJECT_WORK")
    config_dir = check_environment_var("PROJECT_CFG")

    logger = setup_logger(log_dir, name='plant2dxf')
    logger.info("=== plant2dxf Verarbeitung gestartet ===")

    # CSV‑Pfad: nur Dateiname → im WORK‑Ordner suchen
    if os.sep not in args.file and "/" not in args.file:
        csv_path = work_dir / args.file
    else:
        csv_path = Path(args.file)

    cfg_path  = Path(args.config) if args.config else config_dir / "shapes.cfg"
    allgemein_cfg_path = config_dir / "allgemein.cfg"
    default_lib_path = Path(args.lib)    if args.lib    else data_dir   / "blocks.dxf"
    output_path = Path(args.output) if args.output else (work_dir / f"{csv_path.stem}.dxf")
    output_path_jason = Path(args.output) if args.output else (work_dir / f"{csv_path.stem}.jason")
    main(csv_path, default_lib_path, cfg_path,allgemein_cfg_path, output_path,output_path_jason, verbose=args.verbose, logger=logger)
    logger.info("=== plant2dxf Verarbeitung abgeschlossen ===")