

part of 'player_page.dart';

extension _AndroidChrome on _PlayerPageState {
  static const _androidSpeeds = [
    0.25,
    0.5,
    0.75,
    1.0,
    1.25,
    1.5,
    1.75,
    2.0,
    2.5,
    3.0,
  ];

  void _seekBy(int seconds) {
    final session = _session;
    if (session == null) return;
    final target = session.engine.state.position + Duration(seconds: seconds);
    unawaited(
      session.engine.seek(target < Duration.zero ? Duration.zero : target),
    );
  }

  void _onAndroidSpeedIncrement() {
    final idx = _androidSpeeds.indexWhere((s) => s > _playbackRate);
    if (idx >= 0) _setPlaybackRate(_androidSpeeds[idx]);
  }

  void _onAndroidSpeedDecrement() {
    final idx = _androidSpeeds.lastIndexWhere((s) => s < _playbackRate);
    if (idx >= 0) _setPlaybackRate(_androidSpeeds[idx]);
  }

  List<Widget> _buildAndroidTopBar(PlaybackSession session) {
    return [
      IconButton(
        icon: const Icon(Icons.arrow_back, color: Colors.white),
        onPressed: _goBack,
      ),
      const SizedBox(width: 4),
      Expanded(
        child: Text(
          _buildTitleText(session),
          style: const TextStyle(color: Colors.white, fontSize: 15),
          overflow: TextOverflow.ellipsis,
        ),
      ),
      Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.end,
        children: [
          Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              QualityBadge(quality: _activeQualityInfo(session)),
              const SizedBox(width: 8),
              NetworkSpeedIndicator(monitor: session.speedMonitor),
            ],
          ),
          const SizedBox(height: 6),
          const PlayerClockText(),
        ],
      ),
      const SizedBox(width: 4),
      PopupMenuButton<String>(
        icon: const Icon(Icons.more_vert, color: Colors.white),
        onSelected: (value) {
          switch (value) {
            case 'fit:contain':
              _setVideoFit(BoxFit.contain);
            case 'fit:fill':
              _setVideoFit(BoxFit.fill);
            case 'fit:cover':
              _setVideoFit(BoxFit.cover);
            case 'pip':
              unawaited(_enterAndroidPip());
            case 'debug_hud':
              _debugHudVisible.value = !_debugHudVisible.value;
          }
        },
        itemBuilder: (context) => [
          CheckedPopupMenuItem<String>(
            value: 'fit:contain',
            checked: _videoFit.value == BoxFit.contain,
            child: const Text('画面比例 - 适应'),
          ),
          CheckedPopupMenuItem<String>(
            value: 'fit:fill',
            checked: _videoFit.value == BoxFit.fill,
            child: const Text('画面比例 - 拉伸'),
          ),
          CheckedPopupMenuItem<String>(
            value: 'fit:cover',
            checked: _videoFit.value == BoxFit.cover,
            child: const Text('画面比例 - 裁切'),
          ),
          if (_androidPip.isSupported && !_isAndroidLocked)
            const PopupMenuItem<String>(value: 'pip', child: Text('画中画')),
          if (kDebugMode)
            CheckedPopupMenuItem<String>(
              value: 'debug_hud',
              checked: _debugHudVisible.value,
              child: const Text('调试 HUD'),
            ),
        ],
      ),
    ];
  }

  Widget _buildAndroidBottomBar(PlaybackSession session) {
    final seekSettings = ref.read(playerSettingsProvider).value;
    final seekFwd = seekSettings?.seekForwardSeconds ?? 15;
    final seekBwd = seekSettings?.seekBackwardSeconds ?? 15;

    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: Row(
            children: [
              _AndroidTimeLabel(
                player: session.engine,
                showPosition: true,
                seekPreview: _seekPreview,
              ),
              const SizedBox(width: 8),
              Expanded(
                child: DebouncedSeekBar(
                  player: session.engine,
                  onDragChanged: (t) => _seekPreview.value = t,
                ),
              ),
              const SizedBox(width: 8),
              _AndroidTimeLabel(
                player: session.engine,
                showPosition: false,
                seekPreview: _seekPreview,
              ),
            ],
          ),
        ),
        Padding(
          padding: const EdgeInsets.fromLTRB(8, 4, 8, 0),
          child: LayoutBuilder(
            builder: (context, constraints) {
              final hasVersionSwitch =
                  session.context.allMediaSources.length > 1 ||
                  _crossServerCards.isNotEmpty;
              final hasEpisode = session.context.seriesId != null;

              const alwaysVisibleWidth = 120.0 + 96.0 + 96.0;

              final secondaryMenuEntries = <PopupMenuEntry<VoidCallback>>[
                if (hasVersionSwitch)
                  PopupMenuItem<VoidCallback>(
                    value: _toggleVersionBar,
                    child: const ListTile(
                      leading: Icon(Icons.switch_video),
                      title: Text('切换版本'),
                      dense: true,
                    ),
                  ),
                PopupMenuItem<VoidCallback>(
                  value: () => _togglePanel(PlayerPanelCategory.danmaku),
                  child: const ListTile(
                    leading: Icon(Icons.comment_outlined),
                    title: Text('弹幕'),
                    dense: true,
                  ),
                ),
                PopupMenuItem<VoidCallback>(
                  value: () => _togglePanel(PlayerPanelCategory.speed),
                  child: const ListTile(
                    leading: Icon(Icons.speed),
                    title: Text('倍速'),
                    dense: true,
                  ),
                ),
                if (hasEpisode)
                  PopupMenuItem<VoidCallback>(
                    value: () => _togglePanel(PlayerPanelCategory.episode),
                    child: const ListTile(
                      leading: Icon(Icons.playlist_play),
                      title: Text('选集'),
                      dense: true,
                    ),
                  ),
              ];

              final secondaryInlineCount =
                  (hasVersionSwitch ? 1 : 0) + 2 + (hasEpisode ? 1 : 0);
              const iconButtonWidth = 48.0;
              final overflowMenuButtonWidth = secondaryMenuEntries.isNotEmpty
                  ? iconButtonWidth
                  : 0.0;
              final showAllInline =
                  constraints.maxWidth >=
                  alwaysVisibleWidth +
                      secondaryInlineCount * iconButtonWidth +
                      overflowMenuButtonWidth;

              return Row(
                children: [
                  AndroidBottomTransportControls(
                    player: session.engine,
                    hasPrev: _playlist?.hasPrev ?? false,
                    hasNext: _playlist?.hasNext ?? false,
                    onPrevItem: () => unawaited(_playlist!.prev()),
                    onNextItem: () => unawaited(_playlist!.next()),
                  ),
                  IconButton(
                    tooltip: '快退',
                    icon: const Icon(
                      Icons.fast_rewind,
                      color: Colors.white,
                      size: 24,
                    ),
                    onPressed: () => _seekBy(-seekBwd),
                  ),
                  IconButton(
                    tooltip: '快进',
                    icon: const Icon(
                      Icons.fast_forward,
                      color: Colors.white,
                      size: 24,
                    ),
                    onPressed: () => _seekBy(seekFwd),
                  ),
                  const Spacer(),
                  if (showAllInline) ...[
                    if (hasVersionSwitch)
                      IconButton(
                        icon: const Icon(
                          Icons.switch_video,
                          color: Colors.white,
                          size: 24,
                        ),
                        onPressed: _toggleVersionBar,
                        tooltip: '切换版本',
                      ),
                  ],
                  IconButton(
                    icon: const Icon(
                      Icons.subtitles_outlined,
                      color: Colors.white,
                      size: 24,
                    ),
                    onPressed: () => _togglePanel(PlayerPanelCategory.subtitle),
                    tooltip: '字幕',
                  ),
                  IconButton(
                    icon: const Icon(
                      Icons.audiotrack,
                      color: Colors.white,
                      size: 24,
                    ),
                    onPressed: () => _togglePanel(PlayerPanelCategory.audio),
                    tooltip: '音轨',
                  ),
                  if (showAllInline) ...[
                    IconButton(
                      icon: const Icon(
                        Icons.comment_outlined,
                        color: Colors.white,
                        size: 24,
                      ),
                      onPressed: () =>
                          _togglePanel(PlayerPanelCategory.danmaku),
                      tooltip: '弹幕',
                    ),
                    IconButton(
                      icon: const Icon(
                        Icons.speed,
                        color: Colors.white,
                        size: 24,
                      ),
                      onPressed: () => _togglePanel(PlayerPanelCategory.speed),
                      tooltip: '倍速',
                    ),
                    if (hasEpisode)
                      IconButton(
                        icon: const Icon(
                          Icons.playlist_play,
                          color: Colors.white,
                          size: 24,
                        ),
                        onPressed: () =>
                            _togglePanel(PlayerPanelCategory.episode),
                        tooltip: '选集',
                      ),
                  ] else if (secondaryMenuEntries.isNotEmpty)
                    PopupMenuButton<VoidCallback>(
                      icon: const Icon(
                        Icons.more_vert,
                        color: Colors.white,
                        size: 24,
                      ),
                      tooltip: '更多',
                      onSelected: (callback) => callback(),
                      itemBuilder: (_) => secondaryMenuEntries,
                    ),
                ],
              );
            },
          ),
        ),
      ],
    );
  }

  Widget _buildAndroidLeftBar() {
    return AndroidLockButton(onLock: () => _setAndroidLocked(true));
  }

  Widget _buildAndroidRightBar(PlaybackSession session) {
    return AndroidSpeedStrip(
      currentRate: _playbackRate,
      onIncrement: _onAndroidSpeedIncrement,
      onDecrement: _onAndroidSpeedDecrement,
      onTapRate: () => _togglePanel(PlayerPanelCategory.speed),
    );
  }

  Widget _buildAndroidGestureLayer(
    PlaybackSession session,
    VoidCallback toggleControls,
    VoidCallback showControls,
  ) {
    return AndroidGestureLayer(
      enabled: !_isAndroidLocked,
      player: session.engine,
      onToggleControls: toggleControls,
      onDoubleTapCenter: () {
        unawaited(
          session.engine.state.playing
              ? session.engine.pause()
              : session.engine.play(),
        );
        showControls();
      },
      onDoubleTapLeft: () {
        final secs =
            ref.read(playerSettingsProvider).value?.seekBackwardSeconds ?? 15;
        _seekBy(-secs);
        setState(
          () => _androidFeedbackData = GestureFeedbackData(
            kind: GestureFeedbackKind.doubleTapSeek,
            doubleTapSeconds: secs,
            doubleTapIsLeft: true,
          ),
        );
        Future.delayed(const Duration(milliseconds: 600), () {
          if (mounted) {
            setState(
              () => _androidFeedbackData = const GestureFeedbackData.none(),
            );
          }
        });
      },
      onDoubleTapRight: () {
        final secs =
            ref.read(playerSettingsProvider).value?.seekForwardSeconds ?? 15;
        _seekBy(secs);
        setState(
          () => _androidFeedbackData = GestureFeedbackData(
            kind: GestureFeedbackKind.doubleTapSeek,
            doubleTapSeconds: secs,
            doubleTapIsLeft: false,
          ),
        );
        Future.delayed(const Duration(milliseconds: 600), () {
          if (mounted) {
            setState(
              () => _androidFeedbackData = const GestureFeedbackData.none(),
            );
          }
        });
      },
      onBrightnessChange: (delta) async {
        final gen = _brightnessGen;
        try {
          final current = await ScreenBrightness.instance.application;
          if (!mounted || gen != _brightnessGen) return;
          final next = (current + delta).clamp(0.0, 1.0);
          await ScreenBrightness.instance.setApplicationScreenBrightness(next);
          if (!mounted || gen != _brightnessGen) return;
          _lastSetBrightness = next;
          setState(
            () => _androidFeedbackData = GestureFeedbackData(
              kind: GestureFeedbackKind.brightness,
              value: next,
            ),
          );
        } catch (_) {}
      },
      onVolumeChange: (delta) {
        final oldStep = (_volumeFraction * _maxVolumeLevel).round();
        _volumeFraction = (_volumeFraction + delta).clamp(0.0, 1.0);
        final newStep = (_volumeFraction * _maxVolumeLevel).round();
        if (newStep != oldStep) {
          unawaited(
            SystemAudioController.instance.adjustVolume(newStep - oldStep),
          );
        }
        _systemVolumeOsdTimer?.cancel();
        setState(
          () => _androidFeedbackData = GestureFeedbackData(
            kind: GestureFeedbackKind.volume,
            value: _volumeFraction,
          ),
        );
      },
      onVerticalDragEnd: () {
        _brightnessGen++;
        if (_lastSetBrightness != null) {
          unawaited(
            _playerSettingsNotifier.setAndroidBrightness(_lastSetBrightness!),
          );
          _lastSetBrightness = null;
        }
        setState(() => _androidFeedbackData = const GestureFeedbackData.none());
      },
      onHorizontalSeekStart: () {},
      onHorizontalSeekUpdate: (seekDelta) {
        final current = session.engine.state.position;
        final duration = session.engine.state.duration;
        final target = current + seekDelta;
        final clamped = target < Duration.zero
            ? Duration.zero
            : (target > duration ? duration : target);
        _seekPreview.value = clamped;
        setState(
          () => _androidFeedbackData = GestureFeedbackData(
            kind: GestureFeedbackKind.seek,
            seekTarget: clamped,
            totalDuration: duration,
            seekIsForward: !seekDelta.isNegative,
          ),
        );
      },
      onHorizontalSeekEnd: () {
        final target = _seekPreview.value;
        if (target != null) {
          unawaited(session.engine.seek(target));
        }
        _seekPreview.value = null;
        setState(() => _androidFeedbackData = const GestureFeedbackData.none());
      },
      onLongPressStart: (isLeftSide) {
        final settings = ref.read(playerSettingsProvider).value;
        final holdSpeed = isLeftSide
            ? (settings?.keyboardHoldLeftSpeed ?? 0.5)
            : (settings?.keyboardHoldRightSpeed ?? 3.0);
        _savedRateBeforeLongPress = _playbackRate;
        _setPlaybackRate(holdSpeed);
        setState(
          () => _androidFeedbackData = GestureFeedbackData(
            kind: GestureFeedbackKind.longPressSpeed,
            speedRate: holdSpeed,
          ),
        );
      },
      onLongPressEnd: () {
        final restore = _savedRateBeforeLongPress ?? 1.0;
        _savedRateBeforeLongPress = null;
        _setPlaybackRate(restore);
        if (_androidFeedbackData.kind == GestureFeedbackKind.longPressSpeed) {
          setState(
            () => _androidFeedbackData = const GestureFeedbackData.none(),
          );
        }
      },
    );
  }

  Widget _buildAndroidGestureFeedback() {
    return AndroidGestureFeedback(data: _androidFeedbackData);
  }

  Widget _buildAndroidDrawerLayer(PlaybackSession session) {
    return AnimatedBuilder(
      animation: _drawer,
      builder: (context, _) {
        final category = _drawer.category;
        if (category == null) return const SizedBox.shrink();
        return Stack(
          children: [
            GestureDetector(
              behavior: HitTestBehavior.opaque,
              onTap: _drawer.close,
              child: const ColoredBox(
                color: Colors.black38,
                child: SizedBox.expand(),
              ),
            ),
            Positioned(
              right: 0,
              top: 0,
              bottom: 0,
              width: 320,
              child: Material(
                color: const Color(0xF0181818),
                child: PlayerPanelShell(
                  icon: panelCategoryIcon(category),
                  title: panelCategoryLabel(category),
                  width: double.infinity,
                  onClose: _drawer.close,
                  child: _panelBody(session, category),
                ),
              ),
            ),
          ],
        );
      },
    );
  }
}

