import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'tmg_load_dynamic.dart';
import '../../tmg_type.dart';
import '../../tmg_audio_effect_ctrl.dart';
import '../../tmg_error.dart';

typedef PlayEffectNative = Int32 Function(Int32 soundId, Pointer<Int8> filePath,
    Int32 loop, Float pitch, Float pan, Int32 vol);
typedef PlayEffectDart = int Function(int soundId, Pointer<Int8> filePath,
    int loop, double pitch, double pan, int vol);

typedef StopEffectNative = Int32 Function(Int32 soundId);
typedef StopEffectDart = int Function(int soundId);

typedef SetEffectsVolumeNative = Int32 Function(Int32 vol);
typedef SetEffectsVolumeDart = int Function(int vol);

typedef SetEffectVolumeNative = Int32 Function(Int32 soundId, Int32 vol);
typedef SetEffectVolumeDart = int Function(int soundId, int vol);

typedef AccompnyFinishCallBackNative = Void Function(
    Int32 code, Int32 is_finished, Pointer<Int8> filepath);
typedef AccompnyStartCallBackNative = Void Function(Int32 soundId);
typedef AccompnyPauseCallBackNative = Void Function(Int32 soundId);
typedef AccompnyResumeCallBackNative = Void Function(Int32 soundId);

typedef AccompnyStartCallBackDart = void Function(int soundId);
typedef AccompnyPauseCallBackDart = void Function(int soundId);
typedef AccompnyResumeCallBackDart = void Function(int soundId);

typedef StartAccompanyNative = Int32 Function(
    Pointer<Int8> filePath,
    Int32 loopBack,
    Int32 nLoopCount,
    Int32 duckerTimeMs,
    Pointer<NativeFunction<AccompnyFinishCallBackNative>> callback);
typedef StartAccompanyDart = int Function(
    Pointer<Int8> filePath,
    int loopBack,
    int nLoopCount,
    int duckerTimeMs,
    Pointer<NativeFunction<AccompnyFinishCallBackNative>> callback);

typedef StopAccompanyNative = Int32 Function(Int32 duckerTimeMs);
typedef StopAccompanyDart = int Function(int duckerTimeMs);

typedef PauseAccompanyNative = Int32 Function();
typedef PauseAccompanyDart = int Function();

typedef ResumeAccompanyNative = Int32 Function();
typedef ResumeAccompanyDart = int Function();

typedef SetAccompanyVolumeNative = Int32 Function(Int32 vol);
typedef SetAccompanyVolumeDart = int Function(int vol);

typedef GetAccompanyVolumeNative = Int32 Function();
typedef GetAccompanyVolumeDart = int Function();

typedef IsAccompanyPlayEndNative = Int32 Function();
typedef IsAccompanyPlayEndDart = int Function();

typedef GetAccompanyFileCurrentPlayedTimeByMsNative = Int32 Function();
typedef GetAccompanyFileCurrentPlayedTimeByMsDart = int Function();

typedef SetAccompanyFileCurrentPlayedTimeByMsNative = Int32 Function(
    Int32 timeMs);
typedef SetAccompanyFileCurrentPlayedTimeByMsDart = int Function(int timeMs);

typedef GetAccompanyFileTotalTimeByMsNative = Int32 Function();
typedef GetAccompanyFileTotalTimeByMsDart = int Function();

typedef SetVoiceTypeNative = Int32 Function(Int32 voiceType);
typedef SetVoiceTypeDart = int Function(int voiceType);

typedef SetKaraokeTypeNative = Int32 Function(Int32 type);
typedef SetKaraokeTypeDart = int Function(int type);

class TMGAudioEffectCtrlBindings {
  TMGLoadDynamic _tmgDynamicInstance = TMGLoadDynamic.GetInstance();
  DynamicLibrary? _gmeSdkLib = null;
  final int ENABLE_PUBLISH_MUSIC = 1;
  static late Pointer<NativeFunction<AccompnyFinishCallBackNative>>
      accompanyFinishCallback;

  static AccompnyStartCallBackDart? _accompanyStartCallbackDart = null;
  static AccompnyPauseCallBackDart? _accompanyPauseCallbackDart = null;
  static AccompnyResumeCallBackDart? _accompanyResumeCallbackDart = null;

  TMGAudioEffectCtrlBindings() {
    _gmeSdkLib = _tmgDynamicInstance.GetGmeSdkLib();
  }

  static void SetAccompanyFinishCallback(
      Pointer<NativeFunction<AccompnyFinishCallBackNative>> finishCallback,
      Pointer<NativeFunction<AccompnyStartCallBackNative>> startCallback,
      Pointer<NativeFunction<AccompnyPauseCallBackNative>> pauseCallback,
      Pointer<NativeFunction<AccompnyResumeCallBackNative>> resumeCallback) {
    accompanyFinishCallback = finishCallback;
    _accompanyStartCallbackDart =
        startCallback.asFunction<AccompnyStartCallBackDart>();
    _accompanyPauseCallbackDart =
        pauseCallback.asFunction<AccompnyPauseCallBackDart>();
    _accompanyResumeCallbackDart =
        resumeCallback.asFunction<AccompnyResumeCallBackDart>();
  }

