Project

General

Profile


home_page.dart history_page.dart feeding_page.dart connection_page.dart

config_page.dart


import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

// --- MODELO ---
class FishParameters {
  final double temperature_min;
  final double temperature_max;
  final double ph_min;
  final double ph_max;
  final double nivel_luz; 
  final double nivel_agua;

  FishParameters(this.temperature_min, this.temperature_max, this.ph_min, this.ph_max, this.nivel_luz, this.nivel_agua);
}

// --- DATOS FIJOS ---
Map<String, FishParameters> fishParameters = {
  'Pez Platy (Xiphophorus maculatus)': FishParameters(20.0, 26.0, 7.0, 8.2, 2.0, 45.0), 
  'Guppy Cobra': FishParameters(22.0, 28.0, 7.0, 8.5, 2.0, 40.0), 
  'Tetra Neón Verde (Simulans)': FishParameters(23.0, 29.0, 4.0, 6.5, 1.0, 40.0), 
  'Tetra Luminoso (Hemigrammus erythrozonus)': FishParameters(24.0, 28.0, 6.0, 7.5, 1.0, 45.0),
  'Pez Lápiz (Nannostomus beckfordi)': FishParameters(24.0, 28.0, 5.5, 7.5, 1.0, 45.0), 
  'Tetra Limón': FishParameters(23.0, 28.0, 6.0, 7.5, 1.0, 50.0),
  'Gourami Pigmeo (Trichopsis pumila)': FishParameters(25.0, 28.0, 6.0, 7.0, 1.0, 30.0), 
  'Betta Imbellis (Betta Pacífico)': FishParameters(24.0, 28.0, 6.0, 7.5, 1.5, 30.0), 
  'Pseudomugil Furcatus (Ojos Azules)': FishParameters(24.0, 28.0, 7.0, 8.0, 1.0, 45.0),
  'Gobio Pavo Real (Tateurndina ocellicauda)': FishParameters(22.0, 26.0, 6.5, 7.5, 1.5, 40.0),
  'Killi Cabo Lopez (Aphyosemion australe)': FishParameters(21.0, 24.0, 6.0, 7.0, 1.5, 40.0),
  'Nube Blanca (Tanichthys albonubes)': FishParameters(15.0, 24.0, 6.0, 8.0, 1.0, 40.0),
  'Medaka (Oryzias latipes)': FishParameters(16.0, 28.0, 7.0, 8.0, 1.0, 30.0),
  'Multifasciatus (Neolamprologus multifasciatus)': FishParameters(24.0, 27.0, 7.5, 9.0, 2.5, 45.0),
  'Rasbora Esmeralda (Danio erythromicron)': FishParameters(21.0, 25.0, 7.0, 8.0, 1.5, 30.0),
  'Tetra Cobre (Hasemania nana)': FishParameters(22.0, 28.0, 6.0, 7.5, 1.0, 45.0),
  'Pez Hacha Mármol (Carnegiella strigata)': FishParameters(24.0, 28.0, 5.5, 7.0, 2.0, 45.0),
  'Corydora Habrosus': FishParameters(22.0, 26.0, 6.0, 7.5, 1.0, 35.0),
  'Gamba Amano (Caridina multidentata)': FishParameters(18.0, 28.0, 6.5, 8.0, 1.5, 20.0),
  'Gamba Crystal Red (Caridina cantonensis)': FishParameters(20.0, 24.0, 5.5, 6.8, 3.0, 20.0),
  'Crayfish Enano (Cambarellus patzcuarensis)': FishParameters(15.0, 25.0, 6.5, 8.0, 2.0, 40.0),

  // Default
  'Personalizado': FishParameters(24.0, 26.0, 6.5, 7.5, 2.0, 50.0),
};

class ConfigPage extends StatefulWidget {
  final String aquariumId;
  final Function(String, FishParameters) onFishSelected;
  final String currentFish;

  const ConfigPage({super.key, required this.onFishSelected, required this.currentFish, required this.aquariumId});

  @override
  State<ConfigPage> createState() => _ConfigPageState();
}

class _ConfigPageState extends State<ConfigPage> {
  String _selectedFish = 'Personalizado';

