import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'dart:math' as math;
import 'dart:typed_data';

import 'package:device_info_plus/device_info_plus.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:http/http.dart' as http;
import 'package:just_audio/just_audio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_storage/shared_storage.dart' as saf;
import 'package:tangheem/classes/colors.dart';
import 'package:tangheem/classes/consts.dart';
import 'package:tangheem/classes/utils.dart';
import 'package:tangheem/extensions/int_extensions.dart';
import 'package:tangheem/extensions/string_extensions.dart';
import 'package:tangheem/extensions/widget_extensions.dart';
import 'package:tangheem/models/aya_tangheem_type_mapped.dart';

class AyaPlayerWidget extends StatefulWidget {
  final int surahNo;
  final int ayaNo;
  final String numberInSurah;
  final String surahName;
  final String ayaTangheemTypeId;
  final GlobalKey globalKey;
  final List<VoiceNote> voiceNoteList;

  AyaPlayerWidget({Key key, this.surahName, this.ayaTangheemTypeId, this.voiceNoteList, this.ayaNo, this.numberInSurah, this.surahNo, @required this.globalKey}) : super(key: key);

  @override
  _AyaPlayerWidgetState createState() {
    return _AyaPlayerWidgetState();
  }
}

class _AyaPlayerWidgetState extends State<AyaPlayerWidget> {
  double sliderValue = 0.4;
  bool isPlaying = false;
  AudioPlayer _player;
  bool _isAudioHaveError = false;

  @override
  void initState() {
    super.initState();
    _player = AudioPlayer();
    setAudioSource();
  }

  setAudioSource() {
    try {
      List<AudioSource> voiceList = [];

      if ((widget.voiceNoteList?.length ?? 0) > 0) {
        voiceList = widget.voiceNoteList.map((e) => AudioSource.uri(Uri.parse(ApiConsts.baseUrl + e.exposeFilePath))).toList();
      }

      final _playlist = ConcatenatingAudioSource(children: voiceList);
      _player.setAudioSource(_playlist, initialIndex: 0, initialPosition: Duration.zero).then((value) => () {});
    } catch (e) {
      _isAudioHaveError = true;
    }
  }

  @override
  void didUpdateWidget(covariant AyaPlayerWidget oldWidget) {
    if (widget.voiceNoteList != oldWidget.voiceNoteList) {
      setAudioSource();
    }
    super.didUpdateWidget(oldWidget);
  }

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

  int _tempIndex;
  Uint8List temp;