class _AndroidTimeLabel extends StatefulWidget {
  const _AndroidTimeLabel({
    required this.player,
    required this.showPosition,
    required this.seekPreview,
  });

  final PlayerEngine player;
  final bool showPosition;
  final ValueNotifier<Duration?> seekPreview;

  @override
  State<_AndroidTimeLabel> createState() => _AndroidTimeLabelState();
}

class _AndroidTimeLabelState extends State<_AndroidTimeLabel> {
  late Duration _position;
  late Duration _duration;
  StreamSubscription<Duration>? _posSub;
  StreamSubscription<Duration>? _durSub;

  @override
  void initState() {
    super.initState();
    _position = widget.player.state.position;
    _duration = widget.player.state.duration;
    _posSub = widget.player.stream.position.listen((p) {
      if (mounted) setState(() => _position = p);
    });
    _durSub = widget.player.stream.duration.listen((d) {
      if (mounted) setState(() => _duration = d);
    });
    if (widget.showPosition) {
      widget.seekPreview.addListener(_onSeekPreviewChanged);
    }
  }

  @override
  void didUpdateWidget(covariant _AndroidTimeLabel oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.player != widget.player) {
      unawaited(_posSub?.cancel());
      unawaited(_durSub?.cancel());
      _position = widget.player.state.position;
      _duration = widget.player.state.duration;
      _posSub = widget.player.stream.position.listen((p) {
        if (mounted) setState(() => _position = p);
      });
      _durSub = widget.player.stream.duration.listen((d) {
        if (mounted) setState(() => _duration = d);
      });
    }
    if (oldWidget.seekPreview != widget.seekPreview) {
      oldWidget.seekPreview.removeListener(_onSeekPreviewChanged);
      if (widget.showPosition) {
        widget.seekPreview.addListener(_onSeekPreviewChanged);
      }
    }
  }

  void _onSeekPreviewChanged() {
    if (mounted) setState(() {});
  }

  @override
  void dispose() {
    unawaited(_posSub?.cancel());
    unawaited(_durSub?.cancel());
    widget.seekPreview.removeListener(_onSeekPreviewChanged);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final Duration display;
    if (widget.showPosition) {
      display = widget.seekPreview.value ?? _position;
    } else {
      display = _duration;
    }
    return Text(
      formatDurationClock(display),
      style: const TextStyle(
        color: Colors.white70,
        fontSize: 12,
        fontFeatures: [FontFeature.tabularFigures()],
      ),
    );
  }
}
