""" Plate Selector for Lemontropia Suite Browse and search armor plates from Entropia Nexus API """ from decimal import Decimal from PyQt6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QTreeWidget, QTreeWidgetItem, QHeaderView, QLabel, QDialogButtonBox, QProgressBar, QGroupBox, QFormLayout, QComboBox ) from PyQt6.QtCore import Qt, QThread, pyqtSignal from PyQt6.QtGui import QColor from typing import Optional, List from core.nexus_full_api import get_nexus_api, NexusPlate class PlateLoaderThread(QThread): """Background thread for loading plates from API.""" plates_loaded = pyqtSignal(list) error_occurred = pyqtSignal(str) def run(self): try: api = get_nexus_api() plates = api.get_all_plates() self.plates_loaded.emit(plates) except Exception as e: self.error_occurred.emit(str(e)) class PlateSelectorDialog(QDialog): """Dialog for selecting armor plates from Entropia Nexus API.""" plate_selected = pyqtSignal(NexusPlate) def __init__(self, parent=None, damage_type: str = ""): super().__init__(parent) self.setWindowTitle("Select Armor Plate - Entropia Nexus") self.setMinimumSize(900, 600) self.all_plates: List[NexusPlate] = [] self.selected_plate: Optional[NexusPlate] = None self.preferred_damage_type = damage_type # For filtering self._setup_ui() self._load_data() def _setup_ui(self): layout = QVBoxLayout(self) layout.setSpacing(10) # Status self.status_label = QLabel("Loading plates from Entropia Nexus...") layout.addWidget(self.status_label) self.progress = QProgressBar() self.progress.setRange(0, 0) layout.addWidget(self.progress) # Filters filter_layout = QHBoxLayout() filter_layout.addWidget(QLabel("Protection Type:")) self.type_combo = QComboBox() self.type_combo.addItems(["All Types", "Impact", "Cut", "Stab", "Burn", "Cold", "Acid", "Electric"]) if self.preferred_damage_type: index = self.type_combo.findText(self.preferred_damage_type.capitalize()) if index >= 0: self.type_combo.setCurrentIndex(index) self.type_combo.currentTextChanged.connect(self._apply_filters) filter_layout.addWidget(self.type_combo) filter_layout.addWidget(QLabel("Min Protection:")) self.min_prot_combo = QComboBox() self.min_prot_combo.addItems(["Any", "1+", "3+", "5+", "7+", "10+"]) self.min_prot_combo.currentTextChanged.connect(self._apply_filters) filter_layout.addWidget(self.min_prot_combo) layout.addLayout(filter_layout) # Search search_layout = QHBoxLayout() search_layout.addWidget(QLabel("Search:")) self.search_input = QLineEdit() self.search_input.setPlaceholderText("Type to search plates (e.g., '5B', 'Impact')...") self.search_input.textChanged.connect(self._apply_filters) search_layout.addWidget(self.search_input) self.clear_btn = QPushButton("Clear") self.clear_btn.clicked.connect(self._clear_search) search_layout.addWidget(self.clear_btn) layout.addLayout(search_layout) # Results tree self.results_tree = QTreeWidget() self.results_tree.setHeaderLabels([ "Name", "Impact", "Cut", "Stab", "Burn", "Cold", "Acid", "Electric", "Total", "Decay" ]) header = self.results_tree.header() header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) header.setStretchLastSection(False) self.results_tree.itemSelectionChanged.connect(self._on_selection_changed) self.results_tree.itemDoubleClicked.connect(self._on_double_click) layout.addWidget(self.results_tree) # Preview panel self.preview_group = QGroupBox("Plate Preview") preview_layout = QFormLayout(self.preview_group) self.preview_name = QLabel("-") self.preview_protection = QLabel("-") self.preview_decay = QLabel("-") preview_layout.addRow("Name:", self.preview_name) preview_layout.addRow("Protection:", self.preview_protection) preview_layout.addRow("Decay:", self.preview_decay) layout.addWidget(self.preview_group) # Buttons buttons = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel ) buttons.accepted.connect(self._on_accept) buttons.rejected.connect(self.reject) self.ok_button = buttons.button(QDialogButtonBox.StandardButton.Ok) self.ok_button.setEnabled(False) layout.addWidget(buttons) def _load_data(self): """Load plates in background thread.""" self.loader = PlateLoaderThread() self.loader.plates_loaded.connect(self._on_plates_loaded) self.loader.error_occurred.connect(self._on_load_error) self.loader.start() def _on_plates_loaded(self, plates: List[NexusPlate]): """Handle loaded plates.""" self.all_plates = plates self.status_label.setText(f"Loaded {len(plates)} plates from Entropia Nexus") self.progress.setRange(0, 100) self.progress.setValue(100) self._apply_filters() def _on_load_error(self, error: str): """Handle load error.""" self.status_label.setText(f"Error loading plates: {error}") self.progress.setRange(0, 100) self.progress.setValue(0) def _apply_filters(self): """Apply all filters and search.""" plates = self.all_plates.copy() # Type filter type_filter = self.type_combo.currentText() if type_filter != "All Types": type_lower = type_filter.lower() plates = [p for p in plates if getattr(p, f"protection_{type_lower}", Decimal("0")) > 0] # Min protection filter min_prot = self.min_prot_combo.currentText() if min_prot != "Any": min_val = int(min_prot.replace("+", "")) plates = [p for p in plates if ( p.protection_impact + p.protection_cut + p.protection_stab + p.protection_burn + p.protection_cold + p.protection_acid + p.protection_electric ) >= min_val] # Search filter search_text = self.search_input.text() if search_text: query = search_text.lower() plates = [p for p in plates if query in p.name.lower()] self._populate_results(plates) # Update status if search_text: self.status_label.setText(f"Found {len(plates)} plates matching '{search_text}'") else: self.status_label.setText(f"Showing {len(plates)} of {len(self.all_plates)} plates") def _populate_results(self, plates: List[NexusPlate]): """Populate results tree.""" self.results_tree.clear() # Sort by total protection (highest first) plates = sorted(plates, key=lambda p: ( p.protection_impact + p.protection_cut + p.protection_stab + p.protection_burn + p.protection_cold + p.protection_acid + p.protection_electric ), reverse=True) for plate in plates: item = QTreeWidgetItem() item.setText(0, plate.name) item.setText(1, str(plate.protection_impact)) item.setText(2, str(plate.protection_cut)) item.setText(3, str(plate.protection_stab)) item.setText(4, str(plate.protection_burn)) item.setText(5, str(plate.protection_cold)) item.setText(6, str(plate.protection_acid)) item.setText(7, str(plate.protection_electric)) total = plate.protection_impact + plate.protection_cut + plate.protection_stab + plate.protection_burn + plate.protection_cold + plate.protection_acid + plate.protection_electric item.setText(8, str(total)) item.setText(9, f"{plate.decay:.2f}") # Highlight plates matching preferred damage type if self.preferred_damage_type: type_lower = self.preferred_damage_type.lower() if getattr(plate, f"protection_{type_lower}", Decimal("0")) > 0: item.setBackground(0, QColor("#2d4a3e")) # Dark green item.setData(0, Qt.ItemDataRole.UserRole, plate) self.results_tree.addTopLevelItem(item) def _clear_search(self): """Clear search and filters.""" self.search_input.clear() self.type_combo.setCurrentIndex(0) self.min_prot_combo.setCurrentIndex(0) self._apply_filters() def _on_selection_changed(self): """Handle selection change.""" items = self.results_tree.selectedItems() if items: self.selected_plate = items[0].data(0, Qt.ItemDataRole.UserRole) self.ok_button.setEnabled(True) self._update_preview(self.selected_plate) else: self.selected_plate = None self.ok_button.setEnabled(False) def _update_preview(self, plate: NexusPlate): """Update preview panel.""" self.preview_name.setText(plate.name) prot_parts = [] if plate.protection_impact > 0: prot_parts.append(f"Imp:{plate.protection_impact}") if plate.protection_cut > 0: prot_parts.append(f"Cut:{plate.protection_cut}") if plate.protection_stab > 0: prot_parts.append(f"Stab:{plate.protection_stab}") if plate.protection_burn > 0: prot_parts.append(f"Burn:{plate.protection_burn}") if plate.protection_cold > 0: prot_parts.append(f"Cold:{plate.protection_cold}") self.preview_protection.setText(", ".join(prot_parts) if prot_parts else "None") self.preview_decay.setText(f"{plate.decay:.2f} PEC") def _on_double_click(self, item, column): """Handle double click.""" self._on_accept() def _on_accept(self): """Handle OK button.""" if self.selected_plate: self.plate_selected.emit(self.selected_plate) self.accept()