import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'isometric_transform.dart';

// =============================================================================
// LAYER 1: STATIC STRUCTURE PAINTER
// =============================================================================
/// Menggambar grid lantai, dinding transparan, dan barisan rak baglog jamur (balok 3D).
class StaticKumbungPainter extends CustomPainter {
  final IsometricTransform transformer;
  final double roomWidth;  // Dimensi Y (lebar) dalam meter
  final double roomLength; // Dimensi X (panjang) dalam meter
  final double roomHeight; // Dimensi Z (tinggi) dalam meter

  const StaticKumbungPainter({
    required this.transformer,
    this.roomWidth = 6.0,
    this.roomLength = 8.0,
    this.roomHeight = 2.5,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final gridPaint = Paint()
      ..color = Colors.grey.shade300
      ..strokeWidth = 1.0
      ..style = PaintingStyle.stroke;

    final wallPaint = Paint()
      ..color = Colors.blueGrey.withValues(alpha: 0.15)
      ..strokeWidth = 1.5
      ..style = PaintingStyle.stroke;

    // 1. Menggambar Grid Lantai (Z = 0)
    // Garis sepanjang X (Panjang)
    for (double y = 0; y <= roomWidth; y += 1.0) {
      final pStart = transformer.toScreen(0, y, 0);
      final pEnd = transformer.toScreen(roomLength, y, 0);
      canvas.drawLine(pStart, pEnd, gridPaint);
    }
    // Garis sepanjang Y (Lebar)
    for (double x = 0; x <= roomLength; x += 1.0) {
      final pStart = transformer.toScreen(x, 0, 0);
      final pEnd = transformer.toScreen(x, roomWidth, 0);
      canvas.drawLine(pStart, pEnd, gridPaint);
    }

    // 2. Menggambar Rak Jamur (3D Blocks)
    // Kita buat 4 baris rak jamur vertikal (sejajar sumbu X/Panjang)
    // Format rak: { xStart, xEnd, yStart, yEnd, zHeight }
    final raks = [
      _Rak3D(xStart: 1.0, xEnd: 7.0, yStart: 0.6, yEnd: 1.2, height: 1.8),
      _Rak3D(xStart: 1.0, xEnd: 7.0, yStart: 1.9, yEnd: 2.5, height: 1.8),
      _Rak3D(xStart: 1.0, xEnd: 7.0, yStart: 3.5, yEnd: 4.1, height: 1.8),
      _Rak3D(xStart: 1.0, xEnd: 7.0, yStart: 4.8, yEnd: 5.4, height: 1.8),
    ];

    for (final rak in raks) {
      _drawRak(canvas, rak);
    }

    // 3. Menggambar Outline Dinding Kumbung (Wireframe Box)
    // Sudut-sudut dasar lantai
    final p000 = transformer.toScreen(0, 0, 0);
    final pL00 = transformer.toScreen(roomLength, 0, 0);
    final p0W0 = transformer.toScreen(0, roomWidth, 0);
    final pLW0 = transformer.toScreen(roomLength, roomWidth, 0);

    // Sudut-sudut atap (Z = roomHeight)
    final p00H = transformer.toScreen(0, 0, roomHeight);
    final pL0H = transformer.toScreen(roomLength, 0, roomHeight);
    final p0WH = transformer.toScreen(0, roomWidth, roomHeight);
    final pLWH = transformer.toScreen(roomLength, roomWidth, roomHeight);

    // Gambar tiang vertikal sudut
    canvas.drawLine(p000, p00H, wallPaint);
    canvas.drawLine(pL00, pL0H, wallPaint);
    canvas.drawLine(p0W0, p0WH, wallPaint);
    canvas.drawLine(pLW0, pLWH, wallPaint);

    // Gambar garis atap
    final roofPath = Path()
      ..moveTo(p00H.dx, p00H.dy)
      ..lineTo(pL0H.dx, pL0H.dy)
      ..lineTo(pLWH.dx, pLWH.dy)
      ..lineTo(p0WH.dx, p0WH.dy)
      ..close();
    canvas.drawPath(roofPath, wallPaint);

    // Gambar garis dasar (jika tertutup)
    final floorPath = Path()
      ..moveTo(p000.dx, p000.dy)
      ..lineTo(pL00.dx, pL00.dy)
      ..lineTo(pLW0.dx, pLW0.dy)
      ..lineTo(p0W0.dx, p0W0.dy)
      ..close();
    canvas.drawPath(floorPath, wallPaint);
  }

  /// Menggambar balok 3D isometrik untuk rak jamur
  void _drawRak(Canvas canvas, _Rak3D rak) {
    final fillPaint = Paint()
      ..color = Colors.teal.withValues(alpha: 0.12)
      ..style = PaintingStyle.fill;

    final borderPaint = Paint()
      ..color = Colors.teal.withValues(alpha: 0.4)
      ..strokeWidth = 1.0
      ..style = PaintingStyle.stroke;

    // Titik-titik sudut bawah (Z = 0)
    final p10 = transformer.toScreen(rak.xEnd, rak.yStart, 0);
    final p01 = transformer.toScreen(rak.xStart, rak.yEnd, 0);
    final p11 = transformer.toScreen(rak.xEnd, rak.yEnd, 0);

    // Titik-titik sudut atas (Z = height)
    final p00H = transformer.toScreen(rak.xStart, rak.yStart, rak.height);
    final p10H = transformer.toScreen(rak.xEnd, rak.yStart, rak.height);
    final p01H = transformer.toScreen(rak.xStart, rak.yEnd, rak.height);
    final p11H = transformer.toScreen(rak.xEnd, rak.yEnd, rak.height);

    // 1. Gambar sisi kanan (X-Right Face)
    final rightFace = Path()
      ..moveTo(p10.dx, p10.dy)
      ..lineTo(p11.dx, p11.dy)
      ..lineTo(p11H.dx, p11H.dy)
      ..lineTo(p10H.dx, p10H.dy)
      ..close();
    canvas.drawPath(rightFace, fillPaint);
    canvas.drawPath(rightFace, borderPaint);

    // 2. Gambar sisi kiri (Y-Left Face)
    final leftFace = Path()
      ..moveTo(p01.dx, p01.dy)
      ..lineTo(p11.dx, p11.dy)
      ..lineTo(p11H.dx, p11H.dy)
      ..lineTo(p01H.dx, p01H.dy)
      ..close();
    canvas.drawPath(leftFace, fillPaint);
    canvas.drawPath(leftFace, borderPaint);

    // 3. Gambar sisi atas (Top Face)
    final topFace = Path()
      ..moveTo(p00H.dx, p00H.dy)
      ..lineTo(p10H.dx, p10H.dy)
      ..lineTo(p11H.dx, p11H.dy)
      ..lineTo(p01H.dx, p01H.dy)
      ..close();
    canvas.drawPath(topFace, fillPaint..color = Colors.teal.withValues(alpha: 0.18));
    canvas.drawPath(topFace, borderPaint);
  }

  @override
  bool shouldRepaint(covariant StaticKumbungPainter oldDelegate) {
    return oldDelegate.roomWidth != roomWidth ||
        oldDelegate.roomLength != roomLength ||
        oldDelegate.roomHeight != roomHeight ||
        oldDelegate.transformer.scale != transformer.scale ||
        oldDelegate.transformer.origin != transformer.origin;
  }
}

class _Rak3D {
  final double xStart;
  final double xEnd;
  final double yStart;
  final double yEnd;
  final double height;
  _Rak3D({required this.xStart, required this.xEnd, required this.yStart, required this.yEnd, required this.height});
}


// =============================================================================
// LAYER 2: HEATMAP PAINTER
// =============================================================================
/// Menggambar lingkaran gradien heatmap isometrik (elips) di sekitar posisi sensor di atas lantai.
class HeatmapKumbungPainter extends CustomPainter {
  final IsometricTransform transformer;
  final Offset sensorPos; // Koordinat X dan Y sensor (meter)
  final double radius;    // Radius cakupan dalam meter
  final double value;     // Nilai aktual sensor (suhu atau kelembapan)
  final bool isSuhuMode;  // true untuk Suhu, false untuk Kelembapan

