# -*- coding: utf-8 -*-
"""
Advanced Map Downloader Dialog

Dialog class for the Advanced Map Downloader plugin.
Handles UI logic, coordinate type switching, and parameter extraction.
"""

import os
from qgis.PyQt import uic
from qgis.PyQt import QtWidgets
from qgis.PyQt.QtCore import pyqtSignal
from qgis.core import QgsProject

from .export_handlers import EXPORT_FORMATS, FILE_FILTERS

# Load the UI file
FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'advanced_map_downloader_dialog_base.ui'))


class AdvancedMapDownloaderDialog(QtWidgets.QDialog, FORM_CLASS):
    """Dialog for Advanced Map Downloader plugin."""

    # Signals for canvas tools
    draw_from_canvas_clicked = pyqtSignal(str)  # coordinate type
    use_canvas_clicked = pyqtSignal()
    download_clicked = pyqtSignal()  # Signal for download
    dialog_closed = pyqtSignal()  # Signal when dialog is closed
    input_changed = pyqtSignal()  # Signal when any input changes (for rubber band update)

    # Coordinate type to stacked widget page index
    COORD_TYPE_PAGES = {
        'Lat-Long with Pixels': 0,
        'XYZ Tile': 1,
        'Square Grid': 2,
        'WKT': 3,
        'WKB': 4,
        'GeoJSON': 5
    }

    def __init__(self, parent=None):
        """Constructor."""
        super(AdvancedMapDownloaderDialog, self).__init__(parent)
        self.setupUi(self)

        # Dialog will stay on top of parent (QGIS) only, not all windows
        # This is achieved through the parent-child relationship

        # Connect signals
        self.comboBox_coordType.currentTextChanged.connect(
            self._on_coord_type_changed)
        self.pushButton_browse.clicked.connect(self._select_output_file)
        self.pushButton_drawCanvas.clicked.connect(self._on_draw_canvas)
        self.pushButton_useCanvas.clicked.connect(self._on_use_canvas)
        self.comboBox_format.currentTextChanged.connect(
            self._on_format_changed)

        # Connect new buttons
        self.pushButton_download.clicked.connect(self._on_download)
        self.pushButton_close.clicked.connect(self.close)

        # Connect input changes to emit input_changed signal for real-time
        # rubber band updates
        self._connect_input_signals()

        # Initialize stacked widget to first page
        self.stackedWidget_inputs.setCurrentIndex(0)

    def _connect_input_signals(self):
        """Connect all input widgets to the input_changed signal."""
        # Coordinate type change
        self.comboBox_coordType.currentTextChanged.connect(
            self._emit_input_changed)

        # GSD change
        self.spinBox_gsd.valueChanged.connect(self._emit_input_changed)

        # Lat-Long with Pixels inputs
        self.lineEdit_lat.textChanged.connect(self._emit_input_changed)
        self.lineEdit_lon.textChanged.connect(self._emit_input_changed)
        self.spinBox_widthPx.valueChanged.connect(self._emit_input_changed)
        self.spinBox_heightPx.valueChanged.connect(self._emit_input_changed)

        # XYZ Tile inputs
        self.spinBox_z.valueChanged.connect(self._emit_input_changed)
        self.spinBox_x.valueChanged.connect(self._emit_input_changed)
        self.spinBox_y.valueChanged.connect(self._emit_input_changed)

        # Square Grid inputs
        self.lineEdit_gridLat.textChanged.connect(self._emit_input_changed)
        self.lineEdit_gridLon.textChanged.connect(self._emit_input_changed)
        self.spinBox_gridSize.valueChanged.connect(self._emit_input_changed)
        self.comboBox_gridUnit.currentTextChanged.connect(
            self._emit_input_changed)

        # WKT, WKB, GeoJSON inputs
        self.plainTextEdit_wkt.textChanged.connect(self._emit_input_changed)
        self.plainTextEdit_wkb.textChanged.connect(self._emit_input_changed)
        self.plainTextEdit_geojson.textChanged.connect(
            self._emit_input_changed)

    def _emit_input_changed(self):
        """Emit input_changed signal when any input value changes."""
        self.input_changed.emit()

    def _on_coord_type_changed(self, coord_type):
        """Handle coordinate type dropdown change."""
        page_index = self.COORD_TYPE_PAGES.get(coord_type, 0)
        self.stackedWidget_inputs.setCurrentIndex(page_index)

        # All input types now use rectangle drawing
        self.pushButton_drawCanvas.setToolTip(
            "Draw rectangle on map to define download area (all settings auto-adjust)")

    def _on_draw_canvas(self):
        """Emit signal when Draw from Canvas button is clicked."""
        coord_type = self.comboBox_coordType.currentText()
        self.draw_from_canvas_clicked.emit(coord_type)

    def _on_use_canvas(self):
        """Emit signal when Use Canvas Extent button is clicked."""
        self.use_canvas_clicked.emit()

    def _on_download(self):
        """Emit signal when Download button is clicked."""
        self.download_clicked.emit()

    def _on_format_changed(self, format_text):
        """Update file filter based on selected format."""
        # Just updating internal state, actual filter applied in browse

    def _select_output_file(self):
        """Open file dialog to select output file."""
        format_text = self.comboBox_format.currentText()
        format_key = EXPORT_FORMATS.get(format_text, 'geotiff')
        file_filter = FILE_FILTERS.get(format_key, 'All Files (*)')

        filename, _ = QtWidgets.QFileDialog.getSaveFileName(
            self,
            "Select output file",
            "",
            file_filter
        )
        if filename:
            self.lineEdit_output.setText(filename)

    def populate_layers(self):
        """Populate the layer combo box with all layers from the project."""
        self.comboBox_layer.clear()
        layers = QgsProject.instance().mapLayers().values()
        for layer in layers:
            self.comboBox_layer.addItem(layer.name(), layer)

    def get_current_coord_type(self):
        """Get the currently selected coordinate type."""
        return self.comboBox_coordType.currentText()

    def set_latlon(self, lat, lon):
        """Set latitude/longitude values for Lat-Long with Pixels mode."""
        self.lineEdit_lat.setText(str(lat))
        self.lineEdit_lon.setText(str(lon))

    def set_grid_latlon(self, lat, lon):
        """Set latitude/longitude values for Square Grid mode."""
        self.lineEdit_gridLat.setText(str(lat))
        self.lineEdit_gridLon.setText(str(lon))

    def set_xyz(self, z, x, y):
        """Set XYZ tile values."""
        self.spinBox_z.setValue(z)
        self.spinBox_x.setValue(x)
        self.spinBox_y.setValue(y)

    def set_wkt(self, wkt_str):
        """Set WKT geometry string."""
        self.plainTextEdit_wkt.setPlainText(wkt_str)

    def set_wkb(self, wkb_hex):
        """Set WKB hex string."""
        self.plainTextEdit_wkb.setPlainText(wkb_hex)

    def set_geojson(self, geojson_str):
        """Set GeoJSON geometry string."""
        self.plainTextEdit_geojson.setPlainText(geojson_str)

    # Progress bar methods
    def set_progress(self, value, maximum=100):
        """Set progress bar value."""
        self.progressBar.setMaximum(maximum)
        self.progressBar.setValue(value)

    def set_status(self, message):
        """Set status label text."""
        self.label_status.setText(message)

    def reset_progress(self):
        """Reset progress bar and status to initial state."""
        self.progressBar.setValue(0)
        self.progressBar.setMaximum(100)
        self.label_status.setText("Ready")

    def set_downloading(self, is_downloading):
        """Enable/disable controls during download."""
        self.pushButton_download.setEnabled(not is_downloading)
        self.pushButton_drawCanvas.setEnabled(not is_downloading)
        self.pushButton_useCanvas.setEnabled(not is_downloading)
        self.pushButton_browse.setEnabled(not is_downloading)
        self.comboBox_coordType.setEnabled(not is_downloading)
        self.comboBox_layer.setEnabled(not is_downloading)
        self.comboBox_format.setEnabled(not is_downloading)

    def get_parameters(self):
        """
        Get all parameters from the dialog.

        :returns: dict with all parameters or None if invalid
        """
        try:
            coord_type = self.comboBox_coordType.currentText()
            gsd = self.spinBox_gsd.value()
            layer = self.comboBox_layer.currentData()
            output_path = self.lineEdit_output.text()
            format_text = self.comboBox_format.currentText()
            export_format = EXPORT_FORMATS.get(format_text, 'geotiff')
            load_as_layer = self.checkBox_loadLayer.isChecked()

            params = {
                'coord_type': coord_type,
                'gsd': gsd,
                'layer': layer,
                'output_path': output_path,
                'export_format': export_format,
                'load_as_layer': load_as_layer
            }

            # Get coordinate-specific parameters
            if coord_type == 'Lat-Long with Pixels':
                params['lat'] = float(self.lineEdit_lat.text())
                params['lon'] = float(self.lineEdit_lon.text())
                params['width_px'] = self.spinBox_widthPx.value()
                params['height_px'] = self.spinBox_heightPx.value()

            elif coord_type == 'XYZ Tile':
                params['z'] = self.spinBox_z.value()
                params['x'] = self.spinBox_x.value()
                params['y'] = self.spinBox_y.value()

            elif coord_type == 'Square Grid':
                params['lat'] = float(self.lineEdit_gridLat.text())
                params['lon'] = float(self.lineEdit_gridLon.text())
                params['grid_size'] = self.spinBox_gridSize.value()
                params['grid_unit'] = self.comboBox_gridUnit.currentText()

            elif coord_type == 'WKT':
                params['wkt'] = self.plainTextEdit_wkt.toPlainText().strip()
                if not params['wkt']:
                    return None

            elif coord_type == 'WKB':
                params['wkb'] = self.plainTextEdit_wkb.toPlainText().strip()
                if not params['wkb']:
                    return None

            elif coord_type == 'GeoJSON':
                params['geojson'] = self.plainTextEdit_geojson.toPlainText().strip()
                if not params['geojson']:
                    return None

            return params

        except ValueError:
            return None

    def closeEvent(self, event):
        """Handle dialog close event."""
        self.reset_progress()
        self.dialog_closed.emit()  # Emit signal for cleanup
        event.accept()
