import math
import FreeCAD
import Part
from Utils.PVPlantUtils import findObjects

if FreeCAD.GuiUp:
    import FreeCADGui
    from PySide import QtCore, QtGui, QtWidgets
    from PySide.QtCore import QT_TRANSLATE_NOOP

    import os
else:
    # \cond
    def translate(ctxt, txt):
        return txt


    def QT_TRANSLATE_NOOP(ctxt, txt):
        return txt
    # \endcond

__title__ = "PVPlant Export to DXF"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"

import PVPlantResources
from PVPlantResources import DirIcons as DirIcons

field = {"name": "", "width": 0, "heigth": 0}

def getWire(wire, nospline=False, width=.0):
    """Return a list of DXF ready points and bulges from a wire.

    It builds a list of points from the edges of a `wire`.
    If the edges are circular arcs, the "bulge" of that edge is calculated,
    for other cases, the bulge is considered zero.

    Parameters
    ----------
    wire : Part::TopoShape ('Wire')
        A shape representing a wire.

    nospline : bool, optional
        It defaults to `False`.
        If it is `True`, the edges of the wire are not considered as
        being one of `'BSplineCurve'`, `'BezierCurve'`, or `'Ellipse'`,
        and a simple point is added to the list.
        Otherwise, `getSplineSegs(edge)` is used to extract
        the points and add them to the list.

    Returns
    -------
    list of tuples
        It returns a list of tuples ``[(...), (...), ...]``
        where each tuple indicates a point with additional information
        besides the coordinates.
        Two types of tuples may be returned.

    [(float, float, float, None, None, float), ...]
        When `lw` is `True` (`'lwpolyline'`)
        the first three values represent the coordinates of the point,
        the next two are `None`, and the last value is the bulge.

    [((float, float, float), None, [None, None], float), ...]
        When `lw` is `False` (`'polyline'`)
        the first element is a tuple of three values that indicate
        the coordinates of the point, the next element is `None`,
        the next element is a list of two `None` values,
        and the last element is the value of the bulge.

    See also
    --------
    calcBulge
    """

    import DraftGeomUtils
    import math

    offset = FreeCAD.ActiveDocument.Site.Origin

    def fmt(vec, b=0.0):
        return ((vec.x + offset.x) * 0.001, (vec.y + offset.y) * 0.001, width, width, b)

    points = []
    edges = Part.__sortEdges__(wire.Edges)
    for edge in edges:
        v1 = edge.Vertexes[0].Point
        if DraftGeomUtils.geomType(edge) == "Circle":
            # polyline bulge -> negative makes the arc go clockwise
            angle = edge.LastParameter - edge.FirstParameter
            bul = math.tan(angle / 4)
            if edge.Curve.Axis.dot(FreeCAD.Vector(0, 0, 1)) < 0:
                bul = -bul
            points.append(fmt(v1, bul))
        elif (DraftGeomUtils.geomType(edge) in ["BSplineCurve",
                                                "BezierCurve",
                                                "Ellipse"]) and (not nospline):
            spline = getSplineSegs(edge)
            spline.pop()
            for p in spline:
                points.append(fmt(p))
        else:
            points.append(fmt(v1))

    v = edges[-1].Vertexes[-1].Point
    points.append(fmt(v))

    return points


def getArcData(edge):
    """Return center, radius, start, and end angles of a circle-based edge.

    Parameters
    ----------
    edge : Part::TopoShape ('Edge')
        An edge representing a circular arc, either open or closed.

    Returns
    -------
    (tuple, float, float, float)
        It returns a tuple of four values; the first value is a tuple
        with the coordinates of the center `(x, y, z)`;
        the other three represent the magnitude of the radius,
        and the start and end angles in degrees that define the arc.

    (tuple, float, 0, 0)
        If the number of vertices in the `edge` is only one, only the center
        point exists, so it's a full circumference; in this case, both
        angles are zero.
    """
    ce = edge.Curve.Center
    radius = edge.Curve.Radius
    if len(edge.Vertexes) == 1:
        # closed circle
        return DraftVecUtils.tup(ce), radius, 0, 0
    else:
        # new method: recalculate ourselves as we cannot trust edge.Curve.Axis
        # or XAxis
        p1 = edge.Vertexes[0].Point
        p2 = edge.Vertexes[-1].Point
        v1 = p1.sub(ce)
        v2 = p2.sub(ce)
        # print(v1.cross(v2))
        # print(edge.Curve.Axis)
        # print(p1)
        # print(p2)
        # we can use Z check since arcs getting here will ALWAYS be in XY plane
        # Z can be 0 if the arc is 180 deg
        # if (v1.cross(v2).z >= 0) or (edge.Curve.Axis.z > 0):
        # Calculates the angles of the first and last points
        # in the circular arc, with respect to the global X axis.
        if edge.Curve.Axis.z > 0:
            # clockwise
            ang1 = -DraftVecUtils.angle(v1)
            ang2 = -DraftVecUtils.angle(v2)
        else:
            # counterclockwise
            ang2 = -DraftVecUtils.angle(v1)
            ang1 = -DraftVecUtils.angle(v2)

        # obsolete method - fails a lot
        # if round(edge.Curve.Axis.dot(Vector(0, 0, 1))) == 1:
        #    ang1, ang2 = edge.ParameterRange
        # else:
        #    ang2, ang1 = edge.ParameterRange
        # if edge.Curve.XAxis != Vector(1, 0, 0):
        #    ang1 -= DraftVecUtils.angle(edge.Curve.XAxis)
        #    ang2 -= DraftVecUtils.angle(edge.Curve.XAxis)

        return (DraftVecUtils.tup(ce), radius,
                math.degrees(ang1), math.degrees(ang2))