  const HeatmapKumbungPainter({
    required this.transformer,
    required this.sensorPos,
    required this.radius,
    required this.value,
    required this.isSuhuMode,
  });

  @override
  void paint(Canvas canvas, Size size) {
    if (radius <= 0) return;

    // Titik pusat lingkaran di ground (Z = 0) dalam koordinat piksel
    final center = transformer.toScreen(sensorPos.dx, sensorPos.dy, 0);

    // Radius dalam proyeksi isometrik (elips)
    // Sumbu horizontal (Major Axis): radius * scale
    // Sumbu vertikal (Minor Axis): radius * scale * sin(30) -> sin(30) = 0.5
    final double radiusX = radius * transformer.scale * math.cos(30.0 * math.pi / 180.0);
    final double radiusY = radius * transformer.scale * math.sin(30.0 * math.pi / 180.0);

    // Tentukan warna dasar heatmap (tanpa opacity bawaan agar pencampuran warna gradien lebih terkontrol)
    Color baseColor;
    if (isSuhuMode) {
      // Peta Suhu: Dingin (< 20°C) = Electric Blue, Optimal (20 - 33°C) = Neon Green, Panas (> 33°C) = Neon Orange ke Red
      if (value < 20) {
        baseColor = const Color(0xFF2979FF); // Electric Blue
      } else if (value <= 33) {
        baseColor = const Color(0xFF00E676); // Neon Green
      } else {
        // Semakin panas semakin merah menyala
        final factor = ((value - 33) / 10).clamp(0.0, 1.0);
        baseColor = Color.lerp(const Color(0xFFFFA000), const Color(0xFFFF1744), factor)!;
      }
    } else {
      // Peta Kelembapan: Kering (< 60%) = Kuning/Oranye, Cukup (60 - 80%) = Neon Green, Basah/Optimal (> 80%) = Electric Cyan
      if (value < 60) {
        baseColor = const Color(0xFFFF9100); // Vibrant Orange
      } else if (value < 80) {
        baseColor = const Color(0xFF00E676); // Neon Green
      } else {
        baseColor = const Color(0xFF00E5FF); // Electric Cyan
      }
    }

    final paint = Paint()
      ..shader = RadialGradient(
        colors: [
          baseColor.withValues(alpha: 0.70), // Center: strong glow
          baseColor.withValues(alpha: 0.45), // Mid: visible effect area
          baseColor.withValues(alpha: 0.15), // Edge: soft fade
          Colors.transparent,
        ],
        stops: const [0.0, 0.5, 0.85, 1.0],
      ).createShader(Rect.fromLTRB(
        center.dx - radiusX,
        center.dy - radiusY,
        center.dx + radiusX,
        center.dy + radiusY,
      ));

    // Gambar elips heatmap isometrik
    canvas.drawOval(
      Rect.fromCenter(center: center, width: radiusX * 2, height: radiusY * 2),
      paint,
    );
  }