  late final PlayEffectDart PlayEffectPtr = _gmeSdkLib!
      .lookup<NativeFunction<PlayEffectNative>>('QAVSDK_AVAudioCtrl_PlayEffect')
      .asFunction<PlayEffectDart>();

  int PlayEffect(int soundId, String filePath, bool loop) {
    return PlayEffectPtr(soundId, filePath.toNativeUtf8().cast<Int8>(),
        loop ? 1 : 0, 1.0, 0.0, 100);
  }

  late final StopEffectDart StopEffectPtr = _gmeSdkLib!
      .lookup<NativeFunction<StopEffectNative>>('QAVSDK_AVAudioCtrl_StopEffect')
      .asFunction<StopEffectDart>();

  int StopEffect(int soundId) {
    return StopEffectPtr(soundId);
  }

  late final SetEffectsVolumeDart SetEffectsVolumePtr = _gmeSdkLib!
      .lookup<NativeFunction<SetEffectsVolumeNative>>(
          'QAVSDK_AVAudioCtrl_SetEffectsVolume')
      .asFunction<SetEffectsVolumeDart>();

  int SetEffectsVolume(int vol) {
    return SetEffectsVolumePtr(vol);
  }

  late final SetEffectVolumeDart SetEffectVolumePtr = _gmeSdkLib!
      .lookup<NativeFunction<SetEffectVolumeNative>>(
          'QAVSDK_AVAudioCtrl_SetEffectVolume')
      .asFunction<SetEffectVolumeDart>();

  int SetEffectVolume(int soundId, int vol) {
    return SetEffectVolumePtr(soundId, vol);
  }

  late final StartAccompanyDart StartAccompanyPtr = _gmeSdkLib!
      .lookup<NativeFunction<StartAccompanyNative>>(
          'QAVSDK_AVAudioCtrl_StartAccompany')
      .asFunction<StartAccompanyDart>();

  int StartAccompany(String filePath, int nLoopCount) {
    int ret = StartAccompanyPtr(filePath.toNativeUtf8().cast<Int8>(),
        ENABLE_PUBLISH_MUSIC, nLoopCount, 0, accompanyFinishCallback);
    if (ret == 0 && _accompanyStartCallbackDart != null) {
      _accompanyStartCallbackDart!(0);
    }

    return ret;
  }

  late final StopAccompanyDart StopAccompanyPtr = _gmeSdkLib!
      .lookup<NativeFunction<StopAccompanyNative>>(
          'QAVSDK_AVAudioCtrl_StopAccompany')
      .asFunction<StopAccompanyDart>();

  int StopAccompany(int duckerTimeMs) {
    return StopAccompanyPtr(duckerTimeMs);
  }

  late final PauseAccompanyDart PauseAccompanyPtr = _gmeSdkLib!
      .lookup<NativeFunction<PauseAccompanyNative>>(
          'QAVSDK_AVAudioCtrl_PauseAccompany')
      .asFunction<PauseAccompanyDart>();

  int PauseAccompany() {
    int ret = PauseAccompanyPtr();
    if (ret == 0 && _accompanyPauseCallbackDart != null) {
      _accompanyPauseCallbackDart!(0);
    }
    return ret;
  }

  late final ResumeAccompanyDart ResumeAccompanyPtr = _gmeSdkLib!
      .lookup<NativeFunction<ResumeAccompanyNative>>(
          'QAVSDK_AVAudioCtrl_ResumeAccompany')
      .asFunction<ResumeAccompanyDart>();

  int ResumeAccompany() {
    int ret = ResumeAccompanyPtr();
    if (ret == 0 && _accompanyResumeCallbackDart != null) {
      _accompanyResumeCallbackDart!(0);
    }
    return ret;
  }

  late final SetAccompanyVolumeDart SetAccompanyVolumePtr = _gmeSdkLib!
      .lookup<NativeFunction<SetAccompanyVolumeNative>>(
          'QAVSDK_AVAudioCtrl_SetAccompanyVolume')
      .asFunction<SetAccompanyVolumeDart>();

  int SetAccompanyVolume(int vol) {
    return SetAccompanyVolumePtr(vol);
  }

  late final GetAccompanyVolumeDart GetAccompanyVolumePtr = _gmeSdkLib!
      .lookup<NativeFunction<GetAccompanyVolumeNative>>(
          'QAVSDK_AVAudioCtrl_GetAccompanyVolume')
      .asFunction<GetAccompanyVolumeDart>();

  int GetAccompanyVolume() {
    return GetAccompanyVolumePtr();
  }

  late final IsAccompanyPlayEndDart IsAccompanyPlayEndPtr = _gmeSdkLib!
      .lookup<NativeFunction<IsAccompanyPlayEndNative>>(
          'QAVSDK_AVAudioCtrl_IsAccompanyPlayEnd')
      .asFunction<IsAccompanyPlayEndDart>();

  bool IsAccompanyPlayEnd() {
    return IsAccompanyPlayEndPtr() != 0;
  }