# =================================================================================
# CLASE PARA EXPORTACIÓN DXF
# =================================================================================

class exportDXF:

    def __init__(self, filename):

        self.doc = None
        self.msp = None
        self.filename = filename
        self.paper_layouts = []

        '''
        doc.linetypes.add("GRENZE2",
                          # linetype definition in acad.lin:
                          # A,.25,-.1,[BOX,ltypeshp.shx,x=-.1,s=.1],-.1,1
                          # replacing BOX by shape index 132 (got index from an AutoCAD file),
                          # ezdxf can't get shape index from ltypeshp.shx
                          pattern="A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
                          description="Grenze eckig ----[]-----[]----[]-----[]----[]--",
                          length=1.45,  # required for complex line types
        })
        
        doc.linetypes.add("GRENZE2",
                        # linetype definition in acad.lin:
                        # A,.25,-.1,[BOX,ltypeshp.shx,x=-.1,s=.1],-.1,1
                        # replacing BOX by shape index 132 (got index from an AutoCAD file),
                        # ezdxf can't get shape index from ltypeshp.shx
                        pattern = "A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
                        description = "Límite1 ----0-----0----0-----0----0-----0--",
                        length = 1.45,  # required for complex line types
         })
         '''

    def createFile(self, version='R2018'):
        import ezdxf
        from ezdxf.tools.standards import linetypes

        # 1. Create a new document
        self.doc = ezdxf.new(version)

        # 2. Setup document:
        self.doc.header["$INSUNITS"] = 6
        self.doc.header['$MEASUREMENT'] = 1
        self.doc.header['$LUNITS'] = 2
        self.doc.header['$AUNITS'] = 0

        # 3. Add new entities to the modelspace:
        self.msp = self.doc.modelspace()

        for name, desc, pattern in linetypes():
            if name not in self.doc.linetypes:
                self.doc.linetypes.add(name=name,
                                       pattern=pattern,
                                       description=desc,
                                       )


        self.doc.linetypes.add(name="FENCELINE1",
                               pattern="A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
                               description="Límite1 ----0-----0----0-----0----0-----0--",
                               length=1.45)  # required for complex line types

    def save(self):
        from os.path import exists
        file_exists = exists(self.filename)
        self.doc.saveas(self.filename)
        self.doc.save()

    def createLayer(self, layerName="newLayer", layerColor=(0,0,0), layerLineType='CONTINUOUS'):
        layer = self.doc.layers.add(name=layerName,  linetype=layerLineType)
        layer.rgb = layerColor
        return layer

    def createBlock(self, blockName='newBlock'):
        # Create a block
        block = self.doc.blocks.new(name=blockName)
        return block

    def insertBlock(self, name, point=(0.0, 0.0), rotation=0.0, xscale=0.0, yscale=0.0):
        if name == "":
            return None

        return self.msp.add_blockref(name, point, dxfattribs={
            'xscale': xscale,
            'yscale': yscale,
            'rotation': rotation
        })

    def createPolyline(self, wire, layer=""):
        try:
            data = getWire(wire.Shape)
            lwp = self.msp.add_lwpolyline(data)
            if layer:
                lwp.dxf.layer = layer
            return lwp
        except Exception as e:
            print("Error creating polyline:", e)
            return None

    def createHatch(self, wire, pattern="SOLID", scale=1.0, angle=0, layer=None):
        """Crea un sombreado (hatch) para un área"""
        try:
            # Obtener los puntos en metros
            points = [(x, y) for (x, y, *_) in wire]

            # Crear el hatch
            hatch = self.msp.add_hatch(color=7, dxfattribs={'layer': layer})

            if pattern == "SOLID":
                # Sombreado sólido
                hatch.set_solid_fill()
            else:
                # Patrón de sombreado
                hatch.set_pattern_fill(name=pattern, scale=scale, angle=angle)

            # Añadir el contorno
            hatch.paths.add_polyline_path(points, is_closed=True)
            return hatch
        except Exception as e:
            print("Error creating hatch:", e)
            return None

    def export_feature_image(self, feature, layer_name):
        """Exporta una imagen del GeoFeature y la añade al DXF"""
        try:
            # Añadir la imagen al DXF
            image_def = self.doc.add_image_def(feature.ImageFile, size_in_pixel=(feature.XSize, feature.YSize))
            self.msp.add_image(image_def,
                               insert=(0, 0, 0),
                               size_in_units=(feature.XSize,
                                              feature.YSize),
                               rotation=0,
                               dxfattribs={'layer': layer_name})

            print(f"Imagen exportada para {feature.Label}")

            # Eliminar el archivo temporal
            import os
            os.unlink(temp_img.name)
        except Exception as e:
            print(f"Error en exportación de imagen: {e}")