  @override
  bool shouldRepaint(covariant HeatmapKumbungPainter oldDelegate) {
    return oldDelegate.sensorPos != sensorPos ||
        oldDelegate.radius != radius ||
        oldDelegate.value != value ||
        oldDelegate.isSuhuMode != isSuhuMode ||
        oldDelegate.transformer.scale != transformer.scale ||
        oldDelegate.transformer.origin != transformer.origin;
  }
}


// =============================================================================
// LAYER 3: DYNAMIC SENSOR PIN & TAG PAINTER
// =============================================================================
/// Menggambar penanda pin sensor melayang (Z = height), garis bantu vertikal ke ground,
/// lingkaran pulsing glow, serta tag nilai real-time di atas pin.
class PinKumbungPainter extends CustomPainter {
  final IsometricTransform transformer;
  final Offset sensorPos; // X, Y koordinat sensor (meter)
  final double sensorHeight; // Z (tinggi sensor) melayang dalam meter
  final double value;     // Nilai sensor yang ditampilkan
  final String unit;      // Satuan nilai (contoh: "°C" atau "%")
  final double animationValue; // Nilai animasi 0.0 s.d 1.0 untuk denyut pulsing
  final Color pinColor;   // Warna pin (menyesuaikan status)

  const PinKumbungPainter({
    required this.transformer,
    required this.sensorPos,
    required this.sensorHeight,
    required this.value,
    required this.unit,
    required this.animationValue,
    required this.pinColor,
  });

