import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class SpriteEffect {
  final Offset position;
  final String assetPath;
  final int frameCount;
  final double tileWidth;
  final double tileHeight;
  final double scale;
  
  ui.Image? image;
  int currentFrame = 0;
  bool isFinished = false;

  SpriteEffect({
    required this.position,
    required this.assetPath,
    required this.frameCount,
    this.tileWidth = 100.0,
    this.tileHeight = 100.0,
    this.scale = 2.0,
  });
}

class EffectSpriteWidget extends StatefulWidget {
  const EffectSpriteWidget({super.key});

  @override
  EffectSpriteWidgetState createState() => EffectSpriteWidgetState();
}

class EffectSpriteWidgetState extends State<EffectSpriteWidget>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  final List<SpriteEffect> _effects = [];
  final Map<String, ui.Image> _imageCache = {};

  @override
  void initState() {
    super.initState();
    // Approximately 10 FPS (100ms per frame)
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 100),
    );
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        _updateFrames();
        if (_effects.isNotEmpty) {
          _controller.forward(from: 0);
        }
      }
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  Future<void> playEffect({
    required Offset position,
    required String assetPath,
    required int frameCount,
    double tileWidth = 100.0,
    double tileHeight = 100.0,
    double scale = 2.0,
  }) async {
    final effect = SpriteEffect(
      position: position,
      assetPath: assetPath,
      frameCount: frameCount,
      tileWidth: tileWidth,
      tileHeight: tileHeight,
      scale: scale,
    );

    // Preload image if not cached
    if (!_imageCache.containsKey(assetPath)) {
      try {
        final ByteData data = await rootBundle.load(assetPath);
        final List<int> bytes = data.buffer.asUint8List();
        final Completer<ui.Image> completer = Completer();
        ui.decodeImageFromList(Uint8List.fromList(bytes), (ui.Image img) {
          completer.complete(img);
        });
        _imageCache[assetPath] = await completer.future;
      } catch (e) {
        debugPrint('Failed to load effect image $assetPath: $e');
        return;
      }
    }
    
    effect.image = _imageCache[assetPath];

    setState(() {
      _effects.add(effect);
      if (!_controller.isAnimating) {
        _controller.forward(from: 0);
      }
    });
  }

  void _updateFrames() {
    if (_effects.isEmpty) return;

    setState(() {
      for (var i = _effects.length - 1; i >= 0; i--) {
        final effect = _effects[i];
        effect.currentFrame++;
        if (effect.currentFrame >= effect.frameCount) {
          effect.isFinished = true;
          _effects.removeAt(i);
        }
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_effects.isEmpty) return const SizedBox.shrink();

    return IgnorePointer(
      child: CustomPaint(
        size: Size.infinite,
        painter: MultiSpriteEffectPainter(effects: _effects),
      ),
    );
  }
}

class MultiSpriteEffectPainter extends CustomPainter {
  final List<SpriteEffect> effects;

  MultiSpriteEffectPainter({required this.effects});

  @override
  void paint(Canvas canvas, Size size) {
    for (final effect in effects) {
      if (effect.image == null) continue;

      final double srcX = effect.currentFrame * effect.tileWidth;
      final double srcY = 0.0;
      final Rect src = Rect.fromLTWH(srcX, srcY, effect.tileWidth, effect.tileHeight);
      
      final double drawWidth = effect.tileWidth * effect.scale;
      final double drawHeight = effect.tileHeight * effect.scale;
      // Center the effect on the position
      final Rect dst = Rect.fromLTWH(
        effect.position.dx - drawWidth / 2,
        effect.position.dy - drawHeight / 2,
        drawWidth,
        drawHeight,
      );

      canvas.drawImageRect(
        effect.image!,
        src,
        dst,
        Paint()..filterQuality = FilterQuality.none,
      );
    }
  }

  @override
  bool shouldRepaint(covariant MultiSpriteEffectPainter oldDelegate) {
    return true; // Repaint constantly while animating
  }
}