  late TextEditingController _minTempCtrl;
  late TextEditingController _maxTempCtrl;
  late TextEditingController _minPhCtrl;
  late TextEditingController _maxPhCtrl;
  late TextEditingController _aguaCtrl;
  double _nivelLuz = 2.0;

  late DocumentReference _configRef; // Referencia dinámica

  @override
  void initState() {
    super.initState();
    _configRef = FirebaseFirestore.instance
        .collection('acuarios')
        .doc(widget.aquariumId)
        .collection('data')
        .doc('config');

    FishParameters params = fishParameters['Personalizado']!;
    _setupControllers(params);
    _cargarDatosDeNube();
  }

  void _setupControllers(FishParameters params) {
    _minTempCtrl = TextEditingController(text: params.temperature_min.toString());
    _maxTempCtrl = TextEditingController(text: params.temperature_max.toString());
    _minPhCtrl = TextEditingController(text: params.ph_min.toString());
    _maxPhCtrl = TextEditingController(text: params.ph_max.toString());
    _aguaCtrl = TextEditingController(text: params.nivel_agua.toStringAsFixed(0));
    _nivelLuz = params.nivel_luz;
  }

  void _cargarDatosDeNube() async {
    try {
      DocumentSnapshot doc = await _configRef.get();
      if(doc.exists) {
        Map<String, dynamic> data = doc.data() as Map<String, dynamic>;
        setState(() {
          _selectedFish = data['nombre_pez'] ?? 'Personalizado';
          _minTempCtrl.text = (data['temp_min'] ?? 24.0).toString();
          _maxTempCtrl.text = (data['temp_max'] ?? 28.0).toString();
          _minPhCtrl.text = (data['ph_min'] ?? 6.5).toString();
          _maxPhCtrl.text = (data['ph_max'] ?? 7.5).toString();
          _aguaCtrl.text = (data['nivel_agua'] ?? 80).toString();

          int luzPorcentaje = data['nivel_luz'] ?? 50;
          if (luzPorcentaje <= 40) _nivelLuz = 1.0;
          else if (luzPorcentaje <= 70) _nivelLuz = 2.0;
          else _nivelLuz = 3.0;
        });
      }
    } catch (e) { print(e); }
  }

  void _loadFishData(String fishName) {
    if (fishParameters.containsKey(fishName)) {
      FishParameters params = fishParameters[fishName]!;
      setState(() {
        _selectedFish = fishName;
        _minTempCtrl.text = params.temperature_min.toString();
        _maxTempCtrl.text = params.temperature_max.toString();
        _minPhCtrl.text = params.ph_min.toString();
        _maxPhCtrl.text = params.ph_max.toString();
        _aguaCtrl.text = params.nivel_agua.toStringAsFixed(0);
        _nivelLuz = params.nivel_luz;
      });
    }
  }

  void _saveAndExit() async {
    double minT = double.tryParse(_minTempCtrl.text) ?? 24.0;
    double maxT = double.tryParse(_maxTempCtrl.text) ?? 28.0;
    double minPh = double.tryParse(_minPhCtrl.text) ?? 6.5;
    double maxPh = double.tryParse(_maxPhCtrl.text) ?? 7.5;
    double agua = double.tryParse(_aguaCtrl.text) ?? 80.0;

    int luzPorcentaje = 50;
    if (_nivelLuz == 1.0) luzPorcentaje = 30;
    if (_nivelLuz == 2.0) luzPorcentaje = 60;
    if (_nivelLuz == 3.0) luzPorcentaje = 100;

    await _configRef.update({
      'nombre_pez': _selectedFish,
      'temp_min': minT,
      'temp_max': maxT,
      'ph_min': minPh,
      'ph_max': maxPh,
      'nivel_agua': agua,
      'nivel_luz': luzPorcentaje,
      'ultima_modificacion': DateTime.now().toString()
    });

    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Configuración enviada')));
    Navigator.pop(context);
  }

