import 'package:flutter/material.dart'; import 'package:intaleq_maps/intaleq_maps.dart'; import '../models/angle_unit.dart'; import '../models/military_operations_models.dart'; import '../services/artillery_ballistics_engine.dart'; import '../services/military_grid_utils.dart'; /// Dedicated Military Artillery Fire Mission HUD Sheet class TacticalArtillerySheet extends StatefulWidget { final LatLng? gunPosition; final LatLng? targetPosition; final AngleUnit angleUnit; final VoidCallback onPickGun; final VoidCallback onPickTarget; final VoidCallback onSwap; final VoidCallback onClose; final Function(ArtilleryFiringSolution) onSolutionCalculated; const TacticalArtillerySheet({ super.key, required this.gunPosition, required this.targetPosition, required this.angleUnit, required this.onPickGun, required this.onPickTarget, required this.onSwap, required this.onClose, required this.onSolutionCalculated, }); @override State createState() => _TacticalArtillerySheetState(); } class _TacticalArtillerySheetState extends State { ArtilleryWeaponSystem _selectedWeapon = ArtilleryWeaponSystem.standardSystems[0]; bool _highAngle = false; bool _isLoading = false; ArtilleryFiringSolution? _solution; @override void initState() { super.initState(); _recalculate(); } @override void didUpdateWidget(covariant TacticalArtillerySheet oldWidget) { super.didUpdateWidget(oldWidget); if (widget.gunPosition != oldWidget.gunPosition || widget.targetPosition != oldWidget.targetPosition) { _recalculate(); } } Future _recalculate() async { if (widget.gunPosition == null || widget.targetPosition == null) { setState(() => _solution = null); return; } setState(() => _isLoading = true); try { final sol = await ArtilleryBallisticsEngine.calculateFireMission( weapon: _selectedWeapon, gunPos: widget.gunPosition!, targetPos: widget.targetPosition!, highAngle: _highAngle, ); if (mounted) { setState(() { _solution = sol; _isLoading = false; }); widget.onSolutionCalculated(sol); } } catch (e) { if (mounted) setState(() => _isLoading = false); } } @override Widget build(BuildContext context) { return Container( decoration: const BoxDecoration( color: Color(0xFF090E1A), borderRadius: BorderRadius.vertical(top: Radius.circular(20)), border: Border(top: BorderSide(color: Color(0xFF0071E3), width: 1.5)), boxShadow: [ BoxShadow(color: Colors.black87, blurRadius: 24, spreadRadius: 4), ], ), child: SafeArea( top: false, child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // ── Header ─────────────────────────────────────────── Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: const Color(0xFF0071E3).withAlpha(40), borderRadius: BorderRadius.circular(10), border: Border.all(color: const Color(0xFF0071E3)), ), child: const Icon(Icons.gps_fixed, color: Color(0xFF38BDF8), size: 20), ), const SizedBox(width: 10), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'حساب رماية المدفعية وقوس القذيفة (Fire Mission)', style: TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold, ), ), Text( 'حل الرماية البالستية مع فحص قمم الجبال والعوائق التضاريسية', style: TextStyle(color: Color(0xFF94A3B8), fontSize: 10), ), ], ), ), IconButton( icon: const Icon(Icons.close, color: Colors.white70, size: 20), onPressed: widget.onClose, ), ], ), const SizedBox(height: 14), // ── Weapon Selector ─────────────────────────────────── Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0F172A), borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.white12), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: _selectedWeapon, dropdownColor: const Color(0xFF0F172A), isExpanded: true, icon: const Icon(Icons.keyboard_arrow_down, color: Color(0xFF38BDF8)), items: ArtilleryWeaponSystem.standardSystems.map((w) { return DropdownMenuItem( value: w, child: Text( '${w.nameAr} • مدى ${w.maxRangeMeters ~/ 1000} كم', style: const TextStyle(color: Colors.white, fontSize: 12), ), ); }).toList(), onChanged: (w) { if (w != null) { setState(() => _selectedWeapon = w); _recalculate(); } }, ), ), ), const SizedBox(height: 8), // ── High Angle / Low Angle Toggle ──────────────────── Container( decoration: BoxDecoration( color: const Color(0xFF0F172A), borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.white12), ), child: SwitchListTile( value: _highAngle, dense: true, activeTrackColor: const Color(0xFFEF4444), activeThumbColor: Colors.white, title: const Text( 'قوس الرماية العالي (High-Angle Fire)', style: TextStyle(color: Colors.white, fontSize: 11.5, fontWeight: FontWeight.bold), ), subtitle: const Text( 'تجاوز الجبال الشاهقة والضرب في الوديان العميقة', style: TextStyle(color: Color(0xFF94A3B8), fontSize: 9.5), ), onChanged: (v) { setState(() => _highAngle = v); _recalculate(); }, ), ), const SizedBox(height: 12), // ── Positions Picker Card ───────────────────────────── Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFF0F172A), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white12), ), child: Row( children: [ Expanded( child: InkWell( onTap: widget.onPickGun, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: widget.gunPosition != null ? const Color(0x330071E3) : Colors.white.withAlpha(10), borderRadius: BorderRadius.circular(8), border: Border.all( color: widget.gunPosition != null ? const Color(0xFF0071E3) : Colors.white24, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.adjust, color: Color(0xFF38BDF8), size: 14), SizedBox(width: 4), Text( 'مربض المدفعية (A):', style: TextStyle(color: Color(0xFF94A3B8), fontSize: 10), ), ], ), const SizedBox(height: 4), Text( widget.gunPosition != null ? MilitaryGridUtils.latLngToMgrs( widget.gunPosition!.latitude, widget.gunPosition!.longitude, ) : 'حدد المربض...', style: TextStyle( color: widget.gunPosition != null ? Colors.white : Colors.white54, fontSize: 11, fontFamily: 'monospace', fontWeight: FontWeight.bold, ), ), ], ), ), ), ), IconButton( icon: const Icon(Icons.swap_horiz, color: Color(0xFF38BDF8), size: 20), onPressed: widget.onSwap, ), Expanded( child: InkWell( onTap: widget.onPickTarget, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: widget.targetPosition != null ? const Color(0x33EF4444) : Colors.white.withAlpha(10), borderRadius: BorderRadius.circular(8), border: Border.all( color: widget.targetPosition != null ? const Color(0xFFEF4444) : Colors.white24, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.track_changes, color: Color(0xFFEF4444), size: 14), SizedBox(width: 4), Text( 'الهدف المعادي (B):', style: TextStyle(color: Color(0xFF94A3B8), fontSize: 10), ), ], ), const SizedBox(height: 4), Text( widget.targetPosition != null ? MilitaryGridUtils.latLngToMgrs( widget.targetPosition!.latitude, widget.targetPosition!.longitude, ) : 'حدد الهدف...', style: TextStyle( color: widget.targetPosition != null ? Colors.white : Colors.white54, fontSize: 11, fontFamily: 'monospace', fontWeight: FontWeight.bold, ), ), ], ), ), ), ), ], ), ), const SizedBox(height: 12), // ── Ballistic Solution Output ───────────────────────── if (_isLoading) const Center( child: Padding( padding: EdgeInsets.all(16), child: CircularProgressIndicator(color: Color(0xFF0071E3)), ), ) else if (_solution != null) ...[ // Ballistic Trajectory Mini Graph Container( height: 120, padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: const Color(0xFF0F172A), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white12), ), child: CustomPaint( painter: _BallisticProfilePainter(solution: _solution!), child: Container(), ), ), const SizedBox(height: 12), // Metrics Grid Row( children: [ _buildMetricCard( title: 'السمت التكتيكي (Azimuth)', value: '${_solution!.azimuthMilsNato.toInt()} ₥', subvalue: '${_solution!.azimuthDeg.toStringAsFixed(1)}°', color: const Color(0xFF38BDF8), ), const SizedBox(width: 8), _buildMetricCard( title: 'زاوية الارتفاع (QE)', value: '${_solution!.quadrantElevationMilsNato.toInt()} ₥', subvalue: '${_solution!.quadrantElevationDeg.toStringAsFixed(1)}°', color: const Color(0xFFF59E0B), ), ], ), const SizedBox(height: 8), Row( children: [ _buildMetricCard( title: 'المسافة المباشرة', value: '${(_solution!.distanceMeters / 1000).toStringAsFixed(2)} كم', subvalue: '${_solution!.distanceMeters.toInt()} متر', color: Colors.white, ), const SizedBox(width: 8), _buildMetricCard( title: 'زمن الطيران / الذروة', value: '${_solution!.timeOfFlightSeconds.toStringAsFixed(1)} ثانية', subvalue: 'الذروة: ${_solution!.apogeeAltitudeMeters.toInt()}م', color: const Color(0xFF10B981), ), ], ), const SizedBox(height: 10), // Crest Clearance Status Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: _solution!.isCrestClear ? const Color(0x3310B981) : const Color(0x33EF4444), borderRadius: BorderRadius.circular(8), border: Border.all( color: _solution!.isCrestClear ? const Color(0xFF10B981) : const Color(0xFFEF4444), ), ), child: Row( children: [ Icon( _solution!.isCrestClear ? Icons.check_circle : Icons.warning, color: _solution!.isCrestClear ? const Color(0xFF10B981) : const Color(0xFFEF4444), size: 16, ), const SizedBox(width: 8), Expanded( child: Text( _solution!.isCrestClear ? 'مسار القذيفة آمن ويعلو قمم الجبال بـ ${_solution!.minCrestClearanceMeters.toInt()}م' : 'تحذير: القذيفة تصطدم بقمة جبل عائقة في مسار الرماية!', style: TextStyle( color: _solution!.isCrestClear ? const Color(0xFF10B981) : const Color(0xFFEF4444), fontSize: 11, fontWeight: FontWeight.bold, ), ), ), ], ), ), ], ], ), ), ), ); } Widget _buildMetricCard({ required String title, required String value, required String subvalue, required Color color, }) { return Expanded( child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: const Color(0xFF0F172A), borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.white12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle(color: Color(0xFF94A3B8), fontSize: 9.5)), const SizedBox(height: 3), Text( value, style: TextStyle(color: color, fontSize: 14, fontWeight: FontWeight.bold), ), Text(subvalue, style: const TextStyle(color: Colors.white54, fontSize: 9.5)), ], ), ), ); } } /// Canvas Painter for Artillery Ballistic Arc Profile class _BallisticProfilePainter extends CustomPainter { final ArtilleryFiringSolution solution; _BallisticProfilePainter({required this.solution}); @override void paint(Canvas canvas, Size size) { if (solution.trajectoryProfile.isEmpty) return; double minAlt = double.infinity; double maxAlt = -double.infinity; for (final p in solution.trajectoryProfile) { if (p.groundElevationMeters < minAlt) minAlt = p.groundElevationMeters; if (p.altitudeMeters > maxAlt) maxAlt = p.altitudeMeters; } final altRange = (maxAlt - minAlt).clamp(50.0, 10000.0); final terrainPath = Path(); final arcPath = Path(); for (int i = 0; i < solution.trajectoryProfile.length; i++) { final p = solution.trajectoryProfile[i]; final x = (i / (solution.trajectoryProfile.length - 1)) * size.width; final terrainY = size.height - ((p.groundElevationMeters - minAlt) / altRange * (size.height - 20)) - 10; final arcY = size.height - ((p.altitudeMeters - minAlt) / altRange * (size.height - 20)) - 10; if (i == 0) { terrainPath.moveTo(x, terrainY); arcPath.moveTo(x, arcY); } else { terrainPath.lineTo(x, terrainY); arcPath.lineTo(x, arcY); } } // Draw Terrain Fill final terrainFill = Path.from(terrainPath) ..lineTo(size.width, size.height) ..lineTo(0, size.height) ..close(); final terrainPaint = Paint() ..shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [const Color(0xFF475569).withAlpha(120), const Color(0xFF1E293B).withAlpha(40)], ).createShader(Rect.fromLTWH(0, 0, size.width, size.height)); canvas.drawPath(terrainFill, terrainPaint); final terrainLinePaint = Paint() ..color = const Color(0xFF94A3B8) ..strokeWidth = 1.5 ..style = PaintingStyle.stroke; canvas.drawPath(terrainPath, terrainLinePaint); // Draw Ballistic Arc final arcPaint = Paint() ..color = solution.isCrestClear ? const Color(0xFF00F0FF) : const Color(0xFFEF4444) ..strokeWidth = 2.0 ..style = PaintingStyle.stroke; canvas.drawPath(arcPath, arcPaint); } @override bool shouldRepaint(covariant _BallisticProfilePainter oldDelegate) => true; }