import 'package:aves/model/entry.dart';
import 'package:aves/ref/languages.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/viewer/video/controller.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';

import 'aves_dialog.dart';

class VideoStreamSelectionDialog extends StatefulWidget {
  final Map<StreamSummary, bool> streams;

  const VideoStreamSelectionDialog({
    Key? key,
    required this.streams,
  }) : super(key: key);

  @override
  _VideoStreamSelectionDialogState createState() => _VideoStreamSelectionDialogState();
}

class _VideoStreamSelectionDialogState extends State<VideoStreamSelectionDialog> {
  late List<StreamSummary?> _videoStreams, _audioStreams, _textStreams;
  StreamSummary? _currentVideo, _currentAudio, _currentText;

  @override
  void initState() {
    super.initState();

    final byType = groupBy<StreamSummary, StreamType>(widget.streams.keys, (v) => v.type);
    // check width/height to exclude image streams (that are included among video streams)
    _videoStreams = (byType[StreamType.video] ?? []).where((v) => v.width != null && v.height != null).toList();
    _audioStreams = (byType[StreamType.audio] ?? []);
    _textStreams = [null, ...byType[StreamType.text] ?? []];

    final streamEntries = widget.streams.entries;
    _currentVideo = streamEntries.firstWhereOrNull((kv) => kv.key.type == StreamType.video && kv.value)?.key;
    _currentAudio = streamEntries.firstWhereOrNull((kv) => kv.key.type == StreamType.audio && kv.value)?.key;
    _currentText = streamEntries.firstWhereOrNull((kv) => kv.key.type == StreamType.text && kv.value)?.key;
  }

  @override
  Widget build(BuildContext context) {
    final canSelectVideo = _videoStreams.length > 1;
    final canSelectAudio = _audioStreams.length > 1;
    final canSelectText = _textStreams.length > 1;
    final canSelect = canSelectVideo || canSelectAudio || canSelectText;
    return AvesDialog(
      context: context,
      content: canSelect ? null : Text(context.l10n.videoStreamSelectionDialogNoSelection),
      scrollableContent: canSelect
          ? [
              if (canSelectVideo)
                ..._buildSection(
                  icon: AIcons.streamVideo,
                  title: context.l10n.videoStreamSelectionDialogVideo,
                  streams: _videoStreams,
                  current: _currentVideo,
                  setter: (v) => _currentVideo = v,
                ),
              if (canSelectAudio)
                ..._buildSection(
                  icon: AIcons.streamAudio,
                  title: context.l10n.videoStreamSelectionDialogAudio,
                  streams: _audioStreams,
                  current: _currentAudio,
                  setter: (v) => _currentAudio = v,
                ),
              if (canSelectText)
                ..._buildSection(
                  icon: AIcons.streamText,
                  title: context.l10n.videoStreamSelectionDialogText,
                  streams: _textStreams,
                  current: _currentText,
                  setter: (v) => _currentText = v,
                ),
              const SizedBox(height: 8),
            ]
          : null,
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
        ),
        if (canSelect)
          TextButton(
            onPressed: () => _submit(context),
            child: Text(context.l10n.applyButtonLabel),
          ),
      ],
    );
  }

  static String _formatLanguage(String value) {
    final language = Language.living639_2.firstWhereOrNull((language) => language.iso639_2 == value);
    return language?.native ?? value;
  }

  String _commonStreamName(StreamSummary? stream) {
    if (stream == null) return context.l10n.videoStreamSelectionDialogOff;
    final title = stream.title;
    final language = stream.language;
    if (language != null && language != 'und') {
      final formattedLanguage = _formatLanguage(language);
      return '$formattedLanguage${title != null && title != formattedLanguage ? ' • $title' : ''}';
    } else if (title != null) {
      return title;
    } else {
      return '${context.l10n.videoStreamSelectionDialogTrack} ${stream.index} (${stream.codecName})';
    }
  }

  String _streamName(StreamSummary? stream) {
    final common = _commonStreamName(stream);
    if (stream != null && stream.type == StreamType.video) {
      final w = stream.width;
      final h = stream.height;
      if (w != null && h != null) {
        return '$common • $w${AvesEntry.resolutionSeparator}$h';
      }
    }
    return common;
  }

  DropdownMenuItem<StreamSummary> _buildMenuItem(StreamSummary? value) {
    return DropdownMenuItem(
      value: value,
      child: Text(_streamName(value)),
    );
  }

  Widget _buildSelectedItem(StreamSummary? v) {
    return Align(
      alignment: AlignmentDirectional.centerStart,
      child: Text(
        _streamName(v),
        softWrap: false,
        overflow: TextOverflow.fade,
        maxLines: 1,
      ),
    );
  }

  List<Widget> _buildSection({
    required IconData icon,
    required String title,
    required List<StreamSummary?> streams,
    required StreamSummary? current,
    required ValueSetter<StreamSummary?> setter,
  }) {
    return [
      Padding(
        padding: const EdgeInsets.only(left: 16, top: 16, right: 16),
        child: Row(
          children: [
            Icon(icon),
            const SizedBox(width: 16),
            Text(title),
          ],
        ),
      ),
      Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16),
        child: DropdownButton<StreamSummary>(
          items: streams.map(_buildMenuItem).toList(),
          selectedItemBuilder: (context) => streams.map(_buildSelectedItem).toList(),
          value: current,
          onChanged: streams.length > 1 ? (newValue) => setState(() => setter(newValue)) : null,
          isExpanded: true,
          // use a different shade to avoid having the same background
          // on the dialog (using the theme `dialogBackgroundColor`)
          // and on the dropdown (using the theme `canvasColor`)
          dropdownColor: Colors.grey.shade800,
        ),
      ),
    ];
  }

  void _submit(BuildContext context) => Navigator.pop(context, {
        StreamType.video: _currentVideo,
        StreamType.audio: _currentAudio,
        StreamType.text: _currentText,
      });
}
