;;; ==========================================================================
;;; layer_importer.lsp - Importador de Capas desde Excel para AutoCAD
;;; Autor: arqMANES
;;; Versión: 4.09
;;; ==========================================================================
;;; Comando en AutoCAD: Generador_de_capas-arqmanes
;;;
;;; Descripción:
;;; Abre un cuadro de diálogo para seleccionar cualquier archivo de Excel (.xlsx)
;;; con formato de generador de capas. Lee la pestaña "AutoCAD",
;;; extrae las columnas de definición de capas y las crea en AutoCAD configurando:
;;;   - Nombre (Concatenación con reemplazo de espacios por guiones bajos)
;;;   - Color (ACI o True Color RGB)
;;;   - Estado de Impresión (IMPRIMIR: SÍ / NO)
;;;   - Tipo de línea (con carga automática, traducción bilingüe inglés-español
;;;     y selección dinámica de archivo .lin según las unidades del dibujo)
;;;   - Grosor de línea
;;;   - Transparencia (vía ENAME)
;;;
;;; En caso de encontrar errores en parámetros, el script continúa y muestra un
;;; listado final con la advertencia: "Modifique el archivo Excel e inténtelo otra vez".
;;; Si hay tipos de línea traducidos automáticamente, se reportan como "Advertencia".
;;; ==========================================================================

(vl-load-com)

;;; --------------------------------------------------------------------------
;;; Configuración de Depuración
;;; --------------------------------------------------------------------------
(setq *arqmanes-debug* nil) ; Cambiar a t para mostrar mensajes de depuración en consola

(defun arqmanes-log-debug (msg)
  (if *arqmanes-debug*
    (princ msg)
  )
  (princ)
)

;;; --------------------------------------------------------------------------
;;; Diccionario de Traducción Bilingüe de Tipos de Línea (Inglés <-> Español)
;;; --------------------------------------------------------------------------
;;; Mapea los nombres oficiales de tipos de línea por defecto definidos en
;;; acad.lin / acadiso.lin para AutoCAD en inglés y en español.
;;; NOTA IMPORTANTE: Para los nombres con tildes (acentos), las versiones con
;;; tilde están posicionadas PRIMERO en la lista para que la búsqueda retorne
;;; preferentemente el nombre acentuado oficial de AutoCAD en Español.
(setq linetype-translations
  '(
    ("BORDER" . "MORSE_G")
    ("BORDER2" . "MORSE_G2")
    ("BORDERX2" . "MORSE_2")
    ("CENTER" . "CENTRO")
    ("CENTER2" . "CENTRO2")
    ("CENTERX2" . "CENTROX2")
    ("DASHDOT" . "TRAZO_Y_PUNTO")
    ("DASHDOT2" . "TRAZO_Y_PUNTO2")
    ("DASHDOTX2" . "TRAZO_Y_PUNTOX2")
    ("DASHED" . "TRAZOS")
    ("DASHED2" . "TRAZOS2")
    ("DASHEDX2" . "TRAZOSX2")
    ("DIVIDE" . "MORSE_D")
    ("DIVIDE2" . "MORSE_D2")
    ("DIVIDEX2" . "MORSE_DX2")
    ("DOT" . "PUNTOS")
    ("DOT2" . "PUNTOS2")
    ("DOTX2" . "PUNTOSX2")
    ("HIDDEN" . "LÍNEAS_OCULTAS")    ; Con acento primero
    ("HIDDEN" . "LINEAS_OCULTAS")    ; Sin acento después
    ("HIDDEN2" . "LÍNEAS_OCULTAS2")
    ("HIDDEN2" . "LINEAS_OCULTAS2")
    ("HIDDENX2" . "LÍNEAS_OCULTASX2")
    ("HIDDENX2" . "LINEAS_OCULTASX2")
    ("PHANTOM" . "VALS")
    ("PHANTOM2" . "VALS2")
    ("PHANTOMX2" . "VALSX2")
    ("FENCELINE1" . "LÍMITE1")       ; Con acento primero
    ("FENCELINE1" . "LIMITE1")
    ("FENCELINE2" . "LÍMITE2")       ; Con acento primero
    ("FENCELINE2" . "LIMITE2")
    ("TRACKS" . "VÍAS")              ; Con acento primero
    ("TRACKS" . "VIAS")
    ("BATTING" . "AISLAMIENTO")
    ("HOT_WATER_SUPPLY" . "AGUA_CALIENTE")
    ("GAS_LINE" . "GAS")
    ("CONTINUOUS" . "CONTINUA")
  )
)

;;; --------------------------------------------------------------------------
;;; Utilidades de Desempaquetado de Variantes y Texto
;;; --------------------------------------------------------------------------

