// Copyright 2022, the Flutter project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:flutter/widgets.dart';
import 'package:logging/logging.dart';
import 'package:puzzleweave/settings/settings_controller.dart';
import 'package:jc_audio_player/jc_audio_player.dart';

enum SfxType { drop, click, tap, pop, appear, alert, star, success, flip, card, cardShort, panstart, panend, panend2 }

//AudioLogger.logLevel = AudioLogLevel.info;

/// Allows playing music and sound. A facade to `package:audioplayers`.
class JcAudioController {
  static final _log = Logger('JcAudioController');

  SettingsController? _settings;

  ValueNotifier<AppLifecycleState>? _lifecycleNotifier;

  final JcAudioPlayer _audioPlayer = JcAudioPlayer();

  JcAudioController() {
    _audioPlayer.addMusic(1, 'assets/audio/bgm/canon.mp3');

    _audioPlayer.addSound(SfxType.drop.index, 'assets/audio/sfx/button_click8.mp3');
    _audioPlayer.addSound(SfxType.click.index, 'assets/audio/sfx/click.mp3');
    _audioPlayer.addSound(SfxType.tap.index, 'assets/audio/sfx/tap.mp3');
    _audioPlayer.addSound(SfxType.pop.index, 'assets/audio/sfx/pop.mp3');
    _audioPlayer.addSound(SfxType.appear.index, 'assets/audio/sfx/appear.mp3');
    _audioPlayer.addSound(SfxType.alert.index, 'assets/audio/sfx/alert.mp3');
    _audioPlayer.addSound(SfxType.star.index, 'assets/audio/sfx/star.mp3');
    _audioPlayer.addSound(SfxType.success.index, 'assets/audio/sfx/win.mp3');
    _audioPlayer.addSound(SfxType.card.index, 'assets/audio/sfx/card.mp3');
    _audioPlayer.addSound(SfxType.cardShort.index, 'assets/audio/sfx/card2.mp3');
    _audioPlayer.addSound(SfxType.flip.index, 'assets/audio/sfx/flip.mp3');
    _audioPlayer.addSound(SfxType.panstart.index, 'assets/audio/sfx/pan_start.mp3');
    _audioPlayer.addSound(SfxType.panend.index, 'assets/audio/sfx/pan_end.mp3');
    _audioPlayer.addSound(SfxType.panend2.index, 'assets/audio/sfx/pan_end2.mp3');
  }

  void initialize() {}

  void dispose() {
    _lifecycleNotifier?.removeListener(_handleAppLifecycle);
    stopMusic();
    stopSound();
  }

  /// Enables the [AudioController] to listen to [AppLifecycleState] events,
  /// and therefore do things like stopping playback when the game
  /// goes into the background.
  void attachLifecycleNotifier(ValueNotifier<AppLifecycleState> lifecycleNotifier) {
    _lifecycleNotifier?.removeListener(_handleAppLifecycle);

    lifecycleNotifier.addListener(_handleAppLifecycle);
    _lifecycleNotifier = lifecycleNotifier;
  }

  /// Enables the [AudioController] to track changes to settings.
  /// Namely, when any of [SettingsController.muted],
  /// [SettingsController.musicOn] or [SettingsController.soundsOn] changes,
  /// the audio controller will act accordingly.
  void attachSettings(SettingsController settingsController) {
    if (_settings == settingsController) {
      // Already attached to this instance. Nothing to do.
      return;
    }

    // Remove handlers from the old settings controller if present
    final oldSettings = _settings;
    if (oldSettings != null) {
      oldSettings.music.removeListener(_musicOnHandler);
      oldSettings.sound.removeListener(_soundOnHandler);
    }

    _settings = settingsController;

    // Add handlers to the new settings controller
    settingsController.music.addListener(_musicOnHandler);
    settingsController.sound.addListener(_soundOnHandler);
  }

  /// Plays a single sound effect, defined by [type].
  ///
  /// The controller will ignore this call when the attached settings'
  /// [SettingsController.soundsOn] is `false`.
  void playSfx(SfxType type, {Duration? duration}) async {
    final soundsOn = _settings?.sound.value ?? false;
    if (!soundsOn) {
      _log.info(() => 'Ignoring playing sound ($type) because sounds are turned off.');
      return;
    }

    _audioPlayer.playSound(type.index);
    if (duration != null) {
      Future.delayed(duration, () => _audioPlayer.stopSound());
    }
  }

  void _handleAppLifecycle() {
    _log.info(_lifecycleNotifier!.value);
    switch (_lifecycleNotifier!.value) {
      case AppLifecycleState.paused:
      case AppLifecycleState.detached:
      case AppLifecycleState.hidden:
      case AppLifecycleState.inactive:
        pauseMusic();
        break;
      case AppLifecycleState.resumed:
        resumeMusic();
        break;
    }
  }

  void _musicOnHandler() {
    if (_settings!.music.value) {
      // Music got turned on.
      // _resumeMusic();
      startMusic();
    } else {
      // Music got turned off.
      stopMusic();
    }
  }

  void _soundOnHandler() {}

  void startMusic() {
    _log.info('starting music');
    final musicOn = _settings?.music.value ?? false;
    if (!musicOn) {
      _log.info(() => 'Ignoring playing music because music are turned off.');
      return;
    }

    _audioPlayer.playMusic(1, volume: 0.3);
  }

  void stopMusic() {
    _log.info('Stopping music');
    _audioPlayer.stopMusic();
  }

  void pauseMusic() {
    _log.info('pause music');
    _audioPlayer.pauseMusic();
  }

  Future<void> resumeMusic() async {
    _log.info('Resuming music');
    final musicOn = _settings?.music.value ?? false;
    if (musicOn) {
      _audioPlayer.resumeMusic();
    }
  }

  void stopSound() {
    _audioPlayer.stopSound();
  }
}
