import 'dart:async';

import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/services.dart';
import 'package:sense_the_rhythm/models/arrow_direction.dart';
import 'package:sense_the_rhythm/models/input_direction.dart';
import 'package:sense_the_rhythm/models/note.dart';
import 'package:sense_the_rhythm/utils/esense_input.dart';
import 'package:sense_the_rhythm/utils/simfile.dart';
import 'package:sense_the_rhythm/widgets/arrows.dart';
import 'package:sense_the_rhythm/screens/game_over.dart';

class Level extends StatefulWidget {
  const Level(this.simfile, {super.key});
  final Simfile simfile;

  @override
  State<Level> createState() => _LevelState();
}

class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
  final player = AudioPlayer();
  bool _isPlaying = true;
  Duration? _duration;
  Duration? _position;

  StreamSubscription? _durationSubscription;
  StreamSubscription? _positionSubscription;
  StreamSubscription? _buttonSubscription;

  final FocusNode _focusNode = FocusNode();
  InputDirection inputDirection = InputDirection();

  String hitOrMissMessage = 'Play!';

  List<Note> notes = [];

  late AnimationController _animationController;
  late Animation<double> _animation;

  @override
  void setState(VoidCallback fn) {
    // Subscriptions only can be closed asynchronously,
    // therefore events can occur after widget has been disposed.
    if (mounted) {
      super.setState(fn);
    }
  }

  @override
  void initState() {
    super.initState();
    ESenseInput.instance.resetAngles();

    _animationController = AnimationController(
      vsync: this,
      duration: Duration(seconds: 2),
    );
    _animation =
        Tween<double>(begin: 1.0, end: 0.0).animate(_animationController);
    _animationController.forward();

    // Use initial values from player
    player.getDuration().then(
          (value) => setState(() {
            _duration = value;
          }),
        );
    player.getCurrentPosition().then(
          (value) => setState(() {
            _position = value;
          }),
        );

    // listen for new values from player
    _durationSubscription =
        player.onDurationChanged.listen((Duration duration) {
      setState(() => _duration = duration);
    });

    _positionSubscription =
        player.onPositionChanged.listen((Duration position) {
      setState(() => _position = position);
      for (final note in notes) {
        _noteHitCheck(note, position);
      }
    });

    player.onPlayerComplete.listen((void _) {
      Route route = MaterialPageRoute(
          builder: (context) => GameOverStats(
                simfile: widget.simfile,
                notes: notes,
              ));
      Navigator.pushReplacement(context, route);
    });

    // listen for esense button and pause/resume
    if (ESenseInput.instance.connected) {
      _buttonSubscription = ESenseInput.instance.buttonEvents().listen((event) {
        if (!event.pressed) {
          _pauseResume();
        }
      });
    }

    widget.simfile.chartSimplest?.beats.forEach((time, noteData) {
      int arrowIndex = noteData.indexOf('1');
      if (arrowIndex < 0 || arrowIndex > 3) {
        return;
      }
      notes.add(Note(time: time, direction: ArrowDirection.values[arrowIndex]));
    });

    player.play(DeviceFileSource(widget.simfile.audioPath!));
  }

  @override
  void dispose() {
    _animationController.dispose();
    _durationSubscription?.cancel();
    _positionSubscription?.cancel();
    _buttonSubscription?.cancel();
    player.dispose();
    super.dispose();
  }

  void _pauseResume() {
    if (_isPlaying) {
      player.pause();
      setState(() {
        _isPlaying = false;
      });
    } else {
      player.resume();
      setState(() {
        _isPlaying = true;
      });
    }
  }

  void _noteHitCheck(Note note, Duration time) {
    note.position = note.time - time.inMilliseconds / 60000.0;
    if (note.wasHit != null) {
      return;
    }
    if (note.position.abs() < 0.5 * 1.0 / 60.0) {
      InputDirection esenseDirection =
          ESenseInput.instance.getInputDirection(note.direction);
      inputDirection.up |= esenseDirection.up;
      inputDirection.down |= esenseDirection.down;
      inputDirection.left |= esenseDirection.left;
      inputDirection.right |= esenseDirection.right;
      bool keypressCorrect = false;
      switch (note.direction) {
        case ArrowDirection.up:
          keypressCorrect = inputDirection.up;
          break;
        case ArrowDirection.down:
          keypressCorrect = inputDirection.down;
          break;
        case ArrowDirection.right:
          keypressCorrect = inputDirection.right;
          break;
        case ArrowDirection.left:
          keypressCorrect = inputDirection.left;
          break;
      }
      if (keypressCorrect) {
        print("you hit!");
        note.wasHit = true;
        _animationController.reset();
        _animationController.forward();
        inputDirection.reset();
        setState(() {
          hitOrMissMessage = 'Great!';
        });
      }
    } else if (note.position < -0.5 * 1.0 / 60.0) {
      print("Missed");
      note.wasHit = false;
      _animationController.reset();
      _animationController.forward();
      inputDirection.reset();
      setState(() {
        hitOrMissMessage = 'Missed';
      });
    }
  }

  void _keyboardHandler(event) {
    bool isDown = false;
    if (event is KeyDownEvent) {
      isDown = true;
    } else if (event is KeyUpEvent) {
      isDown = false;
    } else {
      return;
    }
    switch (event.logicalKey) {
      case LogicalKeyboardKey.arrowUp:
        inputDirection.up = isDown;
        break;
      case LogicalKeyboardKey.arrowDown:
        inputDirection.down = isDown;
        break;
      case LogicalKeyboardKey.arrowLeft:
        inputDirection.left = isDown;
        break;
      case LogicalKeyboardKey.arrowRight:
        inputDirection.right = isDown;
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return KeyboardListener(
      focusNode: _focusNode,
      autofocus: true,
      onKeyEvent: _keyboardHandler,
      child: Scaffold(
          appBar: AppBar(
            leading: IconButton(
              icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow),
              onPressed: _pauseResume,
            ),
            title: Text(widget.simfile.tags['TITLE']!),
            actions: [
              IconButton(
                  icon: Icon(Icons.close),
                  onPressed: () => Navigator.pop(context))
            ],
            bottom: PreferredSize(
                preferredSize: Size(double.infinity, 1.0),
                child: LinearProgressIndicator(
                  value: (_duration != null &&
                          _position != null &&
                          _position!.inMilliseconds > 0 &&
                          _position!.inMilliseconds < _duration!.inMilliseconds)
                      ? _position!.inMilliseconds / _duration!.inMilliseconds
                      : 0.0,
                )),
          ),
          body: Stack(children: [
            Arrows(
                notes: notes,
                position: _position != null
                    ? _position!.inMilliseconds.toDouble()
                    : 0.0),
            Positioned(
              top: 50,
              width: MediaQuery.of(context).size.width,
              left: 0,
              child: FadeTransition(
                opacity: _animation,
                child: Text(
                  hitOrMissMessage,
                  textScaler: TextScaler.linear(4),
                  textAlign: TextAlign.center,
                ),
              ),
            ),
            Positioned(
              left: MediaQuery.of(context).size.width / 2 - 50,
              bottom: 50,
              child: Container(
                width: 100,
                height: 100,
                decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    border: Border.all(color: Colors.black, width: 10)),
              ),
            ),
          ])),
    );
  }
}