;; Desempaqueta de forma segura un objeto variant de ActiveX para obtener su valor LISP real
(defun unpack-variant (val)
  (if (= (type val) 'VARIANT)
    (vlax-variant-value val)
    val
  )
)

;; Convierte cualquier valor a string de forma segura (desempaqueta variants y evita "nil" de nulos)
(defun val-to-string (val)
  (setq val (unpack-variant val))
  (if (or (null val) (= val "") (= val "nil"))
    ""
    (vl-string-trim " \t\r\n" (vl-princ-to-string val))
  )
)

;; Divide un texto por un delimitador (ej. una coma)
(defun split-string (str delim / pos)
  (if (setq pos (vl-string-search delim str))
    (cons (substr str 1 pos)
          (split-string (substr str (+ pos 1 (strlen delim))) delim)
    )
    (list str)
  )
)

;; Conserva solo dígitos de un texto (para parsear RGB limpios)
(defun clean-number-string (str / i char result)
  (setq i 1
        result ""
  )
  (while (<= i (strlen str))
    (setq char (substr str i 1))
    (if (and (>= char "0") (<= char "9"))
      (setq result (strcat result char))
    )
    (setq i (1+ i))
  )
  result
)

;; Busca la traducción de un tipo de línea de forma bidireccional (Inglés <-> Español)
;; Usa un bucle 'while' que finaliza en la primera coincidencia, lo que asegura que
;; si existen términos duplicados (con y sin acento), retorne el primero (acentuado oficial).
(defun get-linetype-translation (name / result lookupName lst pair)
  (setq lookupName (strcase name))
  (setq result nil)
  (setq lst linetype-translations)
  (while (and lst (not result))
    (setq pair (car lst))
    (cond
      ((= lookupName (car pair))
       (setq result (cdr pair))
      )
      ((= lookupName (cdr pair))
       (setq result (car pair))
      )
    )
    (setq lst (cdr lst))
  )
  result
)

;; Mapea espesor en mm a su correspondiente constante/entero de AutoCAD
(defun get-lineweight (val / fval)
  (setq val (unpack-variant val))
  (cond
    ((null val) (setq fval -3))
    ((= (type val) 'INT)
     (setq fval (/ (float val) 100.0)))
    ((= (type val) 'REAL)
     (setq fval val))
    ((= (type val) 'STR)
     (setq fval (distof val)))
    (t (setq fval -3))
  )
  (if (and fval (>= fval 0.0))
    (cond
      ((<= fval 0.0) 0)
      ((<= fval 0.05) 5)
      ((<= fval 0.09) 9)
      ((<= fval 0.13) 13)
      ((<= fval 0.15) 15)
      ((<= fval 0.18) 18)
      ((<= fval 0.20) 20)
      ((<= fval 0.25) 25)
      ((<= fval 0.30) 30)
      ((<= fval 0.35) 35)
      ((<= fval 0.40) 40)
      ((<= fval 0.50) 50)
      ((<= fval 0.53) 53)
      ((<= fval 0.60) 60)
      ((<= fval 0.70) 70)
      ((<= fval 0.80) 80)
      ((<= fval 0.90) 90)
      ((<= fval 1.00) 100)
      ((<= fval 1.06) 106)
      ((<= fval 1.20) 120)
      ((<= fval 1.40) 140)
      ((<= fval 2.00) 200)
      (t 211)
    )
    -3 ; Default
  )
)

;; Obtiene el valor entero de transparencia (0 a 90)
(defun get-transparency (val / trVal)
  (setq val (unpack-variant val))
  (cond
    ((null val) (setq trVal 0))
    ((= (type val) 'INT) (setq trVal val))
    ((= (type val) 'REAL) (setq trVal (fix val)))
    ((= (type val) 'STR) (setq trVal (atoi (clean-number-string val))))
    (t (setq trVal 0))
  )
  (if (< trVal 0) (setq trVal 0))
  (if (> trVal 90) (setq trVal 90))
  trVal
)

;;; --------------------------------------------------------------------------
;;; Procesamiento y Carga de Tipos de Línea Bilingües
;;; --------------------------------------------------------------------------

;; Devuelve una lista con (primaryFile secondaryFile) según el CAD y MEASUREMENT
(defun get-default-lin-files ( / prodName)
  (setq prodName (strcase (getvar "PRODUCT")))
  (cond
    ((vl-string-search "ZWCAD" prodName)
     (if (= (getvar "MEASUREMENT") 0)
       (list "zwcad.lin" "zwcadiso.lin")
       (list "zwcadiso.lin" "zwcad.lin")
     )
    )
    ((vl-string-search "GSTARCAD" prodName)
     (if (= (getvar "MEASUREMENT") 0)
       (list "gcad.lin" "gcadiso.lin")
       (list "gcadiso.lin" "gcad.lin")
     )
    )
    ((vl-string-search "BRICSCAD" prodName)
     (if (= (getvar "MEASUREMENT") 0)
       (list "default.lin" "iso.lin")
       (list "iso.lin" "default.lin")
     )
    )
    (t
     (if (= (getvar "MEASUREMENT") 0)
       (list "acad.lin" "acadiso.lin")
       (list "acadiso.lin" "acad.lin")
     )
    )
  )
)

;; Procesa, traduce si es necesario y carga el tipo de línea en el dibujo
;; Retorna: (list status loadedLType [error/warning message])
(defun process-layer-linetype (doc ltName / lts fallback tryLoad altName mappedName linFiles primaryFile secondaryFile)
  (setq lts (vla-get-Linetypes doc))
  (setq fallback (if (tblsearch "LTYPE" "Continuous") "Continuous" "Continua"))
  
  ;; Determinar prioridad de archivos .lin según el CAD y MEASUREMENT
  (setq linFiles (get-default-lin-files))
  (setq primaryFile (car linFiles)
        secondaryFile (cadr linFiles)
  )
  
  (cond
    ;; 1. Si es tipo de línea continua
    ((or (= (strcase ltName) "CONTINUOUS") (= (strcase ltName) "CONTINUA"))
     (list 'SUCCESS (if (tblsearch "LTYPE" "Continuous") "Continuous" "Continua"))
    )
    
    ;; 2. Si ya está cargado con su nombre exacto en el dibujo
    ((tblsearch "LTYPE" ltName)
     (list 'SUCCESS ltName)
    )
    
    ;; 3. Intentar cargarlo por su nombre original
    (t
     (setq tryLoad (vl-catch-all-apply 'vla-load (list lts ltName primaryFile)))
     (if (vl-catch-all-error-p tryLoad)
       (setq tryLoad (vl-catch-all-apply 'vla-load (list lts ltName secondaryFile)))
     )
     
     (if (not (vl-catch-all-error-p tryLoad))
       ;; Se cargó con éxito el original
       (list 'SUCCESS ltName)
       
       ;; 4. Si falló la carga original, intentar la traducción
       (progn
         (setq altName (get-linetype-translation ltName))
         (if altName
           (progn
             ;; Si la traducción ya está cargada en el dibujo, se usa directamente
             (if (tblsearch "LTYPE" altName)
               (list 'WARNING altName (strcat "El tipo de línea '" ltName "' no se encontró; en su lugar se utilizó '" altName "'"))
               
               ;; Si no está cargada la traducción, intentar cargarla
               (progn
                 (setq tryLoad (vl-catch-all-apply 'vla-load (list lts altName primaryFile)))
                 (if (vl-catch-all-error-p tryLoad)
                   (setq tryLoad (vl-catch-all-apply 'vla-load (list lts altName secondaryFile)))
                 )
                 
                 (if (not (vl-catch-all-error-p tryLoad))
                   ;; Se cargó con éxito la traducción
                   (list 'WARNING altName (strcat "El tipo de línea '" ltName "' no se encontró; se cargó y aplicó su traducción '" altName "'"))
                   ;; Falló también la traducción
                   (list 'ERROR fallback (strcat "El tipo de línea '" ltName "' (y su traducción '" altName "') no se pudieron encontrar en los archivos de definición (.lin)"))
                 )
               )
             )
           )
           ;; No hay traducción disponible para este tipo de línea
           (list 'ERROR fallback (strcat "El tipo de línea '" ltName "' no se encuentra en " primaryFile " / " secondaryFile " y no posee traducción registrada"))
         )
       )
     )
    )
  )
)

;;; --------------------------------------------------------------------------
;;; Lógica de Parseo de Capas y Colores
;;; --------------------------------------------------------------------------

;; Concatenación de columnas con reemplazo de espacios por guiones bajos
(defun build-layer-name (enc term2 term3 term4 / rawName)
  (setq rawName (strcat (val-to-string enc)
                        (val-to-string term2)
                        (val-to-string term3)
                        (val-to-string term4)))
  (while (vl-string-search " " rawName)
    (setq rawName (vl-string-subst "_" " " rawName))
  )
  rawName
)

;; Analiza el valor del color y devuelve una lista: (ACI num) o (RGB r g b)
(defun parse-color (val / str rgb-list r g b valClean)
  (setq val (unpack-variant val))
  (cond
    ((null val) nil)
    ((= (type val) 'INT)
     (list 'ACI val))
    ((= (type val) 'REAL)
     (list 'ACI (fix val)))
    ((= (type val) 'STR)
     (setq str (vl-string-trim " \t\r\n" val))
     (if (vl-string-search "," str)
       (progn
         (setq rgb-list (split-string str ","))
         (if (= (length rgb-list) 3)
           (progn
             (setq r (atoi (clean-number-string (nth 0 rgb-list))))
             (setq g (atoi (clean-number-string (nth 1 rgb-list))))
             (setq b (atoi (clean-number-string (nth 2 rgb-list))))
             ;; Validación básica de rango RGB
             (if (and (>= r 0) (<= r 255) (>= g 0) (<= g 255) (>= b 0) (<= b 255))
               (list 'RGB r g b)
               nil
             )
           )
           nil
         )
       )
       ;; Si no hay comas, tratar como entero ACI
       (progn
         (setq valClean (atoi (clean-number-string str)))
         (if (and (> valClean 0) (<= valClean 255))
           (list 'ACI valClean)
           nil
         )
       )
     )
    )
    (t nil)
  )
)

;;; --------------------------------------------------------------------------
;;; Crea de forma robusta y multicad un objeto de color (AcCmColor)
;;; --------------------------------------------------------------------------
(defun create-color-object ( / acVer prodName progIDs colorObj tryObj acadObj)
  (setq acVer (substr (getvar "ACADVER") 1 2))
  (setq prodName (strcase (getvar "PRODUCT")))
  (cond
    ((vl-string-search "GSTARCAD" prodName)
     (setq progIDs (list
                     (strcat "GstarCAD.AcCmColor." acVer)
                     (strcat "GstarCAD.GcCmColor." acVer)
                     "GstarCAD.AcCmColor"
                     "GstarCAD.GcCmColor"
                   ))
    )
    ((vl-string-search "ZWCAD" prodName)
     (setq progIDs (list
                     (strcat "ZWCAD.ZcCmColor." acVer)
                     "ZWCAD.ZcCmColor"
                     "ZWCAD.AcCmColor"
                     (strcat "ZWCAD.AcCmColor." acVer)
                     "ZWCAD.ZcCmColor.25"
                     "ZWCAD.ZcCmColor.24"
                     "ZWCAD.ZcCmColor.23"
                     "ZWCAD.ZcCmColor.22"
                     "ZWCAD.ZcCmColor.21"
                     "ZWCAD.ZcCmColor.20"
                   ))
    )
    (t
     (setq progIDs (list
                     (strcat "AutoCAD.AcCmColor." acVer)
                     "AutoCAD.AcCmColor"
                     "AutoCAD.AcCmColor.25"
                     "AutoCAD.AcCmColor.24"
                     "AutoCAD.AcCmColor.23"
                     "AutoCAD.AcCmColor.22"
                     "AutoCAD.AcCmColor.21"
                     "AutoCAD.AcCmColor.20"
                   ))
    )
  )
  (setq colorObj nil)
  (setq acadObj (vlax-get-acad-object))
  (foreach pid progIDs
    (if (null colorObj)
      (progn
        ;; 1. Intentar con vlax-create-object
        (setq tryObj (vl-catch-all-apply 'vlax-create-object (list pid)))
        (if (not (vl-catch-all-error-p tryObj))
          (setq colorObj tryObj)
          (progn
            ;; 2. Intentar con vla-GetInterfaceObject
            (setq tryObj (vl-catch-all-apply 'vla-GetInterfaceObject (list acadObj pid)))
            (if (not (vl-catch-all-error-p tryObj))
              (setq colorObj tryObj)
            )
          )
        )
      )
    )
  )
  colorObj
)

;;; --------------------------------------------------------------------------
;;; Creación y Configuración de Capa Individual (Acumula Errores y Advertencias)
;;; --------------------------------------------------------------------------

(defun create-layer-from-data (doc layers row / name plot color ltype thickness transp layerObj colorObj parsedColor ltName ltResult ltStatus ltApplied lw trVal res layerEname errors warnings missingLtInfo r g b elist oldAci aciVal rgbVal entRes)
  (setq errors nil
        warnings nil
        missingLtInfo nil
  )
  ;; Nombre de la capa (columnas A, B, C, D)
  (setq name (build-layer-name (nth 0 row) (nth 1 row) (nth 2 row) (nth 3 row)))
  
  (if (and name (/= name ""))
    (progn
      ;; Crear o recuperar la capa
      (setq res (vl-catch-all-apply 'vla-add (list layers name)))
      (if (vl-catch-all-error-p res)
        (setq errors (cons (strcat "Error de AutoCAD al crear la capa (nombre inválido o caracteres no permitidos): " (vl-catch-all-error-message res)) errors))
        (progn
          (setq layerObj res)
          
          ;; 1. Imprimible (IMPRIMIR) (columna E)
          (setq plot (nth 4 row))
          (setq res (vl-catch-all-apply
                      '(lambda ()
                         (setq plotStr (val-to-string plot))
                         (if (= (strcase plotStr) "NO")
                           (vla-put-Plottable layerObj :vlax-false)
                           (vla-put-Plottable layerObj :vlax-true)
                         )
                       )))
          (if (vl-catch-all-error-p res)
            (setq errors (cons (strcat "Error al configurar estado de impresión (IMPRIMIR): " (vl-catch-all-error-message res)) errors))
          )
          
          ;; 2. Color (ACI o True Color RGB) (columna F)
          (setq color (nth 5 row))
          (if color
            (progn
              (setq parsedColor (parse-color color))
              (if (null parsedColor)
                (setq errors (cons (strcat "El color '" (val-to-string color) "' no tiene un formato válido (debe ser número 1-255 o RGB R,G,B)") errors))
                (setq res (vl-catch-all-apply
                            '(lambda ()
                               (cond
                                 ;; Color Index (ACI)
                                 ((= (car parsedColor) 'ACI)
                                  (setq cVal (cadr parsedColor))
                                  (if (and (>= cVal 1) (<= cVal 255))
                                    (vla-put-color layerObj cVal)
                                    (progn
                                      (vla-put-color layerObj 7) ; Fallback
                                      (setq errors (cons (strcat "El color ACI '" (itoa cVal) "' no es válido para AutoCAD (rango permitido 1-255)") errors))
                                    )
                                  )
                                 )
                                 ;; True Color (RGB)
                                 ((= (car parsedColor) 'RGB)
                                  (setq r (nth 1 parsedColor)
                                        g (nth 2 parsedColor)
                                        b (nth 3 parsedColor)
                                  )
                                  (setq colorObj (create-color-object))
                                  (if colorObj
                                    (progn
                                      (vla-setrgb colorObj r g b)
                                      (vla-put-truecolor layerObj colorObj)
                                      (vlax-release-object colorObj)
                                    )
                                    (progn
                                      ;; FALLBACK: Modificación directa de DXF si falla el objeto ActiveX
                                      (setq layerEname (tblobjname "layer" name))
                                      (if layerEname
                                        (progn
                                          (setq elist (entget layerEname))
                                          (setq oldAci (assoc 62 elist))
                                          (setq aciVal (if oldAci (cdr oldAci) 7))
                                          (setq elist (vl-remove-if '(lambda (x) (member (car x) '(62 420))) elist))
                                          (setq rgbVal (+ (* 65536 r) (* 256 g) b))
                                          (setq elist (append elist (list (cons 62 aciVal) (cons 420 rgbVal))))
                                          (setq entRes (vl-catch-all-apply 'entmod (list elist)))
                                          (if (vl-catch-all-error-p entRes)
                                            (setq errors (cons (strcat "No se pudo instanciar el objeto de color RGB y falló la modificación DXF: " (vl-catch-all-error-message entRes)) errors))
                                          )
                                        )
                                        (setq errors (cons "No se pudo instanciar el objeto de color RGB ni obtener ENAME para modificación DXF" errors))
                                      )
                                    )
                                  )
                                 )
                               )
                             )))
              )
              (if (vl-catch-all-error-p res)
                (setq errors (cons (strcat "Error de AutoCAD al aplicar el color: " (vl-catch-all-error-message res)) errors))
              )
            )
          )
          
          ;; 3. Tipo de Línea (columna G)
          (setq ltype (nth 6 row))
          (setq ltName (val-to-string ltype))
          (if (and ltName (/= ltName ""))
            (progn
              ;; Invocar procesador robusto bilingüe
              (setq ltResult (process-layer-linetype doc ltName))
              (setq ltStatus (car ltResult))
              (setq ltApplied (cadr ltResult))
              
              ;; Intentar aplicar el tipo de línea resultante
              (setq res (vl-catch-all-apply '(lambda () (vla-put-Linetype layerObj ltApplied))))
              (if (vl-catch-all-error-p res)
                (setq errors (cons (strcat "Error al asignar el tipo de línea '" ltApplied "': " (vl-catch-all-error-message res)) errors))
                (progn
                  ;; Si se asignó bien, clasificar estado de traducción/carga
                  (cond
                    ((= ltStatus 'WARNING)
                     (setq warnings (cons (nth 2 ltResult) warnings))
                    )
                    ((= ltStatus 'ERROR)
                     (setq missingLtInfo (list ltName (get-linetype-translation ltName) layerObj))
                    )
                  )
                )
              )
            )
          )
          
          ;; 4. Grosor de Línea (Espesor) (columna H)
          (setq thickness (nth 7 row))
          (if (and thickness (/= (val-to-string thickness) ""))
            (progn
              (setq lw (get-lineweight thickness))
              (if (= lw -3)
                (setq errors (cons (strcat "El grosor '" (val-to-string thickness) "' no es válido") errors))
                (progn
                  (setq res (vl-catch-all-apply '(lambda () (vla-put-Lineweight layerObj lw))))
                  (if (vl-catch-all-error-p res)
                    (setq errors (cons (strcat "Error al aplicar espesor/grosor: " (vl-catch-all-error-message res)) errors))
                  )
                )
              )
            )
          )
          
          ;; 5. Transparencia (columna I) - Se usa ENAME de la capa
          (setq transp (nth 8 row))
          (setq trVal (get-transparency transp))
          (if (and trVal (> trVal 0))
            (progn
              (setq layerEname (tblobjname "layer" name))
              (if layerEname
                (if (type setpropertyvalue)
                  (progn
                    (setq res (vl-catch-all-apply 'setpropertyvalue (list layerEname "Transparency" trVal)))
                    (if (vl-catch-all-error-p res)
                      (setq errors (cons (strcat "Error al aplicar transparencia: " (vl-catch-all-error-message res)) errors))
                    )
                  )
                  (progn
                    ;; Fallback para otros CADs (ZWCAD, GstarCAD, etc.)
                    (setq res (vl-catch-all-apply 'vl-cmdf (list "_.-layer" "_tr" trVal name "")))
                    (if (vl-catch-all-error-p res)
                      (setq errors (cons (strcat "Error al aplicar transparencia en ZWCAD: " (vl-catch-all-error-message res)) errors))
                    )
                  )
                )
                (setq errors (cons "No se pudo obtener el identificador ENAME para aplicar transparencia" errors))
              )
            )
          )
        )
      )
    )
    (setq errors (cons "Campos de texto nulos (no se pudo armar el nombre de la capa)" errors))
  )
  
  ;; Retorna lista: (errores advertencias missingLtInfo)
  (list (reverse errors) (reverse warnings) missingLtInfo)
)

;;; --------------------------------------------------------------------------
;;; Lectura Celda por Celda (Fallback de alta compatibilidad para otros CADs)
;;; --------------------------------------------------------------------------
(defun read-cells-one-by-one (xlSheet startRow startCol numRows numCols / xlCells dataList r c rowList cellObj cellVal val)
  (setq xlCells (vl-catch-all-apply 'vlax-get-property (list xlSheet 'Cells)))
  (setq dataList nil)
  (if (not (vl-catch-all-error-p xlCells))
    (progn
      (setq xlCells (unpack-variant xlCells))
      (setq r 0)
      (while (< r numRows)
        (setq rowList nil)
        (setq c 0)
        (while (< c numCols)
          (setq cellObj (vl-catch-all-apply 'vlax-get-property (list xlCells 'Item (+ startRow r) (+ startCol c))))
          (setq val nil)
          (if (not (vl-catch-all-error-p cellObj))
            (progn
              (setq cellObj (unpack-variant cellObj))
              (setq cellVal (vl-catch-all-apply 'vlax-get-property (list cellObj 'Value2)))
              (if (vl-catch-all-error-p cellVal)
                (setq cellVal (vl-catch-all-apply 'vlax-get-property (list cellObj 'Value)))
              )
              (if (not (vl-catch-all-error-p cellVal))
                (setq val cellVal)
              )
              (vl-catch-all-apply 'vlax-release-object (list cellObj))
            )
          )
          (setq rowList (cons val rowList))
          (setq c (1+ c))
        )
        (setq dataList (cons (reverse rowList) dataList))
        (setq r (1+ r))
      )
      (vl-catch-all-apply 'vlax-release-object (list xlCells))
    )
  )
  (reverse dataList)
)

;;; --------------------------------------------------------------------------
;;; Funciones de Utilidad para Lectura y Parseo de Archivos CSV
;;; --------------------------------------------------------------------------

;; Divide una línea de CSV usando un delimitador respetando comillas dobles
(defun split-csv-line (line delim / result pos char insideQuote len i val current)
  (setq len (strlen line)
        i 1
        insideQuote nil
        current ""
        result nil
  )
  (while (<= i len)
    (setq char (substr line i 1))
    (cond
      ((= char "\"")
       (setq insideQuote (not insideQuote))
      )
      ((and (= char delim) (not insideQuote))
       (setq result (cons current result))
       (setq current "")
      )
      (t
       (setq current (strcat current char))
      )
    )
    (setq i (1+ i))
  )
  (setq result (cons current result))
  (reverse result)
)

;; Cuenta las ocurrencias de un carácter en una cadena
(defun count-char (str char / count len i)
  (setq count 0
        len (strlen str)
        i 1
  )
  (while (<= i len)
    (if (= (substr str i 1) char)
      (setq count (1+ count))
    )
    (setq i (1+ i))
  )
  count
)

;; Detecta el delimitador de un archivo CSV basado en la primera línea
(defun detect-csv-delimiter (first-line / commaCount semiCount tabCount)
  (setq commaCount (count-char first-line ","))
  (setq semiCount (count-char first-line ";"))
  (setq tabCount (count-char first-line "\t"))
  (cond
    ((and (>= semiCount commaCount) (>= semiCount tabCount) (> semiCount 0)) ";")
    ((and (>= tabCount commaCount) (>= tabCount semiCount) (> tabCount 0)) "\t")
    (t ",")
  )
)

;; Quita la marca de orden de bytes (BOM) UTF-8 si existe
(defun strip-utf8-bom (line / len)
  (setq len (strlen line))
  (if (and (>= len 3)
           (= (ascii (substr line 1 1)) 239)
           (= (ascii (substr line 2 1)) 187)
           (= (ascii (substr line 3 1)) 191)
      )
    (substr line 4)
    line
  )
)

;; Lee un archivo CSV completo y retorna una lista de filas (listas de strings)
(defun read-csv-file (filePath / file line delimiter dataList parsedRow)
  (setq file (open filePath "r"))
  (setq dataList nil)
  (if file
    (progn
      ;; Leer primera línea y detectar delimitador
      (setq line (read-line file))
      (if line
        (progn
          (setq line (strip-utf8-bom line))
          (setq delimiter (detect-csv-delimiter line))
          (arqmanes-log-debug (strcat "\n[DEBUG] Delimitador detectado: '" delimiter "'"))
          ;; Procesar primera línea
          (setq parsedRow (split-csv-line line delimiter))
          (while (< (length parsedRow) 9)
            (setq parsedRow (append parsedRow (list "")))
          )
          (setq dataList (cons parsedRow dataList))
          
          ;; Leer las siguientes líneas
          (while (setq line (read-line file))
            (setq parsedRow (split-csv-line line delimiter))
            (while (< (length parsedRow) 9)
              (setq parsedRow (append parsedRow (list "")))
            )
            (setq dataList (cons parsedRow dataList))
          )
        )
      )
      (close file)
    )
  )
  (reverse dataList)
)

;;; --------------------------------------------------------------------------
;;; Comando Principal: Generador_de_capas-arqmanes
;;; --------------------------------------------------------------------------

(defun c:Generador_de_capas-arqmanes ( / xlFile xlApp xlBooks xlBook xlSheets xlSheet xlRange xlVal dataList row count doc layers res firstVal layerResult layerErrors layerWarnings allWarnings allOtherErrors allMissingLtypes layerName uniqueLtypes item lt userCancel userLinFile resolvedLtypes lts ltName loadResult altName remainingMissing lObj resApply ltApplied ans allLtypeErrors errMsg errIndex errItem lErrs eStr warnIndex warnItem lWarns wStr sh shName defaultLinPath foundLin acadPaths semiPos allExistingLayers startRow startCol numRows numCols tempVal isAutoCAD fileExt linFiles primaryFile secondaryFile)
  (vl-load-com)
  (princ "\n--- Gestor de Importación de Capas desde Excel/CSV v4.09 (arqMANES) ---")
  
  ;; Verificar unidades de dibujo (sistema de medida)
  (setq linFiles (get-default-lin-files))
  (setq primaryFile (car linFiles)
        secondaryFile (cadr linFiles)
  )
  (if (= (getvar "MEASUREMENT") 0)
    (arqmanes-log-debug (strcat "\n[DEBUG] Unidades del dibujo detectadas: Imperial (Pies e Pulgadas). Se priorizará '" primaryFile "'."))
    (arqmanes-log-debug (strcat "\n[DEBUG] Unidades del dibujo detectadas: Métricas (mm, m, cm). Se priorizará '" primaryFile "'."))
  )
  
  ;; Detectar motor CAD y pedir archivo correspondiente
  (setq isAutoCAD (vl-string-search "AUTOCAD" (strcase (getvar "PRODUCT"))))
  (if isAutoCAD
    (setq xlFile (getfiled "Seleccione el archivo de Excel (.xlsx) o CSV (.csv)" "" "xlsx" 4))
    (setq xlFile (getfiled "Seleccione el archivo CSV (.csv) de Capas" "" "csv" 4))
  )
  
  (if xlFile
    (progn
      (setq fileExt (strcase (vl-filename-extension xlFile)))
      (setq dataList nil)
      (if (= fileExt ".CSV")
        (progn
          (arqmanes-log-debug (strcat "\n[DEBUG] Archivo CSV detectado: " xlFile))
          (arqmanes-log-debug "\n[DEBUG] Iniciando lectura rápida de CSV...")
          (setq dataList (read-csv-file xlFile))
          (if dataList
            (arqmanes-log-debug (strcat "\n[DEBUG] Carga exitosa. Se leyeron " (itoa (length dataList)) " filas de datos desde el CSV."))
            (princ "\n[ERROR] No se pudo leer el archivo CSV.")
          )
        )
        (progn
          (if (not isAutoCAD)
            (princ "\n[ADVERTENCIA] Se recomienda exportar a CSV y abrir el CSV en este CAD para evitar lentitud o errores.\n")
          )
          (arqmanes-log-debug "\n[DEBUG] Intentando iniciar Excel.Application...")
          (setq xlApp (vl-catch-all-apply 'vlax-get-or-create-object (list "Excel.Application")))
          
          (if (vl-catch-all-error-p xlApp)
            (princ (strcat "\n[ERROR] No se pudo iniciar Excel: " (vl-catch-all-error-message xlApp)))
            (progn
              (arqmanes-log-debug "\n[DEBUG] Excel.Application iniciado correctamente.")
              (vl-catch-all-apply 'vlax-put-property (list xlApp 'Visible :vlax-false))
              (vl-catch-all-apply 'vlax-put-property (list xlApp 'DisplayAlerts :vlax-false))
              
              (arqmanes-log-debug "\n[DEBUG] Obteniendo colección Workbooks...")
              (setq xlBooks (vl-catch-all-apply 'vlax-get-property (list xlApp 'Workbooks)))
              
              (if (vl-catch-all-error-p xlBooks)
                (princ (strcat "\n[ERROR] No se obtuvo la colección Workbooks: " (vl-catch-all-error-message xlBooks)))
                (progn
                  (arqmanes-log-debug (strcat "\n[DEBUG] Abriendo archivo: " xlFile))
                  (setq xlBook (vl-catch-all-apply 'vlax-invoke-method (list xlBooks 'Open xlFile)))
                  
                  (if (vl-catch-all-error-p xlBook)
                    (princ (strcat "\n[ERROR] No se pudo abrir el archivo de Excel: " (vl-catch-all-error-message xlBook)))
                    (progn
                      (arqmanes-log-debug "\n[DEBUG] Libro abierto. Accediendo a Sheets...")
                      (setq xlSheets (vl-catch-all-apply 'vlax-get-property (list xlBook 'Sheets)))
                      
                      (if (vl-catch-all-error-p xlSheets)
                        (princ (strcat "\n[ERROR] No se obtuvo la colección de hojas: " (vl-catch-all-error-message xlSheets)))
                        (progn
                          ;; Buscar hoja "AutoCAD"
                          (setq xlSheet nil)
                          (vlax-for sh xlSheets
                            (setq shName (vlax-get-property sh 'Name))
                            (arqmanes-log-debug (strcat "\n[DEBUG] Hoja detectada en el libro: '" shName "'"))
                            (if (= (strcase shName) "AUTOCAD")
                              (setq xlSheet sh)
                            )
                          )
                          
                          (if xlSheet
                            (progn
                              (arqmanes-log-debug "\n[DEBUG] Pestaña 'AutoCAD' encontrada. Obteniendo UsedRange...")
                              (setq xlRange (vl-catch-all-apply 'vlax-get-property (list xlSheet 'UsedRange)))
                              
                              (if (vl-catch-all-error-p xlRange)
                                (princ (strcat "\n[ERROR] No se pudo obtener el rango usado (UsedRange): " (vl-catch-all-error-message xlRange)))
                                (progn
                                  (setq xlRange (unpack-variant xlRange))
                                  (arqmanes-log-debug "\n[DEBUG] Leyendo valores del rango...")
                                  ;; 1. Intentar leer con Value2
                                  (setq xlVal (vl-catch-all-apply 'vlax-get-property (list xlRange 'Value2)))
                                  
                                  ;; 2. Si falla, intentar con Value
                                  (if (vl-catch-all-error-p xlVal)
                                    (setq xlVal (vl-catch-all-apply 'vlax-get-property (list xlRange 'Value)))
                                  )
                                  
                                  ;; 3. Procesar el resultado
                                  (if (not (vl-catch-all-error-p xlVal))
                                    (progn
                                      (setq tempVal (vlax-variant-value xlVal))
                                      (if (= (type tempVal) 'SAFEARRAY)
                                        (progn
                                          (setq dataList (vlax-safearray->list tempVal))
                                          (arqmanes-log-debug (strcat "\n[DEBUG] Carga exitosa (lectura rápida). Se leyeron " (itoa (length dataList)) " filas de datos."))
                                        )
                                      )
                                    )
                                  )
                                  
                                  ;; 4. Fallback celda por celda
                                  (if (null dataList)
                                    (progn
                                      (arqmanes-log-debug "\n[DEBUG] La lectura rápida no es compatible en este CAD. Iniciando lectura celda por celda...")
                                      (setq startRow (vl-catch-all-apply 'vlax-get-property (list xlRange 'Row)))
                                      (setq startCol (vl-catch-all-apply 'vlax-get-property (list xlRange 'Column)))
                                      (setq numRows (vl-catch-all-apply 'vlax-get-property (list (vlax-get-property xlRange 'Rows) 'Count)))
                                      (setq numCols (vl-catch-all-apply 'vlax-get-property (list (vlax-get-property xlRange 'Columns) 'Count)))
                                      
                                      (if (and (not (vl-catch-all-error-p startRow))
                                               (not (vl-catch-all-error-p startCol))
                                               (not (vl-catch-all-error-p numRows))
                                               (not (vl-catch-all-error-p numCols)))
                                        (progn
                                          (setq startRow (unpack-variant startRow))
                                          (setq startCol (unpack-variant startCol))
                                          (setq numRows (unpack-variant numRows))
                                          (setq numCols (unpack-variant numCols))
                                          
                                          (setq dataList (read-cells-one-by-one xlSheet startRow startCol numRows numCols))
                                          (if dataList
                                            (arqmanes-log-debug (strcat "\n[DEBUG] Carga exitosa (lectura compatible). Se leyeron " (itoa (length dataList)) " filas de datos."))
                                            (princ "\n[ERROR] Falló la lectura celda por celda.")
                                          )
                                        )
                                        (princ "\n[ERROR] No se pudieron determinar las dimensiones del rango de datos.")
                                      )
                                    )
                                  )
                                  (vl-catch-all-apply 'vlax-release-object (list xlRange))
                                )
                              )
                              (vl-catch-all-apply 'vlax-release-object (list xlSheet))
                            )
                            (princ "\n[ERROR] No se encontró la pestaña 'AutoCAD' en el libro.")
                          )
                        )
                      )
                      (if (and xlSheets (not (vl-catch-all-error-p xlSheets)))
                        (vl-catch-all-apply 'vlax-release-object (list xlSheets))
                      )
                      (arqmanes-log-debug "\n[DEBUG] Cerrando el libro de Excel...")
                      (vl-catch-all-apply 'vlax-invoke-method (list xlBook 'Close :vlax-false))
                      (vl-catch-all-apply 'vlax-release-object (list xlBook))
                    )
                  )
                )
              )
              (if (and xlBooks (not (vl-catch-all-error-p xlBooks)))
                (vl-catch-all-apply 'vlax-release-object (list xlBooks))
              )
              (arqmanes-log-debug "\n[DEBUG] Saliendo de Excel.Application...")
              (vl-catch-all-apply 'vlax-invoke-method (list xlApp 'Quit))
              (vl-catch-all-apply 'vlax-release-object (list xlApp))
              (arqmanes-log-debug "\n[DEBUG] Excel liberado de memoria.")
            )
          )
        )
      )
      
      ;; Si falló la lectura de Excel (dataList es nil), preguntar por un CSV de respaldo
      (if (null dataList)
        (progn
          (princ "\n\n============================================================")
          (princ "\n[AVISO] No se pudo completar la lectura directa de Excel.")
          (princ "\nEsto sucede si Excel no está instalado o si hay incompatibilidad")
          (princ "\ncon ActiveX/COM en esta versión de CAD.")
          (princ "\n\nPor favor, exporte la pestaña 'AutoCAD' de su Excel como un")
          (princ "\narchivo CSV e intente cargarlo a continuación.")
          (princ "\n============================================================\n")
          
          (setq xlFile (getfiled "Seleccione el archivo CSV de Capas de respaldo" "" "csv" 4))
          (if xlFile
            (progn
              (arqmanes-log-debug (strcat "\n[DEBUG] Archivo CSV de respaldo seleccionado: " xlFile))
              (arqmanes-log-debug "\n[DEBUG] Iniciando lectura rápida de CSV...")
              (setq dataList (read-csv-file xlFile))
              (if dataList
                (arqmanes-log-debug (strcat "\n[DEBUG] Carga exitosa. Se leyeron " (itoa (length dataList)) " filas de datos desde el CSV."))
                (princ "\n[ERROR] No se pudo leer el archivo CSV de respaldo.")
              )
            )
          )
        )
      )
      
      ;; Si leímos los datos con éxito, procesamos las capas en AutoCAD
      (if dataList
        (progn
          (arqmanes-log-debug "\n[DEBUG] Iniciando creación de capas en AutoCAD...")
          (setq doc (vla-get-ActiveDocument (vlax-get-acad-object)))
          (setq layers (vla-get-Layers doc))
          
          ;; Transacción para deshacer en bloque
          (vla-StartUndoMark doc)
          
          (setq count 0)
          (setq allOtherErrors nil)
          (setq allWarnings nil)
          (setq allMissingLtypes nil)
          (setq allExistingLayers nil)
          (arqmanes-log-debug "\n[DEBUG] Procesando capas (en silencio): ")
          
          ;; Omitimos la primera fila (encabezados)
          (foreach row (cdr dataList)
            (if (and row (>= (length row) 9))
              (progn
                (setq firstVal (unpack-variant (nth 0 row)))
                (if (and firstVal 
                         (/= firstVal "") 
                         (/= (vl-princ-to-string firstVal) "ENCABEZADO"))
                  (progn
                    ;; Obtener el nombre de la capa para el reporte y validación
                    (setq layerName (build-layer-name (nth 0 row) (nth 1 row) (nth 2 row) (nth 3 row)))
                    (if (or (null layerName) (= layerName ""))
                      (setq layerName (strcat "Fila " (itoa (+ count 2))))
                    )
                    
                    (if (tblsearch "LAYER" layerName)
                      (progn
                        ;; Registrar observación de que ya existía y omitir modificación
                        (setq allExistingLayers (cons layerName allExistingLayers))
                        (setq count (1+ count))
                      )
                      (progn
                        ;; Ejecutar creación de capa y recoger resultado: (errores advertencias missingLtInfo)
                        (setq layerResult (create-layer-from-data doc layers row))
                        (setq layerErrors (car layerResult))
                        (setq layerWarnings (cadr layerResult))
                        (setq missingLtype (caddr layerResult))
                        
                        ;; Almacenar otros errores asociados
                        (if layerErrors
                          (setq allOtherErrors (cons (list layerName layerErrors) allOtherErrors))
                        )
                        ;; Almacenar advertencias asociadas
                        (if layerWarnings
                          (setq allWarnings (cons (list layerName layerWarnings) allWarnings))
                        )
                        ;; Almacenar tipo de línea faltante si lo hay
                        (if missingLtype
                          (setq allMissingLtypes (cons (list layerName (car missingLtype) (cadr missingLtype) (caddr missingLtype)) allMissingLtypes))
                        )
                        
                        (setq count (1+ count))
                      )
                    )
                    
                    ;; Imprimir indicador de progreso cada 50 capas
                    (if (= (rem count 50) 0)
                      (princ (strcat (itoa count) "... "))
                    )
                  )
                )
              )
            )
          )
          
          (princ (strcat (itoa count) "."))
          
          ;; --- BUCLE INTERACTIVO PARA TIPOS DE LÍNEA FALTANTES ---
          (if allMissingLtypes
            (progn
              (setq allMissingLtypes (reverse allMissingLtypes))
              (princ "\n\n============================================================")
              (princ (strcat "\nAVISO: Hay tipos de línea que no se encontraron en " primaryFile " / " secondaryFile ":"))
              
              ;; Listar los nombres únicos de tipos de línea faltantes
              (setq uniqueLtypes nil)
              (foreach item allMissingLtypes
                (setq lt (nth 1 item))
                (if (not (member lt uniqueLtypes))
                  (setq uniqueLtypes (cons lt uniqueLtypes))
                )
              )
              (foreach lt uniqueLtypes
                (princ (strcat "\n   - " lt))
              )
              (princ "\n============================================================")
              
              ;; Bucle de localización de archivos
              (setq userCancel nil)
              (setq defaultLinPath "")
              (setq acadPaths (getenv "ACAD"))
              (if (and acadPaths (/= acadPaths ""))
                (progn
                  (setq semiPos (vl-string-search ";" acadPaths))
                  (if semiPos
                    (setq defaultLinPath (substr acadPaths 1 semiPos))
                    (setq defaultLinPath acadPaths)
                  )
                )
              )
              (if (and defaultLinPath (/= defaultLinPath ""))
                (progn
                  (if (/= (substr defaultLinPath (strlen defaultLinPath) 1) "\\")
                    (setq defaultLinPath (strcat defaultLinPath "\\"))
                  )
                  (setq defaultLinPath (strcat defaultLinPath primaryFile))
                )
              )
              (if (or (null defaultLinPath) (= defaultLinPath ""))
                (progn
                  (setq foundLin (findfile primaryFile))
                  (if (not foundLin)
                    (setq foundLin (findfile secondaryFile))
                  )
                  (if foundLin
                    (setq defaultLinPath foundLin)
                  )
                )
              )
              (while (and allMissingLtypes (not userCancel))
                (setq userLinFile (getfiled "Localice el archivo de definición de tipos de línea (.lin)" defaultLinPath "lin" 2))
                (if userLinFile
                  (progn
                    (arqmanes-log-debug (strcat "\n[DEBUG] Procesando archivo de tipos de línea: " userLinFile))
                    (setq resolvedLtypes nil)
                    
                    ;; Intentar cargar cada tipo de línea único faltante desde el archivo indicado
                    (setq lts (vla-get-Linetypes doc))
                    (foreach ltName uniqueLtypes
                      (setq loadResult (vl-catch-all-apply 'vla-load (list lts ltName userLinFile)))
                      ;; Si falló, intentar con la traducción
                      (if (vl-catch-all-error-p loadResult)
                        (progn
                          (setq altName (get-linetype-translation ltName))
                          (if altName
                            (setq loadResult (vl-catch-all-apply 'vla-load (list lts altName userLinFile)))
                          )
                        )
                      )
                      
                      ;; Si se cargó con éxito, marcar como resuelto
                      (if (not (vl-catch-all-error-p loadResult))
                        (progn
                          (setq resolvedLtypes (cons ltName resolvedLtypes))
                          (princ (strcat "\n[OK] Tipo de línea '" ltName "' cargado correctamente desde: " userLinFile))
                        )
                      )
                    )
                    
                    ;; Si se resolvieron algunos tipos de línea, aplicarlos a sus capas correspondientes
                    (if resolvedLtypes
                      (progn
                        (setq remainingMissing nil)
                        (foreach item allMissingLtypes
                          (setq lName (nth 0 item)
                                ltName (nth 1 item)
                                altName (nth 2 item)
                                lObj (nth 3 item)
                          )
                          (if (member ltName resolvedLtypes)
                            (progn
                              ;; Aplicar el tipo de línea cargado a la capa
                              (setq ltApplied (if (tblsearch "LTYPE" ltName) ltName altName))
                              (setq resApply (vl-catch-all-apply 'vla-put-Linetype (list lObj ltApplied)))
                              (if (vl-catch-all-error-p resApply)
                                (princ (strcat "\n[ERROR] No se pudo asignar '" ltApplied "' a la capa '" lName "': " (vl-catch-all-error-message resApply)))
                                (progn
                                  (princ (strcat "\n[INFO] Capa '" lName "' configurada con tipo de línea '" ltApplied "'."))
                                  ;; Registrar advertencia de carga
                                  (setq allWarnings (cons (list lName (list (strcat "El tipo de línea '" ltName "' se cargó desde el archivo indicado por el usuario: " userLinFile))) allWarnings))
                                )
                              )
                            )
                            ;; Si no se resolvió, mantenerlo en la lista de pendientes
                            (setq remainingMissing (cons item remainingMissing))
                          )
                        )
                        (setq allMissingLtypes (reverse remainingMissing))
                      )
                    )
                    
                    ;; Actualizar uniqueLtypes para el siguiente ciclo
                    (setq uniqueLtypes nil)
                    (foreach item allMissingLtypes
                      (setq lt (nth 1 item))
                      (if (not (member lt uniqueLtypes))
                        (setq uniqueLtypes (cons lt uniqueLtypes))
                      )
                    )
                    
                    (if allMissingLtypes
                      (progn
                        (princ "\n\nAún quedan los siguientes tipos de línea por resolver:")
                        (foreach lt uniqueLtypes
                          (princ (strcat "\n   - " lt))
                        )
                        (initget "Sí No Si")
                        (setq ans (getkword "\n¿Desea localizar otro archivo de tipos de línea? [Sí/No] <Sí>: "))
                        (if (and ans (or (= ans "No") (= (strcase ans) "NO")))
                          (setq userCancel t)
                        )
                      )
                      (princ "\n¡Todos los tipos de línea faltantes fueron resueltos con éxito!")
                    )
                  )
                  ;; Si el usuario cancela la selección de archivo, terminar el bucle
                  (setq userCancel t)
                )
              )
            )
          )
          
          (vla-EndUndoMark doc)
          (princ "\n\n--- IMPORTACIÓN FINALIZADA ---")
          (princ (strcat "\nSe procesaron " (itoa count) " filas en total."))
          
          ;; Construir errores para tipos de línea que quedaron sin resolver
          (setq allLtypeErrors nil)
          (if allMissingLtypes
            (foreach item allMissingLtypes
              (setq lName (nth 0 item)
                    ltName (nth 1 item)
                    altName (nth 2 item)
              )
              (if altName
                (setq errMsg (strcat "El tipo de línea '" ltName "' (y su traducción '" altName "') no se pudieron encontrar en los archivos de definición (.lin)"))
                (setq errMsg (strcat "El tipo de línea '" ltName "' no se encuentra en " primaryFile " / " secondaryFile " y no posee traducción registrada"))
              )
              (setq allLtypeErrors (cons (list lName (list errMsg)) allLtypeErrors))
            )
          )
          
          ;; 1. Mostrar advertencias de traducción e idiomas si las hay
          (if allWarnings
            (progn
              (setq allWarnings (reverse allWarnings))
              (princ "\n\n============================================================")
              (princ "\nADVERTENCIAS (Tipos de línea adaptados automáticamente):")
              (setq warnIndex 1)
              (foreach warnItem allWarnings
                (setq lName (car warnItem))
                (setq lWarns (cadr warnItem))
                (princ (strcat "\n\nAviso " (itoa warnIndex) ", capa '" lName "':"))
                (foreach wStr lWarns
                  (princ (strcat "\n   - " wStr))
                )
                (setq warnIndex (1+ warnIndex))
              )
              (princ "\n============================================================")
            )
          )
          
          ;; 1.5. Mostrar observaciones de capas que ya existían y no se modificaron
          (if allExistingLayers
            (progn
              (setq allExistingLayers (reverse allExistingLayers))
              (princ "\n\n============================================================")
              (princ "\nOBSERVACIONES (Las siguientes capas ya existían y no fueron modificadas):")
              (foreach lName allExistingLayers
                (princ (strcat "\n   - Capa '" lName "'"))
              )
              (princ "\n============================================================")
            )
          )
          
          ;; 2. Mostrar el reporte consolidado de errores graves al final (Tipos de línea primero, otros después)
          (if (or allLtypeErrors allOtherErrors)
            (progn
              (princ "\n\n============================================================")
              (princ "\nHe encontrado errores en las siguientes capas:")
              (setq errIndex 1)
              
              ;; Primero: Errores de tipo de línea
              (if allLtypeErrors
                (progn
                  (setq allLtypeErrors (reverse allLtypeErrors))
                  (foreach errItem allLtypeErrors
                    (setq lName (car errItem))
                    (setq lErrs (cadr errItem))
                    (princ (strcat "\n\nError " (itoa errIndex) ", capa '" lName "':"))
                    (foreach eStr lErrs
                      (princ (strcat "\n   - " eStr ". Modifique el archivo Excel e inténtelo otra vez."))
                    )
                    (setq errIndex (1+ errIndex))
                  )
                )
              )
              
              ;; Segundo: Otros errores (color, espesor, etc.)
              (if allOtherErrors
                (progn
                  (setq allOtherErrors (reverse allOtherErrors))
                  (foreach errItem allOtherErrors
                    (setq lName (car errItem))
                    (setq lErrs (cadr errItem))
                    (princ (strcat "\n\nError " (itoa errIndex) ", capa '" lName "':"))
                    (foreach eStr lErrs
                      (princ (strcat "\n   - " eStr ". Modifique el archivo Excel e inténtelo otra vez."))
                    )
                    (setq errIndex (1+ errIndex))
                  )
                )
              )
              
              (princ "\n============================================================")
            )
            (princ "\n\n¡Felicidades! Importación completada con éxito sin ningún error de gravedad.")
          )
          
          (vla-Regen doc 1)
        )
        (princ "\n[ERROR] No se procesaron capas debido a fallos en la lectura de datos.")
      )
    )
    (princ "\nOperación cancelada por el usuario.")
  )
  (princ)
)

(princ "\nCarga exitosa (Versión 4.09 por arqMANES). Escriba 'Generador_de_capas-arqmanes' en la línea de comandos de AutoCAD para ejecutar la rutina.")
(princ)