  // made this dedicated function to remove avatar flashing
  DecorationImage getDecodedImage(int index) {
    if (_tempIndex == null || _tempIndex != index) {
      _tempIndex = index;
      String encodedImage = widget.voiceNoteList.elementAt(index).profilePicture;
      if (encodedImage?.contains("data:image/png;base64,") ?? false) {
        encodedImage = encodedImage.replaceAll("data:image/png;base64,", "");
      }
      if (encodedImage?.contains("data:image/jpeg;base64,") ?? false) {
        encodedImage = encodedImage.replaceAll("data:image/jpeg;base64,", "");
      }
      if (encodedImage == null) return null;
      temp = base64Decode(encodedImage);
    }
    if (temp == null) return null;
    return DecorationImage(
      fit: BoxFit.cover,
      image: MemoryImage(temp),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        if ((widget.voiceNoteList?.length ?? 0) > 0)
          DottedBorder(
            borderType: BorderType.Circle,
            padding: EdgeInsets.all(5),
            color: Color(0xff91C1BC),
            dashPattern: [4, 4],
            child: StreamBuilder<int>(
              stream: _player.currentIndexStream,
              builder: (context, snapshot) {
                int state = snapshot.data;
                if (state == null) return SizedBox();
                return Container(
                  width: 72.0,
                  margin: EdgeInsets.only(left: 8, right: 8),
                  height: 72.0,
                  decoration: BoxDecoration(
                    image: widget.voiceNoteList.length < 1 ? null : getDecodedImage(state),
                    borderRadius: BorderRadius.all(
                      Radius.circular(50.0),
                    ),
                  ),
                  child: widget.voiceNoteList.length < 1
                      ? ClipRRect(
                          borderRadius: BorderRadius.all(
                            Radius.circular(30.0),
                          ),
                          child: SvgPicture.asset(
                            "assets/icons/chat_user.svg",
                            clipBehavior: Clip.antiAlias,
                          ),
                        )
                      : null,
                );
              },
            ),
          ),
        if ((widget.voiceNoteList?.length ?? 0) > 0)
          Container(
            height: 42,
            margin: EdgeInsets.only(top: 8, bottom: 12),
            padding: EdgeInsets.only(left: 15, right: 8),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(20),
              border: Border.all(color: Color(0xffE3E3E3), width: 1),
            ),
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                if (widget.voiceNoteList.length > 1)
                  PopupMenuButton(
                    padding: EdgeInsets.fromLTRB(4, 4, 0, 4),
                    onSelected: (int index) {
                      _player.seek(Duration.zero, index: index);
                    },
                    child: Icon(
                      Icons.more_vert_rounded,
                      color: Color(0xffB5B5B5),
                      size: 18,
                    ),
                    itemBuilder: (context) {
                      var _voiceList = widget.voiceNoteList;
                      var length = _voiceList.length < 2 ? 0 : _voiceList.length;

                      return List.generate(length, (index) {
                        return PopupMenuItem(
                          value: index,
                          child: Text(
                            '${_voiceList[index].userName}',
                            style: TextStyle(color: ColorConsts.primaryBlack),
                          ),
                        );
                      });
                    },
                  ),
                if (widget.voiceNoteList?.isNotEmpty ?? false)
                  commonIconButton("assets/icons/download_aya.svg", () async {
                    if (Platform.isAndroid && (await DeviceInfoPlugin().androidInfo).version.sdkInt > 29) {
                      String filePath = widget.voiceNoteList[_player.currentIndex].exposeFilePath;
                      saveAudioToAboveAndroid32(filePath, "Tangheem-${widget.voiceNoteList[_player.currentIndex].userName ?? ""}-${widget.voiceNoteList[_player.currentIndex].fileName}",
                          widget.voiceNoteList[_player.currentIndex].fileType);
                    } else if (await _requestPermission()) {
                      saveToPhoneStorage(widget.voiceNoteList[_player.currentIndex].exposeFilePath, widget.voiceNoteList[_player.currentIndex].userName ?? "");
                    } else {
                      Utils.showToast("يجب اعطاء الاذن لتنزيل الآية");
                    }
                  }),
                4.width,
                StreamBuilder<double>(
                  stream: _player.volumeStream,
                  builder: (context, snapshot) {
                    final volumn = snapshot.data;

                    return Icon(
                      volumn == 0 ? Icons.volume_off_rounded : Icons.volume_up_rounded,
                      color: Color(0xffB5B5B5),
                      size: 18,
                    ).onPress(() {
                      _player.setVolume(volumn == 0 ? 1.0 : 0.0);
                    });
                  },
                ),
                SizedBox(width: 4),
                StreamBuilder<Duration>(
                  stream: _player.positionStream,
                  builder: (context, snapshot) {
                    final state = snapshot.data;
                    return Directionality(textDirection: TextDirection.ltr, child: (_durationTime(state) + " / " + _durationTime(_player.duration)).toText(8, color: ColorConsts.greyB7Color));
                  },
                ),
                SizedBox(width: 4),
                Expanded(
                  child: StreamBuilder<Duration>(
                    stream: _player.positionStream,
                    builder: (context, snapshot) {
                      final state = snapshot.data;
                      return SliderTheme(
                        data: SliderTheme.of(context).copyWith(
                            inactiveTrackColor: Color(0xffF5F5F5),
                            activeTrackColor: Color(0xff91C1BC),
                            trackHeight: 5.0,
                            thumbColor: ColorConsts.primaryBlack,
                            thumbShape: RoundSliderThumbShape(enabledThumbRadius: 5.0),
                            overlayColor: ColorConsts.primaryBlack.withAlpha(32),
                            overlayShape: SliderComponentShape.noOverlay),
                        child: Directionality(
                          textDirection: TextDirection.ltr,
                          child: Slider(
                            value: (state?.inSeconds ?? 0) + 0.0,
                            min: 0,
                            max: (_player?.duration?.inSeconds ?? 0) + 0.0,
                            onChanged: (value) {
                              _player.seek(Duration(seconds: value.round()));
                            },
                          ),
                        ),
                      );
                    },
                  ),
                ),
                StreamBuilder<PlayerState>(
                  stream: _player.playerStateStream,
                  builder: (context, snapshot) {
                    final state = snapshot.data?.playing ?? false;
                    if (state) {
                      if (_player?.duration?.inSeconds == _player?.position?.inSeconds) {
                        _player.pause();
                        _player.seek(Duration.zero);
                      }
                    }
                    return commonIconButton(state ? "assets/icons/pause.svg" : "assets/icons/play_aya.svg", () {
                      state
                          ? _player.pause()
                          : _isAudioHaveError
                              ? Utils.showToast("خطأ في تحميل ملف الصوت")
                              : _player.play();
                    });
                  },
                ),
              ],
            ),
          ),
        Row(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            (widget.surahName).toText(13, color: ColorConsts.greyB7Color),
            SizedBox(width: 4),
            widget.numberInSurah.toText(13, color: ColorConsts.greyB7Color),
            SizedBox(width: 4),
            if ((widget.voiceNoteList?.length ?? 0) > 0)
              StreamBuilder<int>(
                stream: _player.currentIndexStream,
                builder: (context, snapshot) {
                  final state = snapshot.data;
                  return ((state == null || widget.voiceNoteList.isEmpty) ? "" : widget.voiceNoteList?.elementAt(state)?.userName ?? "").toText(13, color: ColorConsts.greyB7Color);
                },
              ),
          ],
        ),
        8.height,
      ],
    );
  }

  String _durationTime(Duration duration) {
    if (duration == null) {
      return "00:00";
    }
    String twoDigits(int n) => n.toString().padLeft(2, "0");
    String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
    String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
    return "$twoDigitMinutes:$twoDigitSeconds";
  }

  Widget commonIconButton(String icon, VoidCallback onPressed) {
    return IconButton(
        highlightColor: Colors.transparent,
        splashColor: Colors.transparent,
        constraints: BoxConstraints(),
        padding: EdgeInsets.zero,
        icon: SvgPicture.asset(icon, color: Color(0xff939393), height: 14, width: 14),
        onPressed: onPressed);
  }

  Future<bool> _requestPermission() async {
    Map<Permission, PermissionStatus> statuses = await [Permission.storage].request();

    return statuses[Permission.storage].isGranted;
  }

  void saveToPhoneStorage(String filePath, String name) async {
    Directory storageDirectory;
    if (Platform.isAndroid) {
      storageDirectory = await getExternalStorageDirectory();
    } else if (Platform.isIOS) {
      storageDirectory = await getApplicationDocumentsDirectory();
    } else {
      return;
    }

    String storagePath = storageDirectory.path;
    if (storagePath.contains("/Android/data")) {
      storagePath = storagePath.substring(0, storagePath.indexOf("/Android/data"));
    }
    if (Platform.isAndroid) {
      storagePath += "/Download/Tangheem/";
    } else {
      storagePath += "/Recording/";
    }

    var d = Directory(storagePath);
    if (!d.existsSync()) {
      d.createSync(recursive: true);
    }
    storagePath += "Tangheem-$name-${DateTime.now().millisecondsSinceEpoch}${filePath.substring(filePath.lastIndexOf('.'), filePath.length)}";
    Utils.showLoading(context);
    Utils.downloadFile(ApiConsts.baseUrl + filePath, storagePath, onResponse: (isSuccess) {
      Utils.hideLoading(context);
      if (isSuccess) {
        Utils.showToast("تم حفظ الآية بنجاح");
      } else {
        Utils.showToast("خطأ في حفظ الآية");
      }
    });
  }

  void downloadFile(String url, String path, {Function(bool) onResponse}) {
    var httpClient = http.Client();
    var request = http.Request('GET', Uri.parse(url));
    var response = httpClient.send(request);
    String dir = path;

    List<List<int>> chunks = [];
    int downloaded = 0;
    int lastVal;

    response.asStream().listen((http.StreamedResponse r) {
      r.stream.listen((List<int> chunk) {
        debugPrint('downloadPercentage: ${downloaded / r.contentLength * 100}');
        lastVal = (downloaded / r.contentLength * 100).toInt();

        chunks.add(chunk);
        downloaded += chunk.length;
      }, onDone: () async {
        debugPrint('downloadPercentage: ${downloaded / r.contentLength * 100}');
        if (lastVal == 0) {
          onResponse(false);
          return;
        }
        // Save the file
        File file = new File('$dir');
        final Uint8List bytes = Uint8List(r.contentLength);
        int offset = 0;
        for (List<int> chunk in chunks) {
          bytes.setRange(offset, offset + chunk.length, chunk);
          offset += chunk.length;
        }
        await file.writeAsBytes(bytes);
        onResponse(true);
        return;
      }, onError: (ex) {
        debugPrint("onError:$ex");
      }, cancelOnError: true);
    }).onError((ex) {
      debugPrint("onError:$ex");
      onResponse(false);
    });
  }

  void saveAudioToAboveAndroid32(String pathUrl, String fileName, String fileType) async {
    List<saf.UriPermission> _persistedPermissionUris = await saf.persistedUriPermissions();
    if (_persistedPermissionUris.isNotEmpty) {
      saf.UriPermission permissionUri = _persistedPermissionUris.first;
      startFileDownloadAboveAndroid32(pathUrl, fileName, permissionUri.uri, fileType);
    } else {
      Uri selectedDocumentUris = await saf.openDocumentTree();
      if (selectedDocumentUris == null) return;
      saveAudioToAboveAndroid32(pathUrl, fileName, fileType);
    }
  }

  void startFileDownloadAboveAndroid32(String url, String fileName, Uri persistentUri, String fileType) async {
    try {
      Utils.showLoading(context);
      //String path = await Utils.getStoragePath(fileName);
      Utils.downloadFileAboveAndroid32(ApiConsts.baseUrl + url, "path", onResponse: (bytes) async {
        Utils.hideLoading(context);
        if (bytes.isNotEmpty) {
          final documentFile = await persistentUri.toDocumentFile();
          final child = await documentFile?.child(fileName);
          if (child == null) {
            documentFile?.createFileAsBytes(
              mimeType: fileType,
              bytes: bytes,
              displayName: fileName,
            );
          } else {
            documentFile?.writeToFileAsBytes(
              bytes: bytes,
              mode: FileMode.write,
            );
          }

          Utils.showToast("تم حفظ الملف بنجاح");
        } else {
          Utils.showToast("فشل حفظ الملف ، حاول مرة أخرى");
        }
      });
    } catch (ex) {
      print(ex);
      Utils.hideLoading(context);
    }
  }
}
