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

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:tangheem/classes/colors.dart';
import 'package:tangheem/classes/consts.dart';
import 'package:tangheem/classes/utils.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 Container(
      margin: EdgeInsets.only(top: 8, bottom: 8),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(8),
      ),
      padding: EdgeInsets.all(8),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Row(
            children: [
              if (widget.voiceNoteList.length > 1)
                PopupMenuButton(
                  padding: EdgeInsets.fromLTRB(4, 4, 0, 4),
                  onSelected: (int index) {
                    _player.seek(Duration.zero, index: index);
                  },
                  child: Transform.rotate(
                    angle: 180 * math.pi / 180,
                    child: SvgPicture.asset(
                      "assets/icons/drop_menu.svg",
                      width: 16,
                    ),
                  ),
                  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?.length ?? 0) > 0)
                StreamBuilder<int>(
                  stream: _player.currentIndexStream,
                  builder: (context, snapshot) {
                    int state = snapshot.data;
                    if (state == null) return SizedBox();
                    return Container(
                      width: 50.0,
                      margin: EdgeInsets.only(left: 8, right: 8),
                      height: 50.0,
                      decoration: BoxDecoration(
                        image: widget.voiceNoteList.length < 1 ? null : getDecodedImage(state),
                        borderRadius: BorderRadius.all(
                          Radius.circular(30.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,
                    );
                  },
                ),
              Expanded(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      widget.surahName,
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: ColorConsts.primaryBlack, height: 1),
                    ),
                    SizedBox(height: 4),
                    if ((widget.voiceNoteList?.length ?? 0) > 0)
                      StreamBuilder<int>(
                        stream: _player.currentIndexStream,
                        builder: (context, snapshot) {
                          final state = snapshot.data;
                          return Text(
                            (state == null || widget.voiceNoteList.isEmpty) ? "" : widget.voiceNoteList?.elementAt(state)?.userName ?? "",
                            style: TextStyle(fontSize: 10, color: ColorConsts.textGrey1, height: 1),
                          );
                        },
                      ),
                  ],
                ),
              ),
              if (widget.voiceNoteList?.isNotEmpty ?? false)
                commonIconButton("assets/icons/download_aya.svg", () async {
                  if (await _requestPermission()) {
                    saveToPhoneStorage(widget.voiceNoteList[_player.currentIndex].exposeFilePath, widget.voiceNoteList[_player.currentIndex].userName ?? "");
                  } else {
                    Utils.showToast("يجب اعطاء الاذن لتنزيل الآية");
                  }
                }),
            ],
          ),
          SizedBox(height: 8),
          if ((widget.voiceNoteList?.length ?? 0) > 0)
            Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                commonIconButton("assets/icons/next_aya.svg", () {
                  _player.seekToNext();
                }),
                SizedBox(width: 4),
                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();
                    });
                  },
                ),
                SizedBox(width: 4),
                commonIconButton("assets/icons/previous_aya.svg", () {
                  _player.seekToPrevious();
                }),
                SizedBox(width: 16),
                Expanded(
                  child: StreamBuilder<Duration>(
                    stream: _player.positionStream,
                    builder: (context, snapshot) {
                      final state = snapshot.data;
                      return SliderTheme(
                        data: SliderTheme.of(context).copyWith(
                          inactiveTrackColor: ColorConsts.sliderBackground,
                          activeTrackColor: ColorConsts.secondaryOrange,
                          trackHeight: 8.0,
                          thumbColor: ColorConsts.primaryBlack,
                          thumbShape: RoundSliderThumbShape(enabledThumbRadius: 10.0),
                          overlayColor: ColorConsts.primaryBlack.withAlpha(32),
                          overlayShape: RoundSliderOverlayShape(overlayRadius: 12.0),
                        ),
                        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()));
                            },
                          ),
                        ),
                      );
                    },
                  ),
                ),
                SizedBox(width: 8),
                StreamBuilder<Duration>(
                  stream: _player.positionStream,
                  builder: (context, snapshot) {
                    final state = snapshot.data;
                    return Text(
                      _durationTime(state) ?? "",
                      style: TextStyle(color: ColorConsts.textGrey1, height: 1.1, fontFamily: "Roboto"),
                    );
                  },
                ),
              ],
            )
        ],
      ),
    );
  }

  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.only(right: 2),
        icon: SvgPicture.asset(icon, height: 16, width: 16),
        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"));
    }
    storagePath += "/Download/Tangheem/";
    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);
    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);
    });
  }
}
