;; ModulInsert.lsp
;; Kreisel Modul-Einfügesystem + Smart Connect
;; Version: 4.1 (mit DXFM_BLOCKS Unterstützung)
;; ============================================

;; ============================================================
;; TEIL 1: GLOBALE EINSTELLUNGEN
;; ============================================================

(setq block-pfad (getenv "DXFM_BLOCKS"))

(if (or (null block-pfad) (= block-pfad ""))
  (progn
    (setq block-pfad "C:/Users/y.wang/Documents/dxfmakros/Blocks/") ;; Fallback oder manuell
    (alert (strcat "Umgebungsvariable DXFM_BLOCKS nicht gefunden.\nNutze Standardpfad: " block-pfad))
  )
  (setq block-pfad (strcat (vl-string-right-trim "/" (vl-string-translate "\\" "/" block-pfad)) "/"))
)

(setq *block-path* block-pfad)
(princ (strcat "\n✅ Block-Pfad aktiv: " block-pfad))

;; Standardwerte initialisieren
(if (null #Kreisel_Abstand) (setq #Kreisel_Abstand 2300.0))
(if (null #Kreisel_Typ) (setq #Kreisel_Typ "PIN"))



;; ============================================
;; Haupt-Einfügebefehl (Position manuell angeben)
;; ============================================
(defun c:KreiselInsert ( / pt x y abstand typ oldLayer oldColor oldOsnap oldCmdEcho abstandStr typNum)
  
  (princ "\n=== Kreisel Modul einfügen ===\n")
  (princ "Basispunkt = linke Kreistangente (links Mitte)\n")
  
  ;; 1. Einfügepunkt abfragen
  (setq pt (getpoint "\nBasispunkt (linke Kreistangente): "))
  (if (null pt) (setq pt '(0 0 0)))
  (setq x (car pt) y (cadr pt))
  
  ;; 2. Abstand abfragen
  (princ (strcat "\nAbstand <" (rtos #Kreisel_Abstand 2 0) ">: "))
  (setq abstandStr (getstring ""))
  (if (= abstandStr "")
    (setq abstand #Kreisel_Abstand)
    (setq abstand (atof abstandStr))
  )
  (setq #Kreisel_Abstand abstand)
  
  ;; 3. Typ abfragen
  (princ "\n[1] Kreisel mit PIN")
  (princ "\n[2] Kreisel ohne PIN")
  (princ (strcat "\nBitte wählen <" (if (= #Kreisel_Typ "PIN") "1" "2") ">: "))
  (setq typNum (getstring ""))
  
  (cond
    ((= typNum "2") (setq typ "OhnePIN"))
    (t (setq typ "PIN"))
  )
  (setq #Kreisel_Typ typ)
  
  (princ (strcat "\n→ Modus: " typ))
  
  ;; Modul zeichnen
  (draw-module x y abstand typ)
  
  (princ "\n✅ Kreisel Modul erfolgreich eingefügt")
  (princ)
)


;; ============================================
;; Smart-Connect-Einfügebefehl
;; ============================================
(defun c:KreiselConnect ( / existingObj pickPoint connectionType newX newY abstand typ)
  
  (princ "\n=== Kreisel Smart Connect ===\n")
  
  ;; 1. Bestehendes Modul auswählen
  (setq existingObj (car (entsel "\nBestehendes Modul wählen: ")))
  (if (null existingObj) (exit))
  
  ;; 2. Verbindungspunkt abfragen (Benutzer-Klickposition)
  (setq pickPoint (getpoint "\nVerbindungspunkt wählen (auf Tangente klicken): "))
  (if (null pickPoint) (exit))
  
  ;; 3. Verbindungspunkt-Typ erkennen
  (setq connectionType (identify-connection-point existingObj pickPoint))
  (princ (strcat "\n→ Verbindungspunkt: " connectionType))
  
  ;; 4. Parameter des neuen Moduls abfragen
  (princ (strcat "\nAbstand <" (rtos #Kreisel_Abstand 2 0) ">: "))
  (setq abstandStr (getstring ""))
  (if (= abstandStr "")
    (setq abstand #Kreisel_Abstand)
    (setq abstand (atof abstandStr))
  )
  
  (princ "\n[1] Kreisel mit PIN")
  (princ "\n[2] Kreisel ohne PIN")
  (princ (strcat "\nBitte wählen <" (if (= #Kreisel_Typ "PIN") "1" "2") ">: "))
  (setq typNum (getstring ""))
  (cond
    ((= typNum "2") (setq typ "OhnePIN"))
    (t (setq typ "PIN"))
  )
  
  ;; 5. Basispunkt des neuen Moduls anhand der Verbindungsregeln berechnen
  (setq newBasePoint (calculate-new-basepoint existingObj pickPoint connectionType abstand))
  
  (if newBasePoint
    (progn
      (princ (strcat "\n→ Neue Basis: (" (rtos (car newBasePoint) 2 0) "," (rtos (cadr newBasePoint) 2 0) ")"))
      (draw-module (car newBasePoint) (cadr newBasePoint) abstand typ)
      (princ "\n✅ Smart Connect erfolgreich!")
    )
    (princ "\n❌ Verbindung nicht möglich!")
  )
  (princ)
)


;; ============================================
;; Verbindungspunkt-Typ erkennen
;; ============================================
(defun identify-connection-point (obj pickPoint / x y abstand)
  ;; Basispunkt des Moduls ermitteln (aus Einfügepunkt berechnen)
  (setq insertPoint (cdr (assoc 10 (entget obj))))
  (setq x (car insertPoint) y (cadr insertPoint))
  
  ;; Abstand ermitteln (sollte aus Modulattributen gelesen werden, vorerst globale Variable)
  (setq abstand #Kreisel_Abstand)
  
  (setq pickX (car pickPoint) pickY (cadr pickPoint))
  (setq tolerance 50.0)  ;; Toleranz 50mm
  
  ;; Verbindungspunkte pruefen
  (cond
    ;; T_O (Mittelpunkt obere Tangente)
    ((and (<= (abs (- pickX (+ x 400.0 (/ abstand 2.0)))) tolerance)
          (<= (abs (- pickY (+ y 400.0))) tolerance))
     (setq type "T_O"))

    ;; T_U (Mittelpunkt untere Tangente)
    ((and (<= (abs (- pickX (+ x 400.0 (/ abstand 2.0)))) tolerance)
          (<= (abs (- pickY (- y 400.0))) tolerance))
     (setq type "T_U"))

    ;; M_O (Linker Kreis, oberer Rand)
    ((and (<= (abs (- pickX (+ x 400.0))) tolerance)
          (<= (abs (- pickY (+ y 400.0))) tolerance))
     (setq type "M_O"))

    ;; M_U (Linker Kreis, unterer Rand)
    ((and (<= (abs (- pickX (+ x 400.0))) tolerance)
          (<= (abs (- pickY (- y 400.0))) tolerance))
     (setq type "M_U"))

    ;; M_L (Linker Kreis, linker Rand)
    ((and (<= (abs (- pickX x)) tolerance)
          (<= (abs (- pickY y)) tolerance))
     (setq type "M_L"))

    ;; S_O (Rechter Kreis, oberer Rand)
    ((and (<= (abs (- pickX (+ x 400.0 abstand))) tolerance)
          (<= (abs (- pickY (+ y 400.0))) tolerance))
     (setq type "S_O"))

    ;; S_U (Rechter Kreis, unterer Rand)
    ((and (<= (abs (- pickX (+ x 400.0 abstand))) tolerance)
          (<= (abs (- pickY (- y 400.0))) tolerance))
     (setq type "S_U"))

    ;; S_R (Rechter Kreis, rechter Rand)
    ((and (<= (abs (- pickX (+ x 800.0 abstand))) tolerance)
          (<= (abs (- pickY y)) tolerance))
     (setq type "S_R"))

    (t (setq type "UNKNOWN"))
  )
  type
)


;; ============================================
;; Basispunkt des neuen Moduls berechnen
;; ============================================
(defun calculate-new-basepoint (obj pickPoint connectionType abstand / x y newX newY)
  
  (setq insertPoint (cdr (assoc 10 (entget obj))))
  (setq x (car insertPoint) y (cadr insertPoint))
  
  (cond
    ;; T_O / T_U / M_O / S_O / M_U / S_U → Verbindung nach oben oder unten, neues Modul mit S_R
    ((member connectionType '("T_O" "T_U" "M_O" "S_O" "M_U" "S_U"))
     (princ "\n→ Richtung: nach oben/unten")
     ;; Bestimmen ob nach oben oder unten
     (if (> (cadr pickPoint) y)
       (setq newX x newY (+ y 800.0 abstand))  ;; nach oben
       (setq newX x newY (- y 800.0 abstand))  ;; nach unten
     )
    )
    
    ;; M_L → Verbindung nach links, neues Modul mit S_R
    ((= connectionType "M_L")
     (princ "\n→ Richtung: nach links")
     (setq newX (- x 800.0 abstand) newY y)
    )
    
    ;; S_R → Verbindung nach rechts, neues Modul mit M_L
    ((= connectionType "S_R")
     (princ "\n→ Richtung: nach rechts")
     (setq newX (+ x 800.0 abstand) newY y)
    )
    
    (t (setq newX nil newY nil))
  )
  
  (if (and newX newY) (list newX newY) nil)
)


;; ============================================
;; Komplettes Modul zeichnen
;; ============================================
(defun draw-module (x y abstand typ / oldLayer oldColor oldOsnap oldCmdEcho)
  
  ;; Fang speichern und deaktivieren
  (setq oldOsnap (getvar "OSMODE"))
  (setq oldCmdEcho (getvar "CMDECHO"))
  (setvar "OSMODE" 0)
  (setvar "CMDECHO" 0)
  
  (setq oldLayer (getvar "CLAYER"))
  (setq oldColor (getvar "CECOLOR"))
  
  ;; Linken Kreisblock einfügen
  (command "_.INSERT" (strcat block-pfad "AN8.dwg") (list (+ x 400.0) y) 1 1 0)

  ;; Rechten Kreisblock einfügen
  (command "_.INSERT" (strcat block-pfad "SP8.dwg") (list (+ x 400.0 abstand) y) 1 1 0)
  
  ;; Tangenten zeichnen
  (if (not (tblsearch "LAYER" "S_LP"))
    (command "_.LAYER" "_NEW" "S_LP" "_C" "7" "" "")
  )
  (setvar "CLAYER" "S_LP")
  
  (if (= typ "PIN")
    (setvar "CECOLOR" "5")
    (setvar "CECOLOR" "1")
  )
  
  ;; Obere Tangente
  (command "_.LINE" 
    (list (+ x 400.0) (+ y 400.0))
    (list (+ x 400.0 abstand) (+ y 400.0))
    "")
  
  ;; Untere Tangente
  (command "_.LINE"
    (list (+ x 400.0) (- y 400.0))
    (list (+ x 400.0 abstand) (- y 400.0))
    "")
  
  ;; PIN-Linien zeichnen
  (if (= typ "PIN")
    (progn
      (if (not (tblsearch "LAYER" "pinbereich"))
        (command "_.LAYER" "_NEW" "pinbereich" "_C" "3" "_LT" "2punkt" "")
      )
      (setvar "CLAYER" "pinbereich")
      (setvar "CECOLOR" "3")
      
      ;; Obere PIN-Linie
      (command "_.LINE"
        (list (+ x 800.0) (+ y 300.0))
        (list (+ x abstand) (+ y 300.0))
        "")
      
      ;; Untere PIN-Linie
      (command "_.LINE"
        (list (+ x 800.0) (- y 300.0))
        (list (+ x abstand) (- y 300.0))
        "")
    )
  )
  
  ;; Einstellungen wiederherstellen
  (setvar "CLAYER" oldLayer)
  (setvar "CECOLOR" oldColor)
  (setvar "OSMODE" oldOsnap)
  (setvar "CMDECHO" oldCmdEcho)
)


;; ============================================
;; Hilfsbefehle
;; ============================================

(defun c:KreiselQuick ()
  (setq pt (getpoint "\nBasispunkt (linke Kreistangente): "))
  (if pt
    (draw-module (car pt) (cadr pt) #Kreisel_Abstand #Kreisel_Typ)
  )
  (princ "\n✅ Schnell Einfügen fertig")
)


(defun c:KreiselParams ()
  (princ "\n=== Aktuelle Kreisel Parameter ===")
  (princ (strcat "\nAbstand: " (rtos #Kreisel_Abstand 2 0) " mm"))
  (princ (strcat "\nTyp: " #Kreisel_Typ))
  (princ "\nBasispunkt: linke Kreistangente (links Mitte)")
  (princ "\n===================================\n")
  (princ)
)


(princ "\nKreiselInsert.lsp geladen (Version 4.0 - Smart Connect)!")
(princ "\nBefehle: KreiselInsert, KreiselConnect, KreiselQuick, KreiselParams")
(princ)
