# /**********************************************************************
# *                                                                     *
# * Copyright (c) 2021 Javier Braña <javier.branagutierrez@gmail.com>  *
# *                                                                     *
# * This program is free software; you can redistribute it and/or modify*
# * it under the terms of the GNU Lesser General Public License (LGPL)  *
# * as published by the Free Software Foundation; either version 2 of   *
# * the License, or (at your option) any later version.                 *
# * for detail see the LICENCE text file.                               *
# *                                                                     *
# * This program is distributed in the hope that it will be useful,     *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of      *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       *
# * GNU Library General Public License for more details.                *
# *                                                                     *
# * You should have received a copy of the GNU Library General Public   *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307*
# * USA                                                                 *
# *                                                                     *
# ***********************************************************************

import FreeCAD
import utm

if FreeCAD.GuiUp:
    import FreeCADGui
    from PySide import QtCore, QtGui
    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

import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
from PVPlantResources import DirResources as DirResources


class MapWindow(QtGui.QWidget):
    def __init__(self, WinTitle="MapWindow"):
        super(MapWindow, self).__init__()
        self.raise_()
        self.lat = None
        self.lon = None
        self.minLat = None
        self.maxLat = None
        self.minLon = None
        self.maxLon = None
        self.zoom = None
        self.WinTitle = WinTitle
        self.georeference_coordinates = {'lat': None, 'lon': None}
        self.setupUi()

    def setupUi(self):
        from PySide2.QtWebEngineWidgets import QWebEngineView
        from PySide2.QtWebChannel import QWebChannel

        self.ui = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantGeoreferencing.ui", self)

        self.resize(1200, 800)
        self.setWindowTitle(self.WinTitle)
        self.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "Location.svg")))
        self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)

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

        LeftWidget = QtGui.QWidget(self)
        LeftLayout = QtGui.QVBoxLayout(LeftWidget)
        LeftWidget.setLayout(LeftLayout)
        LeftLayout.setContentsMargins(0, 0, 0, 0)

        RightWidget = QtGui.QWidget(self)
        RightWidget.setFixedWidth(350)
        RightLayout = QtGui.QVBoxLayout(RightWidget)
        RightWidget.setLayout(RightLayout)
        RightLayout.setContentsMargins(0, 0, 0, 0)

        self.layout.addWidget(LeftWidget)
        self.layout.addWidget(RightWidget)

        # Left Widgets:
        # -- Search Bar:
        self.valueSearch = QtGui.QLineEdit(self)
        self.valueSearch.setPlaceholderText("Search")
        self.valueSearch.returnPressed.connect(self.onSearch)

        searchbutton = QtGui.QPushButton('Search')
        searchbutton.setFixedWidth(80)
        searchbutton.clicked.connect(self.onSearch)

        SearchBarLayout = QtGui.QHBoxLayout(self)
        SearchBarLayout.addWidget(self.valueSearch)
        SearchBarLayout.addWidget(searchbutton)
        LeftLayout.addLayout(SearchBarLayout)

        # -- Webbroser:
        self.view = QWebEngineView()
        self.channel = QWebChannel(self.view.page())
        self.view.page().setWebChannel(self.channel)
        self.channel.registerObject("MyApp", self)
        file = os.path.join(DirResources, "webs", "main.html")
        self.view.page().loadFinished.connect(self.onLoadFinished)
        self.view.page().load(QtCore.QUrl.fromLocalFile(file))
        LeftLayout.addWidget(self.view)
        # self.layout.addWidget(self.view, 1, 0, 1, 3)

        # -- Latitud y longitud:
        self.labelCoordinates = QtGui.QLabel()
        self.labelCoordinates.setFixedHeight(21)
        LeftLayout.addWidget(self.labelCoordinates)
        # self.layout.addWidget(self.labelCoordinates, 2, 0, 1, 3)

        # Right Widgets:
        labelKMZ = QtGui.QLabel()
        labelKMZ.setText("Cargar un archivo KMZ/KML:")
        self.kmlButton = QtGui.QPushButton()
        self.kmlButton.setFixedSize(32, 32)
        self.kmlButton.setIcon(QtGui.QIcon(os.path.join(DirIcons, "googleearth.svg")))
        widget = QtGui.QWidget(self)
        layout = QtGui.QHBoxLayout(widget)
        widget.setLayout(layout)
        layout.addWidget(labelKMZ)
        layout.addWidget(self.kmlButton)
        RightLayout.addWidget(widget)

        # -----------------------
        self.groupbox = QtGui.QGroupBox("Importar datos desde:")
        self.groupbox.setCheckable(True)
        self.groupbox.setChecked(True)
        radio1 = QtGui.QRadioButton("Google Elevation")
        radio2 = QtGui.QRadioButton("Nube de Puntos")
        radio3 = QtGui.QRadioButton("Datos GPS")
        radio1.setChecked(True)

        # buttonDialog = QtGui.QPushButton('...')
        # buttonDialog.setEnabled(False)

        vbox = QtGui.QVBoxLayout(self)
        vbox.addWidget(radio1)
        vbox.addWidget(radio2)
        vbox.addWidget(radio3)

        self.groupbox.setLayout(vbox)
        RightLayout.addWidget(self.groupbox)
        # ------------------------

        self.checkboxImportGis = QtGui.QCheckBox("Importar datos GIS")
        RightLayout.addWidget(self.checkboxImportGis)

        self.checkboxImportSatelitalImagen = QtGui.QCheckBox("Importar Imagen Satelital")
        RightLayout.addWidget(self.checkboxImportSatelitalImagen)

        verticalSpacer = QtGui.QSpacerItem(20, 48, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
        RightLayout.addItem(verticalSpacer)

        self.bAccept = QtGui.QPushButton('Accept')
        self.bAccept.clicked.connect(self.onAcceptClick)
        RightLayout.addWidget(self.bAccept)

        # signals/slots
        QtCore.QObject.connect(self.kmlButton, QtCore.SIGNAL("clicked()"), self.importKML)


    def onLoadFinished(self):
        file = os.path.join(DirResources, "webs", "map.js")
        frame = self.view.page()
        with open(file, 'r') as f:
            frame.runJavaScript(f.read())

    def onSearch(self):
        if self.valueSearch.text() == "":
            return

        from geopy.geocoders import Nominatim

        geolocator = Nominatim(user_agent="http")
        location = geolocator.geocode(self.valueSearch.text())
        self.valueSearch.setText(location.address)
        self.panMap(location.longitude, location.latitude, location.raw['boundingbox'])

    def onAcceptClick(self):
        frame = self.view.page()
        # 1. georeferenciar
        frame.runJavaScript(
            "MyApp.georeference(drawnItems.getBounds().getCenter().lat, drawnItems.getBounds().getCenter().lng);"
        )

        # 2. importar todos los elementos dibujados:
        frame.runJavaScript(
            "var data = drawnItems.toGeoJSON();"
            "MyApp.shapes(JSON.stringify(data));"
        )

        self.close()

    @QtCore.Slot(float, float)
    def onMapMove(self, lat, lng):
        self.lat = lat
        self.lon = lng
        x, y, zone_number, zone_letter = utm.from_latlon(lat, lng)
        self.labelCoordinates.setText('Longitud: {:.5f}, Latitud: {:.5f}'.format(lng, lat) +
                                      '  |  UTM: ' + str(zone_number) + zone_letter +
                                      ', {:.5f}m E, {:.5f}m N'.format(x, y))

    @QtCore.Slot(float, float, float, float, int)
    def onMapZoom(self, minLat, minLon, maxLat, maxLon, zoom):
        self.minLat = min([minLat, maxLat])
        self.maxLat = max([minLat, maxLat])
        self.minLon = min([minLon, maxLon])
        self.maxLon = max([minLon, maxLon])
        self.zoom = zoom

    @QtCore.Slot(float, float)
    def georeference(self, lat, lng):
        import PVPlantSite
        from geopy.geocoders import Nominatim

        self.georeference_coordinates['lat'] = lat
        self.georeference_coordinates['lon'] = lng

        Site = PVPlantSite.get(create=True)
        Site.Proxy.setLatLon(lat, lng)

        geolocator = Nominatim(user_agent="http")
        location = geolocator.reverse('{:.5f}, {:.5f}'.format(lat, lng))
        if location:
            if location.raw["address"].get("road"):
                str = location.raw["address"]["road"]
                if location.raw["address"].get("house_number"):
                    str += ' ({0})'.format(location.raw["address"]["house_number"])
                Site.Address = str
            if location.raw["address"].get("city"):
                Site.City = location.raw["address"]["city"]
            if location.raw["address"].get("postcode"):
                Site.PostalCode = location.raw["address"]["postcode"]
            if location.raw["address"].get("address"):
                Site.Region = '{0}'.format(location.raw["address"]["province"])
            if location.raw["address"].get("state"):
                if Site.Region != "":
                    Site.Region += " - "
                Site.Region += '{0}'.format(location.raw["address"]["state"])   # province - state
            Site.Country = location.raw["address"]["country"]

    @QtCore.Slot(str)
    def shapes(self, drawnItems):
        import geojson
        import PVPlantImportGrid as ImportElevation
        import Draft
        import PVPlantSite
        Site = PVPlantSite.get()

        offset = FreeCAD.Vector(0, 0, 0)
        if not (self.lat is None or self.lon is None):
            offset = FreeCAD.Vector(Site.Origin)
            offset.z = 0

        items = geojson.loads(drawnItems)
        for item in items['features']:
            if item['geometry']['type'] == "Point":     # 1. if the feature is a Point or Circle:
                coord = item['geometry']['coordinates']
                point = ImportElevation.getElevationFromOE([[coord[0], coord[1]],])
                c = FreeCAD.Vector(point[0][0], point[0][1], point[0][2]).sub(offset)
                if item['properties'].get('radius'):
                    r = round(item['properties']['radius'] * 1000, 0)
                    p = FreeCAD.Placement()
                    p.Base = c
                    obj = Draft.makeCircle(r, placement=p, face=False)
                else:
                    ''' do something '''
                    obj = Draft.make_point(c * 1000, color=(0.5, 0.3, 0.6), point_size=10)
            else:                                       # 2. if the feature is a Polygon or Line:
                cw = False
                name = "Línea"
                lp = item['geometry']['coordinates']
                if item['geometry']['type'] == "Polygon":
                    cw = True
                    name = "Area"
                    lp = item['geometry']['coordinates'][0]

                pts = [[cords[1], cords[0]] for cords in lp]
                tmp = ImportElevation.getElevationFromOE(pts)
                pts = [p.sub(offset) for p in tmp]

                obj = Draft.makeWire(pts, closed=cw, face=False)
                #obj.Placement.Base = Site.Origin
                obj.Label = name
                Draft.autogroup(obj)

            if item['properties'].get('name'):
                obj.Label = item['properties']['name']

        if self.checkboxImportGis.isChecked():
            self.getDataFromOSM(self.minLat, self.minLon, self.maxLat, self.maxLon)

        if self.checkboxImportSatelitalImagen.isChecked():
            # Usar los límites reales del terreno (rectangular)
            '''s_lat = self.minLat
            s_lon = self.minLon
            n_lat = self.maxLat
            n_lon = self.maxLon

            # Obtener puntos UTM para las esquinas
            corners = ImportElevation.getElevationFromOE([
                [s_lat, s_lon],  # Esquina suroeste
                [n_lat, s_lon],  # Esquina sureste
                [n_lat, n_lon],  # Esquina noreste
                [s_lat, n_lon]  # Esquina noroeste
            ])

            if not corners or len(corners) < 4:
                FreeCAD.Console.PrintError("Error obteniendo elevaciones para las esquinas\n")
                return

            # Descargar imagen satelital
            from lib.GoogleSatelitalImageDownload import GoogleMapDownloader
            downloader = GoogleMapDownloader(
                zoom= 18, #self.zoom,
                layer='raw_satellite'
            )
            img = downloader.generateImage(
                sw_lat=s_lat,
                sw_lng=s_lon,
                ne_lat=n_lat,
                ne_lng=n_lon
            )

            # Guardar imagen en el directorio del documento
            doc_path = os.path.dirname(FreeCAD.ActiveDocument.FileName) if FreeCAD.ActiveDocument.FileName else ""
            if not doc_path:
                doc_path = FreeCAD.ConfigGet("UserAppData")

            filename = os.path.join(doc_path, "background.jpeg")
            img.save(filename)

            ancho, alto = img.size

            # Crear objeto de imagen en FreeCAD
            doc = FreeCAD.ActiveDocument
            img_obj = doc.addObject('Image::ImagePlane', 'Background')
            img_obj.ImageFile = filename
            img_obj.Label = 'Background'

            # Calcular dimensiones en metros usando las coordenadas UTM
            # Extraer las coordenadas de las esquinas
            sw = corners[0]  # Suroeste
            se = corners[1]  # Sureste
            ne = corners[2]  # Noreste
            nw = corners[3]  # Noroeste

            # Calcular ancho (promedio de los lados superior e inferior)
            width_bottom = se.x - sw.x
            width_top = ne.x - nw.x
            width_m = (width_bottom + width_top) / 2

            # Calcular alto (promedio de los lados izquierdo y derecho)
            height_left = nw.y - sw.y
            height_right = ne.y - se.y
            height_m = (height_left + height_right) / 2

            img_obj.XSize = width_m
            img_obj.YSize = height_m

            # Posicionar el centro de la imagen en (0,0,0)
            img_obj.Placement.Base = FreeCAD.Vector(-width_m / 2, -height_m / 2, 0)'''

            # Definir área rectangular
            s_lat = self.minLat
            s_lon = self.minLon
            n_lat = self.maxLat
            n_lon = self.maxLon

            # Obtener puntos UTM para las esquinas y el punto de referencia
            points = [
                [s_lat, s_lon],  # Suroeste
                [n_lat, n_lon],  # Noreste
                [self.georeference_coordinates['lat'], self.georeference_coordinates['lon']]  # Punto de referencia
            ]
            utm_points = ImportElevation.getElevationFromOE(points)

            if not utm_points or len(utm_points) < 3:
                FreeCAD.Console.PrintError("Error obteniendo elevaciones para las esquinas y referencia\n")
                return

            sw_utm, ne_utm, ref_utm = utm_points

            # Descargar imagen satelital
            from lib.GoogleSatelitalImageDownload import GoogleMapDownloader
            downloader = GoogleMapDownloader(
                zoom=self.zoom,
                layer='raw_satellite'
            )
            img = downloader.generateImage(
                sw_lat=s_lat,
                sw_lng=s_lon,
                ne_lat=n_lat,
                ne_lng=n_lon
            )

            # Guardar imagen
            doc_path = os.path.dirname(FreeCAD.ActiveDocument.FileName) if FreeCAD.ActiveDocument.FileName else ""
            if not doc_path:
                doc_path = FreeCAD.ConfigGet("UserAppData")

            filename = os.path.join(doc_path, "background.jpeg")
            img.save(filename)

            # Calcular dimensiones reales en metros
            width_m = ne_utm.x - sw_utm.x  # Ancho en metros (este-oeste)
            height_m = ne_utm.y - sw_utm.y  # Alto en metros (norte-sur)

            # Calcular posición relativa del punto de referencia dentro de la imagen
            rel_x = (ref_utm.x - sw_utm.x) / width_m if width_m != 0 else 0.5
            rel_y = (ref_utm.y - sw_utm.y) / height_m if height_m != 0 else 0.5

            # Crear objeto de imagen en FreeCAD
            doc = FreeCAD.ActiveDocument
            img_obj = doc.addObject('Image::ImagePlane', 'Background')
            img_obj.ImageFile = filename
            img_obj.Label = 'Background'

            # Convertir dimensiones a milímetros (FreeCAD trabaja en mm)
            img_obj.XSize = width_m * 1000
            img_obj.YSize = height_m * 1000

            # Posicionar para que el punto de referencia esté en (0,0,0)
            # La esquina inferior izquierda debe estar en:
            #   x = -rel_x * ancho_total
            #   y = -rel_y * alto_total
            img_obj.Placement.Base = FreeCAD.Vector(
                -rel_x * width_m * 1000,
                -rel_y * height_m * 1000,
                0
            )

            # Refrescar el documento
            doc.recompute()

    def calculate_texture_transform(self, mesh_obj, width_m, height_m):
        """Calcula la transformación precisa para la textura"""
        try:
            # Obtener coordenadas reales de las esquinas
            import utm
            sw = utm.from_latlon(self.minLat, self.minLon)
            ne = utm.from_latlon(self.maxLat, self.maxLon)

            # Crear matriz de transformación
            scale_x = (ne[0] - sw[0]) / width_m
            scale_y = (ne[1] - sw[1]) / height_m

            # Aplicar transformación (solo si se usa textura avanzada)
            if hasattr(mesh_obj.ViewObject, "TextureMapping"):
                mesh_obj.ViewObject.TextureMapping = "PLANE"
                mesh_obj.ViewObject.TextureScale = (scale_x, scale_y)
                mesh_obj.ViewObject.TextureOffset = (sw[0], sw[1])

        except Exception as e:
            FreeCAD.Console.PrintWarning(f"No se pudo calcular transformación: {str(e)}\n")

    def getDataFromOSM(self, min_lat, min_lon, max_lat, max_lon):
        import Importer.importOSM as importOSM
        import PVPlantSite
        site = PVPlantSite.get()

        offset = FreeCAD.Vector(0, 0, 0)
        if not (self.lat is None or self.lon is None):
            offset = FreeCAD.Vector(site.Origin)
            offset.z = 0
        importer = importOSM.OSMImporter(offset)
        osm_data = importer.get_osm_data(f"{min_lat},{min_lon},{max_lat},{max_lon}")
        importer.process_osm_data(osm_data)

        '''FreeCAD.activeDocument().recompute()
        FreeCADGui.updateGui()
        FreeCADGui.SendMsgToActiveView("ViewFit")'''

    def panMap_old(self, lng, lat, geometry=""):
        frame = self.view.page()
        bbox = "[{0}, {1}], [{2}, {3}]".format(float(geometry[0]), float(geometry[2]),
                                               float(geometry[1]), float(geometry[3]))
        command = 'map.panTo(L.latLng({lt}, {lg}));'.format(lt=lat, lg=lng)
        command += 'map.fitBounds([{box}]);'.format(box=bbox)
        frame.runJavaScript(command)

    # deepseek
    def panMap(self, lng, lat, geometry=None):
        frame = self.view.page()

        # 1. Validación del parámetro geometry
        if not geometry or len(geometry) < 4:
            # Pan básico sin ajuste de bounds
            command = f'map.panTo(L.latLng({lat}, {lng}));'
        else:
            try:
                # 2. Mejor manejo de coordenadas (Leaflet usa [lat, lng])
                # Asumiendo que geometry es [min_lng, min_lat, max_lng, max_lat]
                southwest = f"{float(geometry[1])}, {float(geometry[0])}"  # min_lat, min_lng
                northeast = f"{float(geometry[3])}, {float(geometry[2])}"  # max_lat, max_lng
                command = f'map.panTo(L.latLng({lat}, {lng}));'
                command += f'map.fitBounds(L.latLngBounds([{southwest}], [{northeast}]));'
            except (IndexError, ValueError, TypeError) as e:
                print(f"Error en geometry: {str(e)}")
                command = f'map.panTo(L.latLng({lat}, {lng}));'
        frame.runJavaScript(command)

    def importKML(self):
        file = QtGui.QFileDialog.getOpenFileName(None, "FileDialog", "", "Google Earth (*.kml *.kmz)")[0]

        from lib.kml2geojson import kmz_convert
        layers = kmz_convert(file, "", )
        frame = self.view.page()
        for layer in layers:
            command = "var geoJsonLayer = L.geoJSON({0}); drawnItems.addLayer(geoJsonLayer); map.fitBounds(geoJsonLayer.getBounds());".format( layer)
            frame.runJavaScript(command)


class CommandPVPlantGeoreferencing:

    def GetResources(self):
        return {'Pixmap': str(os.path.join(DirIcons, "Location.svg")),
                'Accel': "G, R",
                'MenuText': QT_TRANSLATE_NOOP("Georeferencing","Georeferencing"),
                'ToolTip': QT_TRANSLATE_NOOP("Georeferencing","Referenciar el lugar")}

    def Activated(self):
        self.form = MapWindow()
        self.form.show()

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

'''if FreeCAD.GuiUp:
    FreeCADGui.addCommand('PVPlantGeoreferencing',_CommandPVPlantGeoreferencing())
'''
