import 'package:guru_utils/audio/audio_effector.dart';
import 'package:guru_utils/vibration/vibrate_model.dart';
import 'package:guru_utils/vibration/vibrate_utils.dart';

/// Created by Haoyi on 2023/9/26

class FeedbackOccasion {
  final String name;
  final FeedbackOccasion? parent;

  const FeedbackOccasion._(this.name, {this.parent});

  static const click = FeedbackOccasion._('click');
  static const page = FeedbackOccasion._('page');
  static const popup = FeedbackOccasion._('popup');

  static const clickButton = FeedbackOccasion._('clickButton', parent: click);
  static const clickTab = FeedbackOccasion._('clickTab', parent: click);
  static const clickItem = FeedbackOccasion._('clickItem', parent: click);
  static const clickSwitch = FeedbackOccasion._('clickList', parent: click);

  static const openPage = FeedbackOccasion._('openPage', parent: page);
  static const backPage = FeedbackOccasion._('backPage', parent: page);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is FeedbackOccasion &&
          runtimeType == other.runtimeType &&
          name == other.name &&
          parent == other.parent;

  @override
  int get hashCode => name.hashCode ^ parent.hashCode;
}

class FeedbackProposal {
  final FeedbackOccasion occasion;

  final AudioEffect? audio;

  final VibrateEffect? vibration;

  const FeedbackProposal(this.occasion, {this.audio, this.vibration});
}

class FeedbackCapabilities {
  static const int _sound = 0x01;
  static const int _vibration = 0x02;

  static const disabled = FeedbackCapabilities._(0);
  static const fullCapabilities = FeedbackCapabilities._(_sound | _vibration);
  static const sound = FeedbackCapabilities._(_sound);
  static const vibration = FeedbackCapabilities._(_vibration);

  final int value;

  const FeedbackCapabilities._(this.value);

  bool hasCapabilities(FeedbackCapabilities capabilities) {
    return (value & capabilities.value) == capabilities.value;
  }

  bool canPerform() {
    return value != disabled.value;
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is FeedbackCapabilities && runtimeType == other.runtimeType && value == other.value;

  @override
  int get hashCode => value.hashCode;
}

class FeedbackManager {
  static final FeedbackManager instance = FeedbackManager._();

  FeedbackManager._();

  final Map<FeedbackOccasion, FeedbackProposal> _feedbacks = {};

  static void init({List<FeedbackProposal> proposals = const []}) {
    for (var proposal in proposals) {
      instance._feedbacks[proposal.occasion] = proposal;
    }
  }

  FeedbackProposal? _searchProposal(FeedbackOccasion occasion) {
    for (FeedbackOccasion? o = occasion; o != null; o = o.parent) {
      final proposal = _feedbacks[o];
      if (proposal != null) {
        return proposal;
      }
    }
    return null;
  }

  void perform(FeedbackOccasion occasion,
      {FeedbackCapabilities capabilities = FeedbackCapabilities.fullCapabilities}) {
    final proposal = _searchProposal(occasion);
    if (proposal != null) {
      if (capabilities.hasCapabilities(FeedbackCapabilities.sound) && proposal.audio != null) {
        AudioEffector.instance.play(proposal.audio!);
      }
      if (capabilities.hasCapabilities(FeedbackCapabilities.vibration) &&
          proposal.vibration != null) {
        VibrateUtils.vibrate(proposal.vibration!);
      }
    }
  }
}