# =================================================================================
# INTERFAZ DE USUARIO
# =================================================================================
class LineTypeEditorDelegate(QtWidgets.QStyledItemDelegate):
    """Delegado que muestra el combobox personalizado solo durante la edición"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.line_styles = {
            "CONTINUOUS": QtCore.Qt.SolidLine,
            "DASHED": QtCore.Qt.DashLine,
            "DOTTED": QtCore.Qt.DotLine,
            "DASH-DOT": QtCore.Qt.DashDotLine,
            "DASH-DOT-DOT": QtCore.Qt.DashDotDotLine,
            "FENCELINE1": QtCore.Qt.CustomDashLine,
        }
        self.custom_patterns = {
            "FENCELINE1": [4, 2, 1, 2]
        }

        # Tamaño mínimo para mostrar la previsualización
        self.min_preview_width = 80

    def createEditor(self, parent, option, index):
        # SOLO se crea el editor cuando se inicia la edición
        return LineTypeComboBox(parent)

    def setEditorData(self, editor, index):
        # Establecer el valor actual en el editor
        value = index.data(QtCore.Qt.DisplayRole)
        if value:
            editor.setCurrentText(value)

    def setModelData(self, editor, model, index):
        # Guardar el valor seleccionado en el modelo
        model.setData(index, editor.currentText(), QtCore.Qt.EditRole)

    def paint0(self, painter, option, index):
        """Pintado normal de la celda (sin editor)"""
        # Configurar el fondo según selección
        if option.state & QtWidgets.QStyle.State_Selected:
            painter.fillRect(option.rect, option.palette.highlight())
        else:
            painter.fillRect(option.rect, option.palette.base())

        # Dibujar el texto del tipo de línea
        text = index.data(QtCore.Qt.DisplayRole) or ""
        painter.setPen(option.palette.text().color())
        painter.drawText(option.rect.adjusted(5, 0, -5, 0),
                         QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter,
                         text)

    def paint1(self, painter, option, index):
        # Fondo para selección
        if option.state & QtWidgets.QStyle.State_Selected:
            painter.fillRect(option.rect, option.palette.highlight())
        else:
            painter.fillRect(option.rect, option.palette.base())

        # Obtener el tipo de línea
        line_type = index.data(QtCore.Qt.DisplayRole) or "CONTINUOUS"
        line_type = line_type.upper()

        # Área de texto
        text_rect = option.rect.adjusted(5, 0, -100, 0)
        painter.setPen(option.palette.color(QtGui.QPalette.Text))
        painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, line_type)

        # Área de previsualización
        preview_rect = option.rect.adjusted(option.rect.width() - 90, 2, -5, -2)

        # Configurar el estilo de línea
        pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2)

        if line_type in self.line_styles:
            pen.setStyle(self.line_styles[line_type])

            if line_type in self.custom_patterns:
                pen.setDashPattern(self.custom_patterns[line_type])
        else:
            pen.setStyle(QtCore.Qt.SolidLine)

        painter.setPen(pen)

        # Dibujar la línea
        center_y = preview_rect.center().y()
        painter.drawLine(preview_rect.left(), center_y, preview_rect.right(), center_y)

    def paint(self, painter, option, index):
        # Fondo para selección
        if option.state & QtWidgets.QStyle.State_Selected:
            painter.fillRect(option.rect, option.palette.highlight())
        else:
            painter.fillRect(option.rect, option.palette.base())

        # Obtener el tipo de línea
        line_type = index.data(QtCore.Qt.DisplayRole) or "CONTINUOUS"
        line_type = line_type.upper()

        # Calcular el espacio disponible
        total_width = option.rect.width()

        # Área de texto (ajustable)
        text_rect = option.rect.adjusted(5, 0, -5, 0)

        # Área de previsualización (derecha, tamaño fijo pero adaptable)
        preview_width = min(self.min_preview_width, total_width - 50)  # 50px para texto mínimo
        if preview_width < 20:  # No mostrar previsualización si es demasiado pequeña
            preview_width = 0

        # Ajustar rectángulos
        if preview_width > 0:
            text_rect.setRight(option.rect.right() - preview_width - 10)
            preview_rect = option.rect.adjusted(
                option.rect.width() - preview_width,
                2,
                -5,
                -2
            )
        else:
            # Solo mostrar texto si no hay espacio para previsualización
            preview_rect = None

        # Dibujar texto del tipo de línea
        painter.setPen(option.palette.color(QtGui.QPalette.Text))
        painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, line_type)

        # Dibujar previsualización si hay espacio suficiente
        if preview_rect and preview_width > 20:
            # Configurar el estilo de línea
            pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2)
            if line_type in self.line_styles:
                pen.setStyle(self.line_styles[line_type])
                if line_type in self.custom_patterns:
                    pen.setDashPattern(self.custom_patterns[line_type])
            else:
                pen.setStyle(QtCore.Qt.SolidLine)

            painter.setPen(pen)

            # Dibujar la línea centrada verticalmente
            center_y = preview_rect.center().y()
            painter.drawLine(preview_rect.left(), center_y, preview_rect.right(), center_y)

    def sizeHint(self, option, index):
        return QtCore.QSize(200, 25)


class LineTypeDelegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.line_types = [
            {"name": "Continuous", "pen": QtCore.Qt.SolidLine},
            {"name": "Dashed", "pen": QtCore.Qt.DashLine},
            {"name": "Dotted", "pen": QtCore.Qt.DotLine},
            {"name": "Dash-Dot", "pen": QtCore.Qt.DashDotLine},
            {"name": "Dash-Dot-Dot", "pen": QtCore.Qt.DashDotDotLine}
        ]

    def paint(self, painter, option, index):
        # Dibujar el fondo
        painter.save()
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        # Configuraciones comunes
        line_color = QtGui.QColor(0, 0, 0)  # Color de la línea
        line_width = 2  # Grosor de línea

        # Dibujar el ítem base
        super().paint(painter, option, index)

        # Obtener el tipo de línea
        line_type = self.line_types[index.row()]

        # Configurar el área de dibujo
        text_rect = option.rect.adjusted(5, 0, -100, 0)
        line_rect = option.rect.adjusted(option.rect.width() - 90, 2, -5, -2)

        # Dibujar el texto
        painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, line_type["name"])

        # Dibujar la línea de muestra
        pen = QtGui.QPen(line_color, line_width, line_type["pen"])
        painter.setPen(pen)

        start_y = line_rect.center().y()
        painter.drawLine(line_rect.left(), start_y, line_rect.right(), start_y)

        painter.restore()

    def sizeHint(self, option, index):
        size = super().sizeHint(option, index)
        size.setHeight(25)  # Altura aumentada para mejor visualización
        return size


class LineTypePreviewDelegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.line_types = [
            {"name": "Continuous", "style": QtCore.Qt.SolidLine},
            {"name": "Dashed", "style": QtCore.Qt.DashLine},
            {"name": "Dotted", "style": QtCore.Qt.DotLine},
            {"name": "Custom 1 (--0--0--)", "style": QtCore.Qt.CustomDashLine, "pattern": [8, 4, 2, 4]},  # Patrón personalizado
            {"name": "Custom 2 (- - -)", "style": QtCore.Qt.CustomDashLine, "pattern": [6, 3]}  # Otro patrón
        ]

    def paint(self, painter, option, index):

        # SOLUCIÓN AL PROBLEMA: Usar un enfoque más simple sin painter.save/restore
        try:
            if index.row() >= len(self.line_types):
                return

            # Configurar el fondo para selección
            if option.state & QtGui.QStyle.State_Selected:
                painter.fillRect(option.rect, option.palette.highlight())

            # Determinar el tipo de línea
            line_data = self.line_types[index.row()]
            line_name = line_data["name"]
            line_style = line_data["style"]

            # Área de texto
            text_rect = option.rect.adjusted(5, 0, -100, 0)
            painter.setPen(option.palette.color(QtGui.QPalette.Text))
            painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, line_name)

            # Área de previsualización
            preview_rect = option.rect.adjusted(option.rect.width() - 90, 2, -5, -2)

            # Dibujar la línea de muestra
            pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2)
            pen.setStyle(line_style)
            painter.setPen(pen)

            center_y = preview_rect.center().y()
            painter.drawLine(preview_rect.left(), center_y, preview_rect.right(), center_y)

        except Exception as e:
            print("Error painting line type:", e)

        '''
        painter.save()
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        if index.row() >= len(self.line_types):
            painter.restore()
            return

        line_data = self.line_types[index.row()]
        line_name = line_data["name"]
        line_style = line_data.get("style", QtCore.Qt.SolidLine)
        dash_pattern = line_data.get("pattern", [])

        # Fondo para selección
        if option.state & QtGui.QStyle.State_Selected:
            painter.fillRect(option.rect, option.palette.highlight())

        # Áreas de dibujo
        text_rect = option.rect.adjusted(5, 0, -100, 0)
        preview_rect = option.rect.adjusted(option.rect.width() - 90, 2, -5, -2)

        # Texto
        painter.setPen(option.palette.color(QtGui.QPalette.Text))
        painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, line_name)

        # Línea personalizada
        pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2)
        pen.setStyle(line_style)

        if line_style == QtCore.Qt.CustomDashLine and dash_pattern:
            pen.setDashPattern(dash_pattern)

        painter.setPen(pen)
        painter.drawLine(preview_rect.left(), preview_rect.center().y(),
                         preview_rect.right(), preview_rect.center().y())

        painter.restore()'''

    def sizeHint(self, option, index):
        return QtCore.QSize(200, 25)


class LineTypeComboBox(QtWidgets.QComboBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._delegate = LineTypePreviewDelegate(self)
        self.setItemDelegate(self._delegate)
        self.addItems(["Continuous", "Dashed", "Dotted", "Dash-Dot", "Dash-Dot-Dot"])

    def paintEvent(self, event):
        """Pintado simplificado sin QPainter complejo"""
        painter = QtGui.QPainter(self)
        try:
            # Dibujar fondo
            option = QtWidgets.QStyleOptionComboBox()
            self.initStyleOption(option)
            self.style().drawComplexControl(QtGui.QStyle.CC_ComboBox, option, painter, self)

            # Dibujar texto
            text_rect = self.rect().adjusted(5, 0, -30, 0)
            painter.setPen(self.palette().color(QtGui.QPalette.Text))
            painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, self.currentText())
        finally:
            painter.end()  # Asegurar que el painter se cierre correctamente

layers = [
    ("Available area", QtGui.QColor(0, 204, 153), "Continuous", "1", True),
    ("Available area Names", QtGui.QColor(255, 255, 255), "Continuous", "1", True),

    ("Areas Exclusion", QtGui.QColor(255, 85, 0), "Continuous", "1", True),
    ("Areas Exclusion Offset", QtGui.QColor(255, 85, 0), "Continuous", "1", True),
    ("Areas Exclusion Name", QtGui.QColor(255, 85, 0), "Continuous", "1", True),
    ("Areas Cadastral Plot", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("Areas Cadastral Plot Name", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("Areas Offset", QtGui.QColor(255, 255, 255), "Continuous", "1", True),

    ("Cable codes LV AC inverter", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("Cable codes LV string", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("Cable codes MV System", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("CABLES LV AC inverter 120 mm2", QtGui.QColor(255, 204, 0), "Continuous", "1", True),
    ("CABLES LV AC inverter 185 mm2", QtGui.QColor(204, 153, 0), "Continuous", "1", True),
    ("CABLES LV string 4 mm2", QtGui.QColor(255, 255, 0), "Continuous", "1", True),
    ("CABLES LV string 10 mm2", QtGui.QColor(255, 255, 102), "Continuous", "1", True),
    ("CABLES MV system 300 mm2", QtGui.QColor(102, 51, 0), "Continuous", "1", True),

    ("CIVIL Fence", QtGui.QColor(102, 102, 102), "FENCELINE1", "1", True),
    ("CIVIL External Roads", QtGui.QColor(91, 91, 91), "Continuous", "1", True),
    ("CIVIL External Roads Axis", QtGui.QColor(255, 255, 192), "Dashed", "1", True),
    ("CIVIL External Roads Text", QtGui.QColor(255, 255, 192), "Continuous", "1", True),
    ("CIVIL Internal Roads", QtGui.QColor(153, 95, 76), "Continuous", "1", True),
    ("CIVIL Internal Roads Axis", QtGui.QColor(192, 192, 192), "Dashed", "1", True),
    ("CIVIL Internal Roads Text", QtGui.QColor(192, 192, 192), "Continuous", "1", True),

    ("Contour Line Legend text", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("Major contour line", QtGui.QColor(0, 0, 0), "Continuous", "1", True),
    ("Major contour value", QtGui.QColor(0, 0, 0), "Continuous", "1", True),
    ("Minor contour line", QtGui.QColor(128, 128, 128), "Continuous", "1", True),
    ("Minor contour value", QtGui.QColor(128, 128, 128), "Continuous", "1", True),
    ("Power Stations", QtGui.QColor(255, 0, 0), "Continuous", "1", True),
    ("Power Stations Names", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("ST", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("ST Names", QtGui.QColor(255, 255, 0), "Continuous", "1", True),
    ("String Inv", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("STRUC Structure 1", QtGui.QColor(0, 0, 255), "Continuous", "1", True),
    ("STRUC Structure 2", QtGui.QColor(0, 0, 204), "Continuous", "1", True),
    ("STRUC Structure 3", QtGui.QColor(0, 0, 153), "Continuous", "1", True),
    ("STRUC Structure 4", QtGui.QColor(0, 0, 128), "Continuous", "1", True),
    ("STRUC Structure 5", QtGui.QColor(0, 0, 102), "Continuous", "1", True),
    ("STRUC Structure 6", QtGui.QColor(0, 0, 76), "Continuous", "1", True),
    ("STRUC Structure 7", QtGui.QColor(0, 0, 51), "Continuous", "1", True),
    ("STRUC Structure 8", QtGui.QColor(0, 0, 25), "Continuous", "1", True),
    ("Structure Codes", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("TRENCHES Low voltage 400.0 x 1000.0 m", QtGui.QColor(128, 128, 128), "Continuous", "1", True),
    ("TRENCHES Medium voltage 400.0 x 1000.", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("TRENCHES Medium voltage 800.0 x 1000.", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
    ("TRENCHES Medium voltage 800.0 x 1500.", QtGui.QColor(255, 255, 255), "Continuous", "1", True),
]


class _PVPlantExportDXF(QtGui.QWidget):
    '''The editmode TaskPanel to select what you want to export'''

    def __init__(self):
        import os
        QtGui.QWidget.__init__(self)
        # self.form:
        self.form = FreeCADGui.PySideUic.loadUi(
            os.path.join(os.path.dirname(os.path.realpath(__file__)), "exportDXF.ui"))
        # setWindowIcon(QtGui.QIcon(os.path.join(PVPlantResources.DirIcons, "convert.svg")))
        # self.form.buttonTo.clicked.connect(self.addTo)

        self.layout = QtGui.QHBoxLayout(self)
        self.layout.setContentsMargins(4, 4, 4, 4)
        self.layout.addWidget(self.form)

        self.form.buttonAcept.clicked.connect(self.onAceptClick)
        self.form.buttonCancel.clicked.connect(self.onCancelClick)

        self.delegate = LineTypeEditorDelegate()
        self.form.tableLayers.setItemDelegateForColumn(3, self.delegate)

        from datetime import datetime
        import os
        output_dir = os.path.join(os.path.dirname(FreeCAD.ActiveDocument.FileName), "outputs", "autocad")
        os.makedirs(output_dir, exist_ok=True)
        timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
        self.filename = os.path.join(output_dir, f"{timestamp}-{FreeCAD.ActiveDocument.Name}.dxf")

        # Limpiar la tabla
        for row in range(self.form.tableLayers.rowCount() - 1, -1, -1):
            self.form.tableLayers.removeRow(row)

        # Configuración de las capas por defecto
        for row in layers:
            self.add_row(*row)

    def save_project_settings(self):
        """Guarda la configuración actual en el proyecto de FreeCAD"""
        try:
            # Guardar estado de la tabla
            table_data = []
            for row in range(self.form.tableLayers.rowCount()):
                # Estado del checkbox
                checked = self.form.tableLayers.cellWidget(row, 0).findChild(QtWidgets.QCheckBox).isChecked()

                # Nombre de capa
                name = self.form.tableLayers.item(row, 1).text()

                # Color
                color_btn = self.form.tableLayers.cellWidget(row, 2).findChild(QtWidgets.QPushButton)
                color = color_btn.color
                color_str = f"{color.red()},{color.green()},{color.blue()}"

                # Tipo de línea
                line_type = self.form.tableLayers.item(row, 3).text()

                # Grosor
                thickness_combo = self.form.tableLayers.cellWidget(row, 4)
                thickness = thickness_combo.currentText()

                table_data.append({
                    "name": name,
                    "color": color_str,
                    "line_type": line_type,
                    "thickness": thickness,
                    "checked": checked
                })

            # Crear o actualizar el objeto de configuración en el proyecto
            config_obj = None
            for obj in FreeCAD.ActiveDocument.Objects:
                if obj.Name == "DXF_Export_Config":
                    config_obj = obj
                    break

            if not config_obj:
                config_obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "DXF_Export_Config")

            # Guardar como propiedad del objeto
            config_obj.addProperty("App::PropertyString", "TableSettings", "DXF Export", "Table configuration")
            config_obj.TableSettings = json.dumps(table_data)

            print("Configuración guardada en el proyecto")
        except Exception as e:
            print("Error guardando configuración en el proyecto:", e)

    def load_project_settings(self):
        """Carga la configuración guardada desde el proyecto de FreeCAD"""
        try:
            # Buscar objeto de configuración en el proyecto
            config_obj = None
            for obj in FreeCAD.ActiveDocument.Objects:
                if obj.Name == "DXF_Export_Config":
                    config_obj = obj
                    break

            if config_obj and hasattr(config_obj, "TableSettings"):
                table_json = config_obj.TableSettings
                if table_json:
                    table_data = json.loads(table_json)
                    self.load_table_data(table_data)
                    return

            # Si no hay configuración guardada para este proyecto, cargar configuración por defecto
            self.add_default_rows()
        except Exception as e:
            print("Error cargando configuración del proyecto:", e)
            self.add_default_rows()

    def add_default_rows(self):
        """Añade filas por defecto cuando no hay configuración guardada"""
        self.add_row("Areas_Boundary", QtGui.QColor(0, 125, 125), "FENCELINE1", "1", True)
        self.add_row("Areas_Exclusion", QtGui.QColor(255, 0, 0), "DASHED", "1", True)
        self.add_row("Internal_Roads", QtGui.QColor(128, 128, 128), "CONTINUOUS", "1", True)
        self.add_row("Frames", QtGui.QColor(0, 255, 0), "CONTINUOUS", "1", True)

    def load_table_data(self, table_data):
        """Carga los datos en la tabla desde una estructura de datos"""
        for layer_data in table_data:
            # Parsear color
            color_parts = layer_data["color"].split(",")
            if len(color_parts) == 3:
                color = QtGui.QColor(int(color_parts[0]), int(color_parts[1]), int(color_parts[2]))
            else:
                color = QtGui.QColor(0, 0, 0)  # Color por defecto si hay error

            # Añadir fila
            self.add_row(
                layer_data["name"],
                color,
                layer_data["line_type"],
                layer_data["thickness"],
                layer_data["checked"]
            )

    def add_row(self, name, color, line_type, thickness, checked=True):
        row = self.form.tableLayers.rowCount()
        self.form.tableLayers.insertRow(row)
        self.form.tableLayers.setRowHeight(row, 25)

        # Checkbox
        checkbox = QtWidgets.QCheckBox()
        checkbox.setChecked(checked)
        cell_widget = QtWidgets.QWidget()
        layout = QtWidgets.QHBoxLayout(cell_widget)
        layout.addWidget(checkbox)
        layout.setAlignment(QtCore.Qt.AlignCenter)
        layout.setContentsMargins(0, 0, 0, 0)
        self.form.tableLayers.setCellWidget(row, 0, cell_widget)

        # Nombre de capa
        item = QtWidgets.QTableWidgetItem(name)
        item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
        self.form.tableLayers.setItem(row, 1, item)

        # Selector de color
        color_btn = QtWidgets.QPushButton()
        color_btn.setFixedSize(20, 20)
        color_btn.setStyleSheet(f"background-color: {color.name()}; border: 1px solid #808080; border-radius: 3px;")
        color_btn.color = color
        color_btn.clicked.connect(lambda: self.change_color(color_btn))
        cell_widget = QtWidgets.QWidget()
        layout = QtWidgets.QHBoxLayout(cell_widget)
        layout.addWidget(color_btn)
        layout.setAlignment(QtCore.Qt.AlignCenter)
        layout.setContentsMargins(0, 0, 0, 0)
        self.form.tableLayers.setCellWidget(row, 2, cell_widget)

        # Tipo de línea
        item = QtWidgets.QTableWidgetItem(line_type)
        self.form.tableLayers.setItem(row, 3, item)

        # Grosor de línea
        thickness_combo = QtWidgets.QComboBox()
        thickness_combo.addItems(["0.1", "0.2", "0.3", "0.5", "1.0", "2.0"])
        thickness_combo.setCurrentText(thickness)
        self.form.tableLayers.setCellWidget(row, 4, thickness_combo)

    def change_color(self, button):
        color = QtWidgets.QColorDialog.getColor(button.color, self, "Seleccionar color")
        if color.isValid():
            button.color = color
            button.setStyleSheet(f"background-color: {color.name()}; border: 1px solid #808080; border-radius: 3px;")

    def createLayers(self, exporter):
        for row in range(self.form.tableLayers.rowCount()):
            if self.form.tableLayers.cellWidget(row, 0).findChild(QtWidgets.QCheckBox).isChecked():
                layer_name = self.form.tableLayers.item(row, 1).text()
                color = self.form.tableLayers.cellWidget(row, 2).findChild(QtWidgets.QPushButton).color
                #line_type = self.form.tableLayers.cellWidget(row, 3).currentText()
                line_type = self.form.tableLayers.item(row, 3).text()

                exporter.createLayer(
                    layerName=layer_name,
                    layerColor=(color.red(), color.green(), color.blue()),
                    layerLineType=line_type.upper()
                )

    def writeArea(self, exporter):
        exporter.createPolyline(FreeCAD.ActiveDocument.Site.Boundary, "Available area")
        areas_types = [("Boundaries", "Available area"),
                       ("CadastralPlots", "Areas Cadastral Plot"),
                       ("Exclusions", "Areas Exclusion"),
                       ("Offsets", "Areas Offset")]
        for area_type in areas_types:
            if hasattr(FreeCAD.ActiveDocument, area_type[0]):
                area_group = FreeCAD.ActiveDocument.getObjectsByLabel(area_type[0])
                if len(area_group):
                    for area in area_group[0].Group:
                        tmp = exporter.createPolyline(area, area_type[1])
                        if area_type[0] == "Exclusions":
                            exporter.createHatch(
                                tmp,
                                pattern="ANSI37",
                                scale=0.3,
                                angle=0,
                                layer=area_type[1]
                            )
        for obj in FreeCADGui.Selection.getSelection():
            tmp = exporter.createPolyline(obj, areas_types[0][1])


    def writeFrameSetups(self, exporter):
        if not hasattr(FreeCAD.ActiveDocument, "Site"):
            return

        # 2. Postes
        for ts in FreeCAD.ActiveDocument.Site.Frames:
            for poletype in ts.PoleType:
                block = exporter.createBlock(poletype.Label)
                w = poletype.Base.Shape.Wires[0]
                center = w.BoundBox.Center
                w.Placement.Base = w.Placement.Base.sub(center)
                block.add_lwpolyline(getWire(w))

                block.add_circle((0, 0), 0.2, dxfattribs={'layer': 'Structure Posts'}) #'color': 2,
                p = math.sin(math.radians(45)) * 0.2

                block.add_line((-p, -p), (p, p), dxfattribs={'layer': 'Structure Posts'})
                block.add_line((-p, p), (p, -p), dxfattribs={'layer': 'Structure Posts'})

        # 2. Frames
        for ts in FreeCAD.ActiveDocument.Site.Frames:
            w = max(ts.Shape.SubShapes[0].SubShapes[0].SubShapes[0].Faces, key=lambda x: x.Area)
            pts = [w.BoundBox.getPoint(i) for i in range(4)]
            pts.append(FreeCAD.Vector(pts[0]))
            w = Part.makePolygon(pts)
            w.Placement.Base = w.Placement.Base.sub(w.BoundBox.Center)
            mblockname = "Trina_TSM-DEG21C-20-6XXWp Vertex"
            mblock = exporter.createBlock(mblockname)
            mblock.add_lwpolyline(getWire(w))

            rblock = exporter.createBlock(ts.Label)
            w = max(ts.Shape.SubShapes[0].SubShapes[1].SubShapes[0].Faces, key=lambda x: x.Placement.Base.z).Wires[0]
            w.Placement.Base = w.Placement.Base.sub(w.BoundBox.Center)
            rblock.add_lwpolyline(getWire(w))
            for module in ts.Shape.SubShapes[0].SubShapes[0].SubShapes:
                point = FreeCAD.Vector(module.BoundBox.Center) * 0.001
                point = point[:2]
                rblock.add_blockref(mblockname, point, dxfattribs={
                    'xscale': .0,
                    'yscale': .0,
                    'rotation': 0})
            for ind in range(int(ts.NumberPole.Value)):
                point = ts.Shape.SubShapes[1].SubShapes[0].SubShapes[ind].Placement.Base * 0.001
                point = point[:2]
                name = ts.PoleType[ts.PoleSequence[ind]].Label
                rblock.add_blockref(name, point, dxfattribs={
                    'xscale': .0,
                    'yscale': .0,
                    'rotation': 0})

    def writeFrames(self, exporter):
        objects = findObjects('Tracker')
        for frame in objects:
            if hasattr(frame, "Setup"):
                point = frame.Placement.Base * 0.001
                point = point[:2]
                exporter.insertBlock(frame.Setup.Label, point=point, rotation=frame.AngleZ)

    def writeRoads(self, exporter):
        objects = findObjects("Road")
        #rblock = exporter.createBlock("Internal_roads")
        for road in objects:
            base = exporter.createPolyline(road.Base, "Internal_Roads")
            base.dxf.const_width = road.Width.Value * 0.001

            axis = exporter.createPolyline(road.Base, "Internal_Roads_Axis")
            axis.dxf.const_width = .2

        if FreeCAD.ActiveDocument.Transport:
            for road in FreeCAD.ActiveDocument.Transport.Group:
                base = exporter.createPolyline(road, "CIVIL External Roads")
                base.dxf.const_width = road.Width

                axis = exporter.createPolyline(road, "CIVIL External Roads Axis")
                axis.dxf.const_width = .2

    def writeTrenches(self, exporter):
        objects = findObjects("Trench")
        # rblock = exporter.createBlock("Internal_roads")
        for obj in objects:
            base = exporter.createPolyline(obj.Base, "Trench")
            base.dxf.const_width = obj.Width.Value * 0.001

    def setup_layout4(self, doc):
        layout2 = doc.layouts.new("scale 1-1")
        # The default paperspace scale is 1:1
        # 1 mm printed is 1 drawing unit in paperspace
        # For most use cases this is the preferred scaling and important fact:
        # the paperspace scaling has no influence on the VIEWPORT scaling - this is
        # a total different topic, see example "viewports_in_paperspace.py"

        layout2.page_setup(size=(297, 210),
                           margins=(10, 10, 10, 10),
                           units="mm",
                           scale=(1, 1),
                           #offset=(50, 50),
                           )
        layout2.add_viewport(
            # center of viewport in paperspace units
            center=(100, 100),
            # viewport size in paperspace units
            size=(50, 50),
            # modelspace point to show in center of viewport in WCS
            view_center_point=(60, 40),
            # how much modelspace area to show in viewport in drawing units
            view_height=20,
            #status=2,
        )
        lower_left, upper_right = layout2.get_paper_limits()
        x1, y1 = lower_left
        x2, y2 = upper_right
        center = lower_left.lerp(upper_right)

        # Add DXF entities to the "Layout1" in paperspace coordinates:
        layout2.add_line((x1, center.y), (x2, center.y))  # horizontal center line
        layout2.add_line((center.x, y1), (center.x, y2))  # vertical center line
        layout2.add_circle((0, 0), radius=5)  # plot origin

    def createPaperSpaces(self, exporter):
        # Datos para el cajetín
        title_block_data = {
            'width': 190,
            'height': 50,
            'fields': [
                {'name': 'Proyecto', 'value': FreeCAD.ActiveDocument.Label},
                {'name': 'Escala', 'value': '1:100'},
                {'name': 'Fecha', 'value': QtCore.QDate.currentDate().toString("dd/MM/yyyy")},
                {'name': 'Dibujado por', 'value': os.getlogin()}
            ]
        }

        # Datos para la tabla de información
        table_data = {
            'Potencia pico': '50 MWp',
            'Número de trackers': str(len(findObjects('Tracker'))),
            'Superficie total': f"{FreeCAD.ActiveDocument.Site.Boundary.Shape.Area / 10000:.2f} ha"
        }

        # Datos para el viewport
        viewport_data = {
            'center': (150, 100),
            'size': (250, 180),
            'view_center': (0, 0),
            'view_height': 200
        }

        # Crear diferentes layouts para diferentes tamaños de papel
        exporter.createPaperSpaceLayout(
            "Layout_A4",
            (297, 210),  # A4 landscape
            title_block_data,
            table_data,
            viewport_data
        )

        exporter.createPaperSpaceLayout(
            "Layout_A3",
            (420, 297),  # A3 landscape
            title_block_data,
            table_data,
            viewport_data
        )

    def onAceptClick(self):
        exporter = exportDXF(self.filename)
        exporter.createFile()

        # Crear capas configuradas
        self.createLayers(exporter)

        # Exportar objetos
        self.writeArea(exporter)
        self.writeFrameSetups(exporter)
        self.writeFrames(exporter)
        self.writeRoads(exporter)
        self.writeTrenches(exporter)

        # Crear espacios de papel
        #self.setup_layout4(exporter.doc)
        #self.createPaperSpaces(exporter)

        if hasattr(FreeCAD.ActiveDocument, "Background"):
            # Exportar como imagen en lugar de polilínea
            exporter.export_feature_image(FreeCAD.ActiveDocument.Background, "Site_Image")

        # Guardar archivo
        exporter.save()

        # Mostrar mensaje de éxito
        QtWidgets.QMessageBox.information(
            self,
            "Exportación completada",
            f"Archivo DXF guardado en:\n{self.filename}"
        )

        self.close()

    def onCancelClick(self):
        # Cerrar el diálogo sin hacer nada
        self.close()

class CommandExportDXF:
    def GetResources(self):
        return {'Pixmap': str(os.path.join(DirIcons, "dxf.svg")),
                'Accel': "E, A",
                'MenuText': "Export to DXF",
                'ToolTip': QT_TRANSLATE_NOOP("Placement", "Export choosed layers to dxf")}

    def Activated(self):
        taskd = _PVPlantExportDXF()
        taskd.setParent(FreeCADGui.getMainWindow())
        taskd.setWindowFlags(QtCore.Qt.Dialog or QtCore.Qt.Dialog)
        taskd.setWindowModality(QtCore.Qt.WindowModal)
        taskd.show()

    def IsActive(self):
        if FreeCAD.ActiveDocument:
            return True
        else:
            return False


'''if FreeCAD.GuiUp:
    FreeCADGui.addCommand('exportDXF', _CommandExportDXF())'''
