import 'dart:io';
import 'dart:typed_data';

import 'package:comunic/ui/dialogs/alert_dialog.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/log_utils.dart';
import 'package:comunic/utils/permission_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:record_mp3/record_mp3.dart';
import 'package:video_player/video_player.dart';

/// Record audio dialog
///
/// @author Pierre Hubert

/// Record audio
Future<Uint8List> showRecordAudioDialog(BuildContext context) async {
  // Request record permission
  if (!await requestPermission(context, Permission.microphone)) {
    alert(context, tr("Did not get permission to access microphone!"));
    return null;
  }

  final res = await showDialog(
    context: context,
    builder: (c) => Scaffold(
      body: _RecordAudioDialog(),
      backgroundColor: Colors.transparent,
    ),
    barrierDismissible: false,
  );

  return res;
}

class _RecordAudioDialog extends StatefulWidget {
  @override
  __RecordAudioDialogState createState() => __RecordAudioDialogState();
}

class __RecordAudioDialogState extends State<_RecordAudioDialog> {
  String _recordPath;

  File get _recordFile => _recordPath == null ? null : File(_recordPath);

  bool _recording = false;

  bool get _hasRecord => !_recording && _recordPath != null;

  VideoPlayerController _videoPlayerController;

  bool _playing = false;

  bool _paused = false;

  /// Get record data. This getter can be accessed only once
  Uint8List get _bytes {
    final bytes = _recordFile.readAsBytesSync();
    File(_recordPath).deleteSync();
    return bytes;
  }

  @override
  void dispose() {
    _disposePlayer();
    RecordMp3.instance.stop();
    super.dispose();
  }

  void _disposePlayer() {
    if (_videoPlayerController != null) {
      _videoPlayerController.dispose();
    }
    _videoPlayerController = null;
  }

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text(tr("Audio record")),
      content: _buildContent(),
      actions: <Widget>[
        _ActionButton(
          visible: !_recording,
          text: tr("Cancel"),
          onPressed: () => Navigator.of(context).pop(),
        ),
        _ActionButton(
          visible: _hasRecord,
          text: tr("Send"),
          onPressed: () => Navigator.of(context).pop(_bytes),
        ),
      ],
    );
  }

  String get _status {
    if (_recording)
      return tr("Recording...");
    else if (_paused)
      return tr("Playback paused...");
    else if (_playing)
      return tr("Playing...");
    else if (!_hasRecord)
      return tr("Ready");
    else
      return tr("Done");
  }

  Widget _buildContent() => Row(
        children: <Widget>[
          Text(_status),

          Spacer(),

          // Start recording
          _RecordAction(
            visible: !_recording && !_playing,
            icon: Icons.fiber_manual_record,
            onTap: _startRecording,
            color: Colors.red,
          ),

          // Stop recording
          _RecordAction(
            visible: _recording,
            icon: Icons.stop,
            onTap: _stopRecording,
            color: Colors.red,
          ),

          // Play recording
          _RecordAction(
            visible: !_recording && _hasRecord && !_playing,
            icon: Icons.play_arrow,
            onTap: _playRecord,
          ),

          // Pause playback
          _RecordAction(
            visible: _playing && !_paused,
            icon: Icons.pause,
            onTap: _pausePlayback,
          ),

          // Resume recording
          _RecordAction(
            visible: _paused,
            icon: Icons.play_arrow,
            onTap: _resumePlayback,
          ),

          // Stop recording
          _RecordAction(
            visible: _playing,
            icon: Icons.stop,
            onTap: _stopPlayback,
          ),
        ],
      );

  void _startRecording() async {
    try {
      if (_recordFile != null) _recordFile.deleteSync();

      final dir = await getTemporaryDirectory();

      _recordPath = path.join(dir.absolute.path, "tmp-audio-record.mp3");

      RecordMp3.instance.start(_recordPath, (fail) {
        print(fail);
        snack(context, tr("Failed to start recording!"));
      });

      setState(() => _recording = true);
    } catch (e, s) {
      logError(e, s);
      snack(context, tr("Error while recording!"));
    }
  }

  void _stopRecording() async {
    try {
      RecordMp3.instance.stop();

      setState(() => _recording = false);
    } catch (e, s) {
      logError(e, s);
      snack(context, tr("Error while recording!"));
    }
  }

  void _playRecord() async {
    try {
      _disposePlayer();

      _videoPlayerController = VideoPlayerController.file(File(_recordPath));
      await _videoPlayerController.initialize();

      _videoPlayerController.addListener(() async {
        if (_videoPlayerController == null) return;

        if (_videoPlayerController.value.position ==
            _videoPlayerController.value.duration) _stopPlayback();
      });

      await _videoPlayerController.play();

      setState(() {
        _playing = true;
        _paused = false;
      });
    } catch (e, s) {
      logError(e, s);
      snack(context, tr("Error while playing record!"));
    }
  }

  void _pausePlayback() async {
    try {
      await _videoPlayerController.pause();
      setState(() => _paused = true);
    } catch (e, s) {
      logError(e, s);
      snack(context, tr("Error while pausing playback!"));
    }
  }

  void _resumePlayback() async {
    try {
      await _videoPlayerController.play();
      setState(() => _paused = false);
    } catch (e, s) {
      logError(e, s);
      snack(context, tr("Error while resuming playback!"));
    }
  }

  void _stopPlayback() async {
    try {
      _disposePlayer();
      setState(() {
        _paused = false;
        _playing = false;
      });
    } catch (e, s) {
      logError(e, s);
      snack(context, tr("Error while stopping playback!"));
    }
  }
}

class _RecordAction extends StatelessWidget {
  final bool visible;
  final IconData icon;
  final void Function() onTap;
  final Color color;

  const _RecordAction({
    Key key,
    @required this.visible,
    @required this.icon,
    @required this.onTap,
    this.color,
  })  : assert(visible != null),
        assert(icon != null),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    if (!visible) return Container(width: 0, height: 0);
    return IconButton(icon: Icon(icon, color: color), onPressed: onTap);
  }
}

class _ActionButton extends StatelessWidget {
  final bool visible;
  final String text;
  final void Function() onPressed;

  const _ActionButton({
    Key key,
    this.visible,
    this.text,
    this.onPressed,
  })  : assert(visible != null),
        assert(text != null),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    if (!visible) return Container();
    return MaterialButton(
      onPressed: onPressed,
      child: Text(text.toUpperCase()),
    );
  }
}