  @override
  void paint(Canvas canvas, Size size) {
    // 1. Koordinat Piksel Ground (Z = 0) dan Koordinat Floating (Z = sensorHeight)
    final pGround = transformer.toScreen(sensorPos.dx, sensorPos.dy, 0);
    final pFloat = transformer.toScreen(sensorPos.dx, sensorPos.dy, sensorHeight);

    // 2. Menggambar Garis Bantu Vertikal (Dotted/Dashed Line)
    final linePaint = Paint()
      ..color = pinColor.withValues(alpha: 0.5)
      ..strokeWidth = 1.2
      ..style = PaintingStyle.stroke;

    _drawDashedLine(canvas, pGround, pFloat, linePaint);

    // 3. Menggambar Lingkaran Pulsing Glow di Lantai (Z = 0)
    final double pulseMaxRadius = 24.0;
    final double pulseRadius = pulseMaxRadius * animationValue;
    final double pulseOpacity = (1.0 - animationValue).clamp(0.0, 1.0);
    final pulsePaint = Paint()
      ..color = pinColor.withValues(alpha: pulseOpacity * 0.4)
      ..style = PaintingStyle.fill;
    
    // Proyeksi isometrik elips untuk pulsing
    final double prX = pulseRadius;
    final double prY = pulseRadius * math.sin(30.0 * math.pi / 180.0);
    canvas.drawOval(
      Rect.fromCenter(center: pGround, width: prX * 2, height: prY * 2),
      pulsePaint,
    );

    // 4. Menggambar Pin Sensor Melayang (Floating Pin)
    // Efek mengambang naik-turun menggunakan nilai animasi
    final double floatOffset = 6.0 * math.sin(animationValue * 2 * math.pi);
    final pFloatingPin = Offset(pFloat.dx, pFloat.dy + floatOffset);

    // Lingkaran pin inti
    final corePaint = Paint()
      ..color = pinColor
      ..style = PaintingStyle.fill;
    final borderPaint = Paint()
      ..color = Colors.white
      ..strokeWidth = 2.0
      ..style = PaintingStyle.stroke;

    canvas.drawCircle(pFloatingPin, 8.0, corePaint);
    canvas.drawCircle(pFloatingPin, 8.0, borderPaint);

    // 5. Menggambar Tag Pembacaan Nilai (Tooltip Tag) di atas Pin
    final tagText = '${value.toStringAsFixed(1)}$unit';
    _drawTooltipTag(canvas, pFloatingPin - const Offset(0, 14), tagText);
  }

  /// Helper untuk menggambar dashed line
  void _drawDashedLine(Canvas canvas, Offset p1, Offset p2, Paint paint) {
    const double dashHeight = 4.0;
    const double dashGap = 3.0;
    double startY = p1.dy;
    double endY = p2.dy;

    // Arah vertical naik (dy mengecil)
    if (startY < endY) {
      final temp = startY;
      startY = endY;
      endY = temp;
    }

    double currentY = startY;
    while (currentY > endY) {
      final double nextY = math.max(currentY - dashHeight, endY);
      canvas.drawLine(Offset(p1.dx, currentY), Offset(p1.dx, nextY), paint);
      currentY = nextY - dashGap;
    }
  }

  /// Helper untuk menggambar text tag box (tooltip) di atas pin sensor
  void _drawTooltipTag(Canvas canvas, Offset position, String text) {
    final textPainter = TextPainter(
      text: TextSpan(
        text: text,
        style: const TextStyle(
          color: Colors.white,
          fontSize: 10.0,
          fontWeight: FontWeight.bold,
        ),
      ),
      textDirection: TextDirection.ltr,
    )..layout();

    final double paddingX = 8.0;
    final double paddingY = 4.0;
    final double width = textPainter.width + paddingX * 2;
    final double height = textPainter.height + paddingY * 2;

    final rect = Rect.fromCenter(center: position, width: width, height: height);
    final rrect = RRect.fromRectAndRadius(rect, const Radius.circular(6.0));

    // Paint background tag box (semacam pill box gelap)
    final bgPaint = Paint()
      ..color = Colors.black.withValues(alpha: 0.7)
      ..style = PaintingStyle.fill;
    canvas.drawRRect(rrect, bgPaint);

    // Gambar teks di tengah box
    textPainter.paint(
      canvas,
      Offset(position.dx - textPainter.width / 2, position.dy - textPainter.height / 2),
    );
  }

  @override
  bool shouldRepaint(covariant PinKumbungPainter oldDelegate) {
    return oldDelegate.sensorPos != sensorPos ||
        oldDelegate.sensorHeight != sensorHeight ||
        oldDelegate.value != value ||
        oldDelegate.unit != unit ||
        oldDelegate.animationValue != animationValue ||
        oldDelegate.pinColor != pinColor ||
        oldDelegate.transformer.scale != transformer.scale ||
        oldDelegate.transformer.origin != transformer.origin;
  }
}
