import FreeCAD
import FreeCADGui
import Part
import Draft
from xml.etree import ElementTree as ET
import ssl
import certifi
import urllib.request
import math
import utm
from collections import defaultdict
import PVPlantImportGrid as ImportElevation

scale = 1000.0


class OSMImporter:

    def __init__(self, origin):
        self.Origin = origin if origin else FreeCAD.Vector(0, 0, 0)
        self.overpass_url = "https://overpass-api.de/api/interpreter"
        self.nodes = {}
        self.ways_data = defaultdict(dict)
        self.feature_colors = {
            'building': (0.8, 0.8, 0.6),
            'highway': {
                'motorway': (1.0, 0.4, 0.4),
                'trunk': (1.0, 0.6, 0.4),
                'primary': (1.0, 0.8, 0.4),
                'secondary': (1.0, 1.0, 0.4),
                'tertiary': (0.8, 1.0, 0.4),
                'residential': (0.6, 0.6, 0.6)
            },
            'railway': (0.4, 0.4, 0.4),
            'power': {
                'line': (0.0, 0.0, 0.0),
                'tower': (0.3, 0.3, 0.3),
                'substation': (0.8, 0.0, 0.0)
            },
            'vegetation': (0.4, 0.8, 0.4),
            'water': (0.4, 0.6, 1.0)
        }
        self.ssl_context = ssl.create_default_context(cafile=certifi.where())

    def transform_from_latlon(self, coordinates):
        points = ImportElevation.getElevationFromOE(coordinates)
        pts = [FreeCAD.Vector(p.x, p.y, p.z).sub(self.Origin) for p in points]
        return pts

    def get_osm_data(self, bbox):
        query = f"""
        [out:xml][bbox:{bbox}];
        (
            way["building"];
            way["highway"];
            way["railway"];
            way["power"="line"];
            way["power"="substation"];
            way["natural"="water"];
            way["landuse"="forest"];
            node["natural"="tree"];
        );
        (._;>;);
        out body;
        """
        req = urllib.request.Request(
            self.overpass_url,
            data=query.encode('utf-8'),
            headers={'User-Agent': 'FreeCAD-OSM-Importer/1.0'},
            method='POST'
        )
        return urllib.request.urlopen(req, context=self.ssl_context, timeout=30).read()

    def create_layer(self, name):
        if not FreeCAD.ActiveDocument.getObject(name):
            return FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", name)
        return FreeCAD.ActiveDocument.getObject(name)

    def process_osm_data(self, osm_data):
        root = ET.fromstring(osm_data)

        # Almacenar nodos transformados
        coordinates = [[float(node.attrib['lat']), float(node.attrib['lon'])] for node in root.findall('node')]
        coordinates = self.transform_from_latlon(coordinates)
        for i, node in enumerate(root.findall('node')):
            self. nodes[node.attrib['id']] = coordinates[i]
        '''return
        for node in root.findall('node'):
            self.nodes[node.attrib['id']] = self.transform_from_latlon(
                float(node.attrib['lat']),
                float(node.attrib['lon'])
            )'''

        # Procesar ways
        for way in root.findall('way'):
            way_id = way.attrib['id']
            self.ways_data[way_id] = {
                'tags': {tag.attrib['k']: tag.attrib['v'] for tag in way.findall('tag')},
                'nodes': [nd.attrib['ref'] for nd in way.findall('nd')]
            }

        print("1. Create Transportations")
        FreeCADGui.updateGui()
        self.create_transportation()

        print("2. Create Buildings")
        FreeCADGui.updateGui()
        self.create_buildings()

        print("3. Create Power Infrastructure")
        FreeCADGui.updateGui()
        self.create_power_infrastructure()

        print("4. Create Vegetation")
        FreeCADGui.updateGui()
        self.create_vegetation()

        print("5. Create Water Bodies")
        FreeCADGui.updateGui()
        self.create_water_bodies()

    def create_transportation(self):
        transport_layer = self.create_layer("Transport")

        for way_id, data in self.ways_data.items():
            tags = data['tags']
            nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]

            if len(nodes) < 2:
                continue

            # Carreteras
            if 'highway' in tags:
                highway_type = tags['highway']
                width = {
                    'motorway': 10.0,
                    'trunk': 8.0,
                    'primary': 6.0,
                    'secondary': 5.0,
                    'tertiary': 4.0
                }.get(highway_type, 3.0)
                self.create_road(nodes, width, highway_type, transport_layer, tags['name'] if 'name' in tags else "")

            # Vías férreas
            if 'railway' in tags:
                self.create_railway(nodes, transport_layer, tags['name'] if 'name' in tags else "")

    def create_road(self, nodes, width, road_type, layer, name=""):
        points = [n for n in nodes]
        polyline = Draft.make_wire(points, closed=False, face=False)
        polyline.Label = f"Road_{name if (name != '') else road_type}"
        polyline.ViewObject.LineWidth = 2.0
        polyline.ViewObject.ShapeColor = self.feature_colors['highway'].get(road_type, (0.5, 0.5, 0.5))
        polyline.addProperty("App::PropertyString", "OSMType", "Metadata", "Tipo de vía").OSMType = road_type
        polyline.addProperty("App::PropertyFloat", "Width", "Metadata", "Ancho de la vía").Width = width
        layer.addObject(polyline)

    def create_railway(self, nodes, layer, name=""):
        points = [n for n in nodes]
        rail_line = Draft.make_wire(points, closed=False, face=False)
        rail_line.Label = f"{name if (name != '') else 'Railway'}"
        rail_line.ViewObject.LineWidth = 1.5
        rail_line.ViewObject.ShapeColor = self.feature_colors['railway']
        layer.addObject(rail_line)

    def create_buildings(self):
        building_layer = self.create_layer("Buildings")
        for way_id, data in self.ways_data.items():
            print(data)
            if 'building' not in data['tags']:
                continue

            tags = data['tags']
            nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]

            if len(nodes) < 3:
                continue

            # Calcular altura
            height = self.get_building_height(tags)

            # Crear polígono base
            polygon_points = [n for n in nodes]
            if polygon_points[0] != polygon_points[-1]:
                polygon_points.append(polygon_points[0])

            try:
                polygon = Part.makePolygon(polygon_points)
                face = Part.Face(polygon)
                extruded = face.extrude(FreeCAD.Vector(0, 0, height))# * scale - self.Origin )

                building = building_layer.addObject("Part::Feature", f"Building_{way_id}")
                building.Shape = extruded
                building.Label = f"Building ({height}m)"
                building.ViewObject.ShapeColor = self.feature_colors['building']

                # Metadatos
                building.addProperty("App::PropertyFloat", "Height", "Metadata", "Altura del edificio").Height = height
                if 'building:levels' in tags:
                    building.addProperty("App::PropertyInteger", "Levels", "Metadata",
                                         "Niveles del edificio").Levels = int(tags['building:levels'])

            except Exception as e:
                print(f"Error en edificio {way_id}: {str(e)}")

    @staticmethod
    def get_building_height(tags):
        # Lógica de cálculo de altura
        if 'height' in tags:
            try:
                return float(tags['height'].split()[0])
            except:
                pass
        if 'building:levels' in tags:
            try:
                return float(tags['building:levels']) * 3.0
            except:
                pass
        return 5.0  # Altura por defecto

    def create_power_infrastructure(self):
        power_layer = self.create_layer("Power_Infrastructure")

        for way_id, data in self.ways_data.items():
            tags = data['tags']
            nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]

            if 'power' in tags:
                print("\n\n")
                print(tags)
                feature_type = tags['power']
                if feature_type == 'line':
                    print("3.1. Create Power Lines")
                    FreeCADGui.updateGui()
                    self.create_power_line(
                        nodes=nodes,
                        tags=tags,
                        layer=power_layer
                    )

                elif feature_type == 'substation':
                    print("3.1. Create substations")
                    FreeCADGui.updateGui()
                    self.create_substation(
                        way_id=way_id,
                        tags=tags,
                        nodes=nodes,
                        layer=power_layer
                    )

                elif feature_type == 'tower':
                    print("3.1. Create power towers")
                    FreeCADGui.updateGui()
                    self.create_power_tower(
                        position=nodes[0] if nodes else None,
                        tags=tags,
                        layer=power_layer
                    )
                    '''self.create_power_tower_1(
                        way=way_id,
                        layer=power_layer
                    )'''

    def create_power_line(self, nodes, tags, layer):
        """Crea líneas de transmisión eléctrica con propiedades técnicas"""
        try:
            # Configuración basada en tags
            line_type = tags.get('line', 'overhead')
            voltage = self.parse_voltage(tags.get('voltage', '0'))
            cables = int(tags.get('cables', '3'))
            material = tags.get('material', 'aluminum')

            # Crear geometría
            points = [FreeCAD.Vector(*n) for n in nodes]
            if len(points) < 2:
                return

            wire = Draft.make_wire(points, closed=False, face=False)
            wire.Label = f"Power_Line_{int(voltage/1000000)}kV"

            # Propiedades visuales
            wire.ViewObject.LineWidth = 6 #'''1 + (voltage / 100000)'''
            color = self.feature_colors['power']['line']
            wire.ViewObject.ShapeColor = color

            # Propiedades técnicas
            wire.addProperty("App::PropertyFloat", "Voltage", "PowerLine", "Voltage in volts").Voltage = voltage
            wire.addProperty("App::PropertyInteger", "Cables", "PowerLine", "Number of conductors").Cables = cables
            wire.addProperty("App::PropertyString", "Material", "PowerLine", "Conductor material").Material = material
            wire.addProperty("App::PropertyString", "Type", "PowerLine", "Line type").Type = line_type

            layer.addObject(wire)

            # Añadir torres si es overhead
            '''if line_type == 'overhead':
                distance_between_towers = 150  # metros por defecto
                if 'distance_between_towers' in tags:
                    try:
                        distance_between_towers = float(tags['distance_between_towers'])
                    except:
                        pass

                self.create_power_towers_along_line(
                    points=points,
                    voltage=voltage,
                    distance=distance_between_towers,
                    layer=layer
                )'''

        except Exception as e:
            FreeCAD.Console.PrintError(f"Error creating power line: {str(e)}\n")

    def create_power_towers_along_line(self, points, voltage, distance, layer):
        """Crea torres de transmisión a lo largo de la línea"""
        total_length = 0
        previous_point = None

        for point in points:
            if previous_point is not None:
                segment_length = (point - previous_point).Length
                num_towers = int(segment_length / distance)

                if num_towers > 0:
                    step = segment_length / num_towers
                    direction = (point - previous_point).normalize()

                    for i in range(num_towers):
                        tower_pos = previous_point + (direction * (i * step))
                        self.create_power_tower(
                            position=tower_pos,
                            tags={'voltage': str(voltage)},
                            layer=layer
                        )

            previous_point = point

    def create_power_tower(self, position, tags, layer):
        """Crea una torre de transmisión individual"""
        try:
            voltage = self.parse_voltage(tags.get('voltage', '0'))

            # Dimensiones basadas en voltaje
            base_size = 2.0 + (voltage / 100000)
            height = 25.0 + (voltage / 10000)

            # Geometría de la torre
            base = Part.makeBox(base_size, base_size, 3.0,
                                FreeCAD.Vector(position.x - base_size / 2, position.y - base_size / 2, 0))
            mast = Part.makeCylinder(0.5, height, FreeCAD.Vector(position.x, position.y, 3.0))

            # Unir componentes
            tower = base.fuse(mast)
            tower_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Power_Tower")
            layer.addObject(tower_obj)
            tower_obj.Shape = tower
            tower_obj.ViewObject.ShapeColor = self.feature_colors['power']['tower']

            # Añadir propiedades
            tower_obj.addProperty("App::PropertyFloat", "Voltage", "Technical", "Design voltage").Voltage = voltage
            tower_obj.addProperty("App::PropertyFloat", "Height", "Technical", "Tower height").Height = height

            # Añadir crossarms (crucetas)
            for i in range(3):
                crossarm_height = 3.0 + (i * 5.0)
                crossarm = Part.makeBox(
                    4.0, 0.2, 0.2,
                    FreeCAD.Vector(position.x - 2.0, position.y - 0.1, crossarm_height)
                )
                tower_obj.Shape = tower_obj.Shape.fuse(crossarm)

        except Exception as e:
            FreeCAD.Console.PrintError(f"Error creating power tower: {str(e)}\n")

    def create_power_line_simple(self, nodes, layer):
        # Torres de alta tensión
        for node in nodes:
            cylinder = Part.makeCylinder(1.0, 20.0, FreeCAD.Vector(node[0], node[1], 0))# * scale - self.Origin )
            pole = FreeCAD.ActiveDocument.addObject("Part::Feature", "PowerPole")
            layer.addObject(pole)
            pole.Shape = cylinder
            pole.ViewObject.ShapeColor = self.feature_colors['power']['tower']

        # Líneas eléctricas
        points = [n for n in nodes]
        cable = Draft.make_wire(points, closed=False, face=False)
        cable.ViewObject.LineWidth = 3.0
        cable.ViewObject.ShapeColor = self.feature_colors['power']['line']
        layer.addObject(cable)

    def create_power_tower_1(self, way, layer):
        """Crea una torre eléctrica según especificaciones OSM"""
        tags = way['tags']
        nodes = [self.nodes[ref] for ref in way['nodes'] if ref in self.nodes]

        if not nodes:
            return

        try:
            # Obtener parámetros principales
            structure_type = tags.get('structure', 'lattice')
            material = tags.get('material', 'steel')
            height = float(tags.get('height', 30.0))  # Altura en metros
            voltage = self.parse_voltage(tags.get('voltage', '132000'))  # 132kV por defecto
            lines = int(tags.get('lines', '3'))
            operator = tags.get('operator', '')
            tower_name = tags.get('name', f"Tower_{way['id']}")

            # Calcular posición (usar primer nodo)
            position = FreeCAD.Vector(*nodes[0])

            # Configurar dimensiones basadas en parámetros
            base_size = self.calculate_base_size(structure_type, height)
            crossarm_length = self.calculate_crossarm_length(voltage, lines)
            color = self.get_material_color(material)

            # Crear geometría según tipo de estructura
            if structure_type == 'lattice':
                tower = self.create_lattice_tower(height, base_size, crossarm_length)
            elif structure_type == 'tubular':
                tower = self.create_tubular_tower(height, base_size, crossarm_length)
            elif structure_type == 'portal':
                tower = self.create_portal_tower(height, base_size, crossarm_length)
            else:
                tower = self.create_default_tower(height, base_size, crossarm_length)

            # Crear objeto en FreeCAD
            tower_obj = layer.addObject("Part::Feature", "Power_Tower")
            tower_obj.Shape = tower
            tower_obj.ViewObject.ShapeColor = color
            tower_obj.Placement.Base = position

            # Añadir propiedades técnicas
            tower_obj.addProperty("App::PropertyFloat", "Voltage", "Technical", "Voltaje nominal (V)").Voltage = voltage
            tower_obj.addProperty("App::PropertyFloat", "Height", "Technical", "Altura total (m)").Height = height
            tower_obj.addProperty("App::PropertyString", "StructureType", "Technical",
                                  "Tipo de estructura").StructureType = structure_type
            tower_obj.addProperty("App::PropertyString", "Material", "Technical",
                                  "Material de construcción").Material = material
            tower_obj.addProperty("App::PropertyInteger", "Lines", "Technical", "Número de circuitos").Lines = lines
            if operator:
                tower_obj.addProperty("App::PropertyString", "Operator", "General", "Operador").Operator = operator

            # Añadir cables si existen nodos de conexión
            if len(nodes) >= 2:
                connection_points = [FreeCAD.Vector(*n) for n in nodes]
                self.create_power_lines_between_towers(connection_points, voltage, layer)

        except Exception as e:
            FreeCAD.Console.PrintError(f"Error creando torre {way['id']}: {str(e)}\n")

    def create_lattice_tower(self, height, base_size, crossarm_length):
        """Crea torre de celosía tipo armazón"""
        # Base
        base = Part.makeBox(base_size, base_size, 3.0)

        # Patas principales
        leg_profile = Part.makeBox(0.5, 0.5, height)
        legs = []
        for x in [-base_size / 2, base_size / 2]:
            for y in [-base_size / 2, base_size / 2]:
                leg = leg_profile.copy()
                leg.translate(FreeCAD.Vector(x, y, 3.0))
                legs.append(leg)

        # Travesaños horizontales
        horizontal_bars = []
        for z in [10.0, height / 2, height - 5.0]:
            bar = Part.makeBox(base_size + 1.0, 0.3, 0.3, FreeCAD.Vector(-(base_size + 1) / 2, -0.15, z))
            horizontal_bars.append(bar)

        # Crucetas
        crossarms = self.create_crossarms(height, crossarm_length)

        # Unir todas las partes
        tower = base.multiFuse(legs + horizontal_bars + crossarms)
        return tower.removeSplitter()

    def create_crossarms(self, height, length):
        """Crea crucetas para líneas eléctricas"""
        crossarms = []
        positions = [
            (height - 5.0, 0),  # Superior
            (height - 15.0, 22.5),  # Media con ángulo
            (height - 25.0, -22.5)  # Inferior con ángulo
        ]

        for z, angle in positions:
            crossarm = Part.makeBox(length, 0.3, 0.3)
            crossarm.rotate(FreeCAD.Vector(0, 0, z), FreeCAD.Vector(0, 0, 1), angle)
            crossarm.translate(FreeCAD.Vector(-length / 2, -0.15, z))
            crossarms.append(crossarm)

        return crossarms

    def calculate_base_size(self, structure_type, height):
        """Calcula tamaño de base según tipo de estructura y altura"""
        base_sizes = {
            'lattice': 4.0 + (height * 0.1),
            'tubular': 3.0 + (height * 0.05),
            'portal': 6.0,
            'default': 3.0
        }
        return base_sizes.get(structure_type, base_sizes['default'])

    def calculate_crossarm_length(self, voltage, lines):
        """Calcula longitud de crucetas según voltaje y número de circuitos"""
        return (voltage / 100000) * 8.0 + (lines * 2.0)

    def get_material_color(self, material):
        """Devuelve color según material de construcción"""
        colors = {
            'steel': (0.65, 0.65, 0.7),
            'concrete': (0.8, 0.8, 0.8),
            'wood': (0.5, 0.3, 0.2),
            'aluminum': (0.9, 0.9, 0.9),
            'default': (0.5, 0.5, 0.5)
        }
        return colors.get(material.lower(), colors['default'])

    def create_power_lines_between_towers(self, points, voltage, layer):
        """Crea cables entre torres"""
        cable_thickness = 0.1 + (voltage / 500000)

        for i in range(len(points) - 1):
            start = points[i]
            end = points[i + 1]

            # Crear cable curvo (catenaria)
            cable = self.create_catenary(start, end, sag=5.0)
            cable_obj = layer.addObject("Part::Feature", f"Power_Line_{i}")
            cable_obj.Shape = cable
            cable_obj.ViewObject.LineWidth = cable_thickness * 1000  # Convertir a mm
            cable_obj.ViewObject.ShapeColor = (0.1, 0.1, 0.1)

    def create_catenary(self, start, end, sag=5.0):
        """Crea curva de catenaria para cables eléctricos"""
        mid_point = (start + end) / 2
        mid_point.z -= sag

        arc = Part.Arc(
            start,
            mid_point,
            end
        )

        return Part.Edge(arc.toShape())

    def create_substation(self, way_id, tags, nodes, layer):
        """Crea subestaciones con todos los componentes detallados"""
        try:
            # 1. Parámetros base
            voltage = self.parse_voltage(tags.get('voltage', '0'))
            substation_type = tags.get('substation', 'distribution')
            name = tags.get('name', f"Substation_{way_id}")

            if len(nodes) < 3:
                FreeCAD.Console.PrintWarning(f"Subestación {way_id} ignorada: polígono inválido\n")
                return

            # 2. Geometría base
            polygon_points = [n for n in nodes if isinstance(n, FreeCAD.Vector)]
            if polygon_points[0] != polygon_points[-1]:
                polygon_points.append(polygon_points[0])

            # 3. Base del terreno
            base_height = 0.3
            try:
                base_shape = Part.makePolygon(polygon_points)
                base_face = Part.Face(base_shape)
                base_extrude = base_face.extrude(FreeCAD.Vector(0, 0, base_height))
                base_obj = layer.addObject("Part::Feature", f"{name}_Base")
                base_obj.Shape = base_extrude
                base_obj.ViewObject.ShapeColor = (0.2, 0.2, 0.2)
            except Exception as e:
                FreeCAD.Console.PrintError(f"Error base {way_id}: {str(e)}\n")

            # 4. Cercado perimetral
            if tags.get('fence', 'no') == 'yes':
                try:
                    fence_offset = -0.8  # metros hacia adentro
                    fence_points = self.offset_polygon(polygon_points, fence_offset)
                    if len(fence_points) > 2:
                        fence_shape = Part.makePolygon(fence_points)
                        fence_face = Part.Face(fence_shape)
                        fence_extrude = fence_face.extrude(FreeCAD.Vector(0, 0, 2.8))
                        fence_obj = layer.addObject("Part::Feature", f"{name}_Fence")
                        fence_obj.Shape = fence_extrude
                        fence_obj.ViewObject.ShapeColor = (0.4, 0.4, 0.4)
                except Exception as e:
                    FreeCAD.Console.PrintWarning(f"Error cerca {way_id}: {str(e)}\n")

            # 5. Edificio principal
            if tags.get('building', 'no') == 'yes':
                try:
                    building_offset = -2.0  # metros hacia adentro
                    building_height = 4.5 + (voltage / 100000)
                    building_points = self.offset_polygon(polygon_points, building_offset)
                    if len(building_points) > 2:
                        building_shape = Part.makePolygon(building_points)
                        building_face = Part.Face(building_shape)
                        building_extrude = building_face.extrude(FreeCAD.Vector(0, 0, building_height))
                        building_obj = layer.addObject("Part::Feature", f"{name}_Building")
                        building_obj.Shape = building_extrude
                        building_obj.ViewObject.ShapeColor = (0.7, 0.7, 0.7)
                except Exception as e:
                    FreeCAD.Console.PrintWarning(f"Error edificio {way_id}: {str(e)}\n")

            # 6. Transformadores
            try:
                num_transformers = int(tags.get('transformers', 1))
                for i in range(num_transformers):
                    transformer_pos = self.calculate_equipment_position(
                        polygon_points,
                        index=i,
                        total=num_transformers,
                        offset=3.0
                    )
                    transformer = self.create_transformer(
                        position=transformer_pos,
                        voltage=voltage,
                        tech_type=tags.get('substation:type', 'outdoor')
                    )
                    layer.addObject(transformer)
            except Exception as e:
                FreeCAD.Console.PrintWarning(f"Error transformadores {way_id}: {str(e)}\n")

            # 7. Torre de seccionamiento para alta tensión
            if substation_type == 'transmission' and voltage >= 110000:
                try:
                    tower_pos = self.calculate_tower_position(polygon_points)
                    tower = self.create_circuit_breaker_tower(
                        position=tower_pos,
                        voltage=voltage
                    )
                    layer.addObject(tower)
                except Exception as e:
                    FreeCAD.Console.PrintWarning(f"Error torre {way_id}: {str(e)}\n")

            # 8. Propiedades técnicas
            substation_data = layer.addObject("App::FeaturePython", f"{name}_Data")
            props = {
                "Voltage": voltage,
                "Type": substation_type,
                "Components": ['Base'] + (['Fence'] if 'fence' in tags else []) +
                              (['Building'] if 'building' in tags else []) +
                              [f'Transformer_{n + 1}' for n in range(num_transformers)]
            }
            for prop, value in props.items():
                if isinstance(value, list):
                    substation_data.addProperty("App::PropertyStringList", prop, "Technical").Components = value
                else:
                    substation_data.addProperty(
                        "App::PropertyFloat" if isinstance(value, float) else "App::PropertyString",
                        prop, "Technical").setValue(value)

        except Exception as e:
            FreeCAD.Console.PrintError(f"Error crítico en subestación {way_id}: {str(e)}\n")

    def add_substation_fence(self, parent_obj, polygon_points):
        """Añade cerca perimetral"""
        try:
            offset_points = self.offset_polygon(polygon_points, -0.5)
            fence = Part.makePolygon(offset_points).extrude(FreeCAD.Vector(0, 0, 2.5))
            fence_obj = parent_obj.Document.addObject("Part::Feature", "Fence")
            fence_obj.Shape = fence
            fence_obj.ViewObject.ShapeColor = (0.4, 0.4, 0.4)
            fence_obj.Placement.Base = parent_obj.Placement.Base
        except Exception as e:
            FreeCAD.Console.PrintWarning(f"Error en cerca: {str(e)}\n")

    def parse_voltage(self, voltage_str):
        # Convertir valores comunes de voltaje a numéricos
        voltage_map = {
            'low': 400,
            'medium': 20000,
            'high': 110000,
            'extra high': 380000,
            'ultra high': 765000
        }
        try:
            return float(''.join(filter(str.isdigit, voltage_str.split()[0]))) * 1000
        except:
            return 20000  # Valor por defecto

    def offset_polygon(self, points, offset):
        """Versión corregida sin error de sintaxis"""
        if not points:
            return []

        sum_x = sum(p.x for p in points)
        sum_y = sum(p.y for p in points)
        centroid = FreeCAD.Vector(sum_x / len(points), sum_y / len(points), 0)

        return [p + (centroid - p).normalize() * offset for p in points]

    def create_transformer(self, position, voltage, technology):
        # Crear transformador básico según características
        height = 2.0 + (voltage / 100000)
        radius = 0.5 + (voltage / 500000)

        transformer = Part.makeCylinder(radius, height, FreeCAD.Vector(position))
        transformer_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Transformer")
        transformer_obj.Shape = transformer
        transformer_obj.ViewObject.ShapeColor = (0.1, 0.1, 0.5) if 'oil' in technology.lower() else (0.5, 0.5, 0.5)
        return transformer_obj

    def calculate_equipment_position(self, polygon_points, index, total, offset=0.0):
        """Calcula posición equidistante alrededor del perímetro con offset"""
        perimeter = sum((p2 - p1).Length for p1, p2 in zip(polygon_points, polygon_points[1:]))
        target_dist = (perimeter / total) * index

        accumulated = 0.0
        for i in range(len(polygon_points) - 1):
            p1 = polygon_points[i]
            p2 = polygon_points[i + 1]
            segment_length = (p2 - p1).Length
            if accumulated + segment_length >= target_dist:
                direction = (p2 - p1).normalize()
                return p1 + direction * (target_dist - accumulated) + direction.cross(FreeCAD.Vector(0, 0, 1)) * offset
            accumulated += segment_length
        return polygon_points[0]

    def create_circuit_breaker_tower(self, position, voltage):
        """Crea torre de seccionamiento especializada"""
        tower = Part.makeCompound([
            Part.makeCylinder(0.8, 12, position),  # Poste principal
            Part.makeBox(3, 0.3, 0.3, position + FreeCAD.Vector(-1.5, -0.15, 12)),  # Cruceta superior
            Part.makeBox(2.5, 0.3, 0.3, position + FreeCAD.Vector(-1.25, -0.15, 9))  # Cruceta media
        ])
        tower_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "CircuitBreakerTower")
        tower_obj.Shape = tower
        tower_obj.ViewObject.ShapeColor = (0.1, 0.1, 0.1)
        tower_obj.addProperty("App::PropertyFloat", "Voltage", "Technical").Voltage = voltage
        return tower_obj

    def calculate_tower_position(self, polygon_points):
        # Colocar torre en el punto medio del lado más largo
        # (Implementación compleja que requeriría cálculo geométrico)
        return FreeCAD.Vector(polygon_points[0].x, polygon_points[0].y, 0)


    def create_vegetation(self):
        vegetation_layer = self.create_layer("Vegetation")

        # Procesar nodos de vegetación individual
        for way_id, tags in self.ways_data.items():
            coords = self.nodes.get(way_id)
            if not coords:
                continue

            pos = FreeCAD.Vector(*coords)

            if tags.get('natural') == 'tree':
                self.create_tree(pos, tags, vegetation_layer)
            elif tags.get('natural') == 'shrub':
                self.create_shrub(pos, tags, vegetation_layer)
            """elif tags.get('natural') == 'tree_stump':
                self.create_tree_stump(pos, vegetation_layer)"""

        # Procesar áreas vegetales
        for way_id, data in self.ways_data.items():
            tags = data['tags']
            nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]

            if not nodes or len(nodes) < 3:
                continue

            if tags.get('natural') == 'wood' or tags.get('landuse') == 'forest':
                self.create_forest(nodes, tags, vegetation_layer)
            elif tags.get('natural') == 'grassland':
                self.create_grassland(nodes, vegetation_layer)
            elif tags.get('natural') == 'heath':
                self.create_heathland(nodes, vegetation_layer)
            elif tags.get('natural') == 'scrub':
                self.create_scrub_area(nodes, vegetation_layer)

    def create_tree(self, position, tags, layer):
        """Crea un árbol individual con propiedades basadas en etiquetas OSM"""
        height = float(tags.get('height', 10.0))
        trunk_radius = float(tags.get('circumference', 1.0)) / (2 * math.pi)
        canopy_radius = float(tags.get('diameter_crown', 4.0)) / 2

        # Crear tronco
        trunk = Part.makeCylinder(trunk_radius, height, position)

        # Crear copa (forma cónica)
        canopy_center = position + FreeCAD.Vector(0, 0, height)
        canopy = Part.makeCone(canopy_radius, canopy_radius * 0.7, canopy_radius * 1.5, canopy_center)

        tree = trunk.fuse(canopy)
        tree_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Tree")
        layer.addObject(tree_obj)
        tree_obj.Shape = tree
        tree_obj.ViewObject.ShapeColor = (0.3, 0.6, 0.2)  # Verde bosque

        # Añadir metadatos
        for prop in ['genus', 'species', 'leaf_type', 'height']:
            if prop in tags:
                tree_obj.addProperty("App::PropertyString", prop.capitalize(), "Botany",
                                     "Botanical property").__setattr__(prop.capitalize(), tags[prop])

    def create_forest(self, nodes, tags, layer):
        """Crea un área boscosa con densidad variable"""
        polygon_points = [FreeCAD.Vector(*n) for n in nodes]
        if polygon_points[0] != polygon_points[-1]:
            polygon_points.append(polygon_points[0])

        # Crear base del bosque
        polygon = Part.makePolygon(polygon_points)
        face = Part.Face(polygon)
        forest_base = FreeCAD.ActiveDocument.addObject("Part::Feature", "Forest_Base")
        layer.addObject(forest_base)
        forest_base.Shape = face
        forest_base.ViewObject.ShapeColor = (0.15, 0.4, 0.1)  # Verde oscuro

        # Generar árboles aleatorios dentro del polígono
        density = float(tags.get('density', 0.5))  # Árboles por m²
        area = face.Area
        num_trees = int(area * density)

        for _ in range(num_trees):
            rand_point = self.random_point_in_polygon(polygon_points)
            self.create_tree(rand_point, {}, layer)

    def create_grassland(self, nodes, layer):
        """Crea un área de pastizales"""
        polygon_points = [FreeCAD.Vector(*n) for n in nodes]
        if polygon_points[0] != polygon_points[-1]:
            polygon_points.append(polygon_points[0])

        polygon = Part.makePolygon(polygon_points)
        face = Part.Face(polygon)
        grassland = FreeCAD.ActiveDocument.addObject("Part::Feature", "Grassland")
        layer.addObject(grassland)
        grassland.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1))
        grassland.ViewObject.ShapeColor = (0.5, 0.7, 0.3)  # Verde pasto

    def create_heathland(self, nodes, layer):
        """Crea un área de brezales con vegetación baja"""
        polygon_points = [FreeCAD.Vector(*n) for n in nodes]
        if polygon_points[0] != polygon_points[-1]:
            polygon_points.append(polygon_points[0])

        polygon = Part.makePolygon(polygon_points)
        face = Part.Face(polygon)
        heath = FreeCAD.ActiveDocument.addObject("Part::Feature", "Heathland")
        layer.addObject(heath)
        heath.Shape = face
        heath.ViewObject.ShapeColor = (0.6, 0.5, 0.4)  # Color terroso

        # Añadir arbustos dispersos
        for _ in range(int(face.Area * 0.1)):  # 1 arbusto cada 10m²
            rand_point = self.random_point_in_polygon(polygon_points)
            self.create_shrub(rand_point, {}, layer)

    def create_shrub(self, position, tags, layer):
        """Crea un arbusto individual"""
        height = float(tags.get('height', 1.5))
        radius = float(tags.get('diameter_crown', 1.0)) / 2

        # Crear forma de arbusto (cono invertido)
        base_center = position + FreeCAD.Vector(0, 0, height / 2)
        shrub = Part.makeCone(radius, radius * 1.5, height, base_center)

        shrub_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Shrub")
        layer.addObject(shrub_obj)
        shrub_obj.Shape = shrub
        shrub_obj.ViewObject.ShapeColor = (0.4, 0.5, 0.3)  # Verde arbusto

        # Añadir metadatos si existen
        if 'genus' in tags:
            shrub_obj.addProperty("App::PropertyString", "Genus", "Botany", "Plant genus").Genus = tags['genus']

    def create_tree_stump(self, position, layer):
        """Crea un tocón de árbol"""
        height = 0.4
        radius = 0.5

        stump = Part.makeCylinder(radius, height, position)
        stump_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Tree_Stump")
        layer.addObject(stump_obj)
        stump_obj.Shape = stump
        stump_obj.ViewObject.ShapeColor = (0.3, 0.2, 0.1)  # Marrón madera

    def random_point_in_polygon(self, polygon_points):
        """Genera un punto aleatorio dentro de un polígono"""
        min_x = min(p.x for p in polygon_points)
        max_x = max(p.x for p in polygon_points)
        min_y = min(p.y for p in polygon_points)
        max_y = max(p.y for p in polygon_points)

        while True:
            rand_x = random.uniform(min_x, max_x)
            rand_y = random.uniform(min_y, max_y)
            rand_point = FreeCAD.Vector(rand_x, rand_y, 0)

            # Verificar si el punto está dentro del polígono
            polygon = Part.makePolygon(polygon_points)
            face = Part.Face(polygon)
            if face.isInside(rand_point, 0.1, True):
                return rand_point

    def create_water_bodies(self):
        water_layer = self.create_layer("Water")

        for way_id, data in self.ways_data.items():
            if 'natural' in data['tags'] and data['tags']['natural'] == 'water':
                nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
                if len(nodes) > 2:
                    polygon_points = [n for n in nodes]
                    polygon = Part.makePolygon(polygon_points)
                    face = Part.Face(polygon)
                    water = FreeCAD.ActiveDocument.addObject("Part::Feature", "WaterBody")
                    water_layer.addObject(water)
                    water.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1))# * scale - self.Origin )
                    water.ViewObject.ShapeColor = self.feature_colors['water']