  late final GetAccompanyFileCurrentPlayedTimeByMsDart
      GetAccompanyFileCurrentPlayedTimeByMsPtr = _gmeSdkLib!
          .lookup<NativeFunction<GetAccompanyFileCurrentPlayedTimeByMsNative>>(
              'QAVSDK_AVAudioCtrl_GetAccompanyFileCurrentPlayedTimeByMs')
          .asFunction<GetAccompanyFileCurrentPlayedTimeByMsDart>();

  int GetAccompanyFileCurrentPlayedTimeByMs() {
    return GetAccompanyFileCurrentPlayedTimeByMsPtr();
  }

  late final SetAccompanyFileCurrentPlayedTimeByMsDart
      SetAccompanyFileCurrentPlayedTimeByMsPtr = _gmeSdkLib!
          .lookup<NativeFunction<SetAccompanyFileCurrentPlayedTimeByMsNative>>(
              'QAVSDK_AVAudioCtrl_SetAccompanyFileCurrentPlayedTimeByMs')
          .asFunction<SetAccompanyFileCurrentPlayedTimeByMsDart>();

  int SetAccompanyFileCurrentPlayedTimeByMs(int timeMs) {
    return SetAccompanyFileCurrentPlayedTimeByMsPtr(timeMs);
  }

  late final GetAccompanyFileTotalTimeByMsDart
      GetAccompanyFileTotalTimeByMsPtr = _gmeSdkLib!
          .lookup<NativeFunction<GetAccompanyFileTotalTimeByMsNative>>(
              'QAVSDK_AVAudioCtrl_GetAccompanyFileTotalTimeByMs')
          .asFunction<GetAccompanyFileTotalTimeByMsDart>();

  int GetAccompanyFileTotalTimeByMs() {
    return GetAccompanyFileTotalTimeByMsPtr();
  }

  late final SetVoiceTypeDart SetVoiceTypePtr = _gmeSdkLib!
      .lookup<NativeFunction<SetVoiceTypeNative>>(
          'QAVSDK_AVAudioCtrl_SetVoiceType')
      .asFunction<SetVoiceTypeDart>();

  int VoiceTypeToV2VoiceTypeValue(ITMG_VOICE_TYPE type) {
    switch (type) {
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_ORIGINAL_SOUND:
        return 0;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_LOLITA:
        return 1;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_UNCLE:
        return 2;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_INTANGIBLE:
        return 3;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_DEAD_FATBOY:
        return 4;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_HEAVY_MENTAL:
        return 5;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_DIALECT:
        return 6;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_INFLUENZA:
        return 7;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_CAGED_ANIMAL:
        return 8;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_HEAVY_MACHINE:
        return 9;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_STRONG_CURRENT:
        return 10;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_KINDER_GARTEN:
        return 11;
      case ITMG_VOICE_TYPE.ITMG_VOICE_TYPE_PIGSY:
        return 12;
      default:
        return -1;
    }
  }

  int SetVoiceType(ITMG_VOICE_TYPE type) {
    final voiceTypeValue = VoiceTypeToV2VoiceTypeValue(type);
    if (voiceTypeValue == -1) {
      return TMGError.AV_ERR_NOT_IMPLEMENTED;
    }
    return SetVoiceTypePtr(voiceTypeValue);
  }

  late final SetKaraokeTypeDart SetKaraokeTypePtr = _gmeSdkLib!
      .lookup<NativeFunction<SetKaraokeTypeNative>>(
        'QAVSDK_AVAudioCtrl_SetKaraokeType',
      )
      .asFunction<SetKaraokeTypeDart>();

  int KaraokeTypeToV2KaraokeTypeValue(ITMG_KARAOKE_TYPE type) {
    switch (type) {
      case ITMG_KARAOKE_TYPE.ITMG_KARAOKE_TYPE_0:
        return 0;
      case ITMG_KARAOKE_TYPE.ITMG_KARAOKE_TYPE_1:
        return 1;
      case ITMG_KARAOKE_TYPE.ITMG_KARAOKE_TYPE_2:
        return 2;
      case ITMG_KARAOKE_TYPE.ITMG_KARAOKE_TYPE_9:
        return 3;
      case ITMG_KARAOKE_TYPE.ITMG_KARAOKE_TYPE_10:
        return 4;
      case ITMG_KARAOKE_TYPE.ITMG_KARAOKE_TYPE_8:
        return 5;
      case ITMG_KARAOKE_TYPE.ITMG_KARAOKE_TYPE_11:
        return 6;
      case ITMG_KARAOKE_TYPE.ITMG_KARAOKE_TYPE_4:
        return 7;
      case ITMG_KARAOKE_TYPE.ITMG_KARAOKE_TYPE_7:
        return 8;
      default:
        return -1;
    }
  }

  int SetKaraokeType(ITMG_KARAOKE_TYPE type) {
    final karaokeTypeValue = KaraokeTypeToV2KaraokeTypeValue(type);
    if (karaokeTypeValue == -1) {
      return TMGError.AV_ERR_NOT_IMPLEMENTED;
    }
    return SetKaraokeTypePtr(karaokeTypeValue);
  }
}