  @override
  Widget build(BuildContext context) {
    List<String> dropdownItems = fishParameters.keys.toList();
    bool isEditable = _selectedFish == 'Personalizado';

    return Scaffold(
      appBar: AppBar(
        title: const Text('Configuración', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
        backgroundColor: Colors.transparent,
        elevation: 0,
        iconTheme: const IconThemeData(color: Colors.white),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _saveAndExit,
        backgroundColor: const Color(0xFFFF5400),
        icon: const Icon(Icons.cloud_upload, color: Colors.white),
        label: const Text("ENVIAR A PECERA", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)),
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.fromLTRB(20, 10, 20, 80),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text('Perfil del Acuario:', style: TextStyle(fontSize: 16, color: Colors.white70)),
            const SizedBox(height: 10),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
              decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(30), border: Border.all(color: const Color(0xFFFF5400), width: 3)),
              child: DropdownButtonHideUnderline(
                child: DropdownButton<String>(
                  dropdownColor: Colors.white, 
                  value: dropdownItems.contains(_selectedFish) ? _selectedFish : 'Personalizado',
                  isExpanded: true,
                  icon: const Icon(Icons.arrow_drop_down_circle, color: Color(0xFFFF5400)),
                  style: const TextStyle(fontSize: 18, color: Colors.black87, fontWeight: FontWeight.bold),
                  items: dropdownItems.map((String item) {
                    return DropdownMenuItem<String>(value: item, child: Text(item));
                  }).toList(),
                  onChanged: (val) => _loadFishData(val!),
                ),
              ),
            ),
            const SizedBox(height: 20),
            Container(
              padding: const EdgeInsets.all(20),
              decoration: BoxDecoration(color: Colors.black.withOpacity(0.3), borderRadius: BorderRadius.circular(20), border: Border.all(color: isEditable ? const Color(0xFFFF5400) : Colors.white12)),
              child: IgnorePointer(
                ignoring: !isEditable,
                child: Opacity(
                  opacity: isEditable ? 1.0 : 0.6,
                  child: Column(
                    children: [
                        _buildRangeInput(icon: Icons.thermostat, label: 'Temperatura (°C)', minCtrl: _minTempCtrl, maxCtrl: _maxTempCtrl, enabled: isEditable),
                        _buildRangeInput(icon: Icons.science, label: 'Rango de pH', minCtrl: _minPhCtrl, maxCtrl: _maxPhCtrl, enabled: isEditable),
                        _buildSingleInput(icon: Icons.waves, label: 'Nivel Agua %', ctrl: _aguaCtrl, enabled: isEditable),
                        const SizedBox(height: 15),
                        const Text('Nivel de Luz', style: TextStyle(color: Colors.white70)),
                        Slider(
                          value: _nivelLuz,
                          min: 1.0, max: 3.0, divisions: 2,
                          activeColor: const Color(0xFFFF5400),
                          inactiveColor: Colors.grey,
                          onChanged: isEditable ? (val) => setState(() => _nivelLuz = val) : null, 
                        ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildRangeInput({required IconData icon, required String label, required TextEditingController minCtrl, required TextEditingController maxCtrl, required bool enabled}) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 10.0),
      child: Row(
        children: [
          Icon(icon, color: Colors.white54),
          const SizedBox(width: 10),
          Expanded(child: Text(label, style: const TextStyle(color: Colors.white70))),
          SizedBox(width: 60, child: _buildTextField(minCtrl, enabled)),
          const Text(' - ', style: TextStyle(color: Colors.white)),
          SizedBox(width: 60, child: _buildTextField(maxCtrl, enabled)),
        ],
      ),
    );
  }

  Widget _buildSingleInput({required IconData icon, required String label, required TextEditingController ctrl, required bool enabled}) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 10.0),
      child: Row(
        children: [
          Icon(icon, color: Colors.white54),
          const SizedBox(width: 10),
          Expanded(child: Text(label, style: const TextStyle(color: Colors.white70))),
          SizedBox(width: 80, child: _buildTextField(ctrl, enabled)),
        ],
      ),
    );
  }

  Widget _buildTextField(TextEditingController controller, bool enabled) {
    return TextFormField(
      controller: controller,
      enabled: enabled,
      keyboardType: const TextInputType.numberWithOptions(decimal: true),
      style: const TextStyle(color: Colors.white),
      textAlign: TextAlign.center,
      decoration: InputDecoration(
        filled: true,
        fillColor: Colors.white10,
        contentPadding: const EdgeInsets.all(5),
        border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
      ),
    );
  }
}