content: import 'package:auto_size_text/auto_size_text.dart';
  import 'package:debounce_throttle/debounce_throttle.dart';
  import 'package:flutter/material.dart';
  import 'package:flutter_hbb/common.dart';
  import 'package:flutter_hbb/models/platform_model.dart';
  import 'package:get/get.dart';
  import 'package:provider/provider.dart';
  
  import '../../consts.dart';
  import '../../desktop/widgets/tabbar_widget.dart';
  import '../../models/chat_model.dart';
  import '../../models/model.dart';
  import 'chat_page.dart';
  
  class DraggableChatWindow extends StatelessWidget {
    const DraggableChatWindow(
        {Key? key,
        this.position = Offset.zero,
        required this.width,
        required this.height,
        required this.chatModel})
        : super(key: key);
  
    final Offset position;
    final double width;
    final double height;
    final ChatModel chatModel;
  
    @override
    Widget build(BuildContext context) {
      if (draggablePositions.chatWindow.isInvalid()) {
        draggablePositions.chatWindow.update(position);
      }
      return isIOS
          ? IOSDraggable(
              position: draggablePositions.chatWindow,
              chatModel: chatModel,
              width: width,
              height: height,
              builder: (context) {
                return Column(
                  children: [
                    _buildMobileAppBar(context),
                    Expanded(
                      child: ChatPage(chatModel: chatModel),
                    ),
                  ],
                );
              },
            )
          : Draggable(
              checkKeyboard: true,
              position: draggablePositions.chatWindow,
              width: width,
              height: height,
              chatModel: chatModel,
              builder: (context, onPanUpdate) {
                final child = Scaffold(
                  resizeToAvoidBottomInset: false,
                  appBar: CustomAppBar(
                    onPanUpdate: onPanUpdate,
                    appBar: (isDesktop || isWebDesktop)
                        ? _buildDesktopAppBar(context)
                        : _buildMobileAppBar(context),
                  ),
                  body: ChatPage(chatModel: chatModel),
                );
                return Container(
                    decoration:
                        BoxDecoration(border: Border.all(color: MyTheme.border)),
                    child: child);
              });
    }
  
    Widget _buildMobileAppBar(BuildContext context) {
      return Container(
        color: Theme.of(context).colorScheme.primary,
        height: 50,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Padding(
                padding: const EdgeInsets.symmetric(horizontal: 15),
                child: Text(
                  translate("Chat"),
                  style: const TextStyle(
                      color: Colors.white,
                      fontFamily: 'WorkSans',
                      fontWeight: FontWeight.bold,
                      fontSize: 20),
                )),
            Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                IconButton(
                    onPressed: () {
                      chatModel.hideChatWindowOverlay();
                    },
                    icon: const Icon(
                      Icons.keyboard_arrow_down,
                      color: Colors.white,
                    )),
                IconButton(
                    onPressed: () {
                      chatModel.hideChatWindowOverlay();
                      chatModel.hideChatIconOverlay();
                    },
                    icon: const Icon(
                      Icons.close,
                      color: Colors.white,
                    ))
              ],
            )
          ],
        ),
      );
    }
  
    Widget _buildDesktopAppBar(BuildContext context) {
      return Container(
        decoration: BoxDecoration(
            border: Border(
                bottom: BorderSide(
                    color: Theme.of(context).hintColor.withOpacity(0.4)))),
        height: 38,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Padding(
                padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
                child: Obx(() => Opacity(
                    opacity: chatModel.isWindowFocus.value ? 1.0 : 0.4,
                    child: Row(children: [
                      Icon(Icons.chat_bubble_outline,
                          size: 20, color: Theme.of(context).colorScheme.primary),
                      SizedBox(width: 6),
                      Text(translate("Chat"))
                    ])))),
            Padding(
                padding: EdgeInsets.all(2),
                child: ActionIcon(
                  message: 'Close',
                  icon: IconFont.close,
                  onTap: chatModel.hideChatWindowOverlay,
                  isClose: true,
                  boxSize: 32,
                ))
          ],
        ),
      );
    }
  }
  
  class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
    final GestureDragUpdateCallback onPanUpdate;
    final Widget appBar;
  
    const CustomAppBar(
        {Key? key, required this.onPanUpdate, required this.appBar})
        : super(key: key);
  
    @override
    Widget build(BuildContext context) {
      return GestureDetector(onPanUpdate: onPanUpdate, child: appBar);
    }
  
    @override
    Size get preferredSize => const Size.fromHeight(kToolbarHeight);
  }
  
  /// floating buttons of back/home/recent actions for android
  class DraggableMobileActions extends StatelessWidget {
    DraggableMobileActions(
        {this.onBackPressed,
        this.onRecentPressed,
        this.onHomePressed,
        this.onHidePressed,
        required this.position,
        required this.width,
        required this.height,
        required this.scale});
  
    final double scale;
    final DraggableKeyPosition position;
    final double width;
    final double height;
    final VoidCallback? onBackPressed;
    final VoidCallback? onHomePressed;
    final VoidCallback? onRecentPressed;
    final VoidCallback? onHidePressed;
  
    @override
    Widget build(BuildContext context) {
      return Draggable(
          position: position,
          width: scale * width,
          height: scale * height,
          builder: (_, onPanUpdate) {
            return GestureDetector(
                onPanUpdate: onPanUpdate,
                child: Card(
                    color: Colors.transparent,
                    shadowColor: Colors.transparent,
                    child: Container(
                      decoration: BoxDecoration(
                          color: MyTheme.accent.withOpacity(0.4),
                          borderRadius:
                              BorderRadius.all(Radius.circular(15 * scale))),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceAround,
                        children: [
                          IconButton(
                              color: Colors.white,
                              onPressed: onBackPressed,
                              splashRadius: kDesktopIconButtonSplashRadius,
                              icon: const Icon(Icons.arrow_back),
                              iconSize: 24 * scale),
                          IconButton(
                              color: Colors.white,
                              onPressed: onHomePressed,
                              splashRadius: kDesktopIconButtonSplashRadius,
                              icon: const Icon(Icons.home),
                              iconSize: 24 * scale),
                          IconButton(
                              color: Colors.white,
                              onPressed: onRecentPressed,
                              splashRadius: kDesktopIconButtonSplashRadius,
                              icon: const Icon(Icons.more_horiz),
                              iconSize: 24 * scale),
                          const VerticalDivider(
                            width: 0,
                            thickness: 2,
                            indent: 10,
                            endIndent: 10,
                          ),
                          IconButton(
                              color: Colors.white,
                              onPressed: onHidePressed,
                              splashRadius: kDesktopIconButtonSplashRadius,
                              icon: const Icon(Icons.keyboard_arrow_down),
                              iconSize: 24 * scale),
                        ],
                      ),
                    )));
          });
    }
  }
  
  class DraggableKeyPosition {
    final String key;
    Offset _pos;
    late Debouncer<int> _debouncerStore;
    DraggableKeyPosition(this.key)
        : _pos = DraggablePositions.kInvalidDraggablePosition;
  
    get pos => _pos;
  
    _loadPosition(String k) {
      final value = bind.getLocalFlutterOption(k: k);
      if (value.isNotEmpty) {
        final parts = value.split(',');
        if (parts.length == 2) {
          return Offset(double.parse(parts[0]), double.parse(parts[1]));
        }
      }
      return DraggablePositions.kInvalidDraggablePosition;
    }
  
    load() {
      _pos = _loadPosition(key);
      _debouncerStore = Debouncer<int>(const Duration(milliseconds: 500),
          onChanged: (v) => _store(), initialValue: 0);
    }
  
    update(Offset pos) {
      _pos = pos;
      _triggerStore();
    }
  
    // Adjust position to keep it in the screen
    // Only used for desktop and web desktop
    tryAdjust(double w, double h, double scale) {
      final size = MediaQuery.of(Get.context!).size;
      w = w * scale;
      h = h * scale;
      double x = _pos.dx;
      double y = _pos.dy;
      if (x + w > size.width) {
        x = size.width - w;
      }
      final tabBarHeight = isDesktop ? kDesktopRemoteTabBarHeight : 0;
      if (y + h > (size.height - tabBarHeight)) {
        y = size.height - tabBarHeight - h;
      }
      if (x < 0) {
        x = 0;
      }
      if (y < 0) {
        y = 0;
      }
      if (x != _pos.dx || y != _pos.dy) {
        update(Offset(x, y));
      }
    }
  
    isInvalid() {
      return _pos == DraggablePositions.kInvalidDraggablePosition;
    }
  
    _triggerStore() => _debouncerStore.value = _debouncerStore.value + 1;
    _store() {
      bind.setLocalFlutterOption(k: key, v: '${_pos.dx},${_pos.dy}');
    }
  }
  
  class DraggablePositions {
    static const kChatWindow = 'draggablePositionChat';
    static const kMobileActions = 'draggablePositionMobile';
    static const kIOSDraggable = 'draggablePositionIOS';
  
    static const kInvalidDraggablePosition = Offset(-999999, -999999);
    final chatWindow = DraggableKeyPosition(kChatWindow);
    final mobileActions = DraggableKeyPosition(kMobileActions);
    final iOSDraggable = DraggableKeyPosition(kIOSDraggable);
  
    load() {
      chatWindow.load();
      mobileActions.load();
      iOSDraggable.load();
    }
  }
  
  DraggablePositions draggablePositions = DraggablePositions();
  
  class Draggable extends StatefulWidget {
    Draggable(
        {Key? key,
        this.checkKeyboard = false,
        this.checkScreenSize = false,
        required this.position,
        required this.width,
        required this.height,
        this.chatModel,
        required this.builder})
        : super(key: key);
  
    final bool checkKeyboard;
    final bool checkScreenSize;
    final DraggableKeyPosition position;
    final double width;
    final double height;
    final ChatModel? chatModel;
    final Widget Function(BuildContext, GestureDragUpdateCallback) builder;
  
    @override
    State<StatefulWidget> createState() => _DraggableState(chatModel);
  }
  
  class _DraggableState extends State<Draggable> {
    late ChatModel? _chatModel;
    bool _keyboardVisible = false;
    double _saveHeight = 0;
    double _lastBottomHeight = 0;
  
    _DraggableState(ChatModel? chatModel) {
      _chatModel = chatModel;
    }
  
    get position => widget.position.pos;
  
    void onPanUpdate(DragUpdateDetails d) {
      final offset = d.delta;
      final size = MediaQuery.of(context).size;
      double x = 0;
      double y = 0;
  
      if (position.dx + offset.dx + widget.width > size.width) {
        x = size.width - widget.width;
      } else if (position.dx + offset.dx < 0) {
        x = 0;
      } else {
        x = position.dx + offset.dx;
      }
  
      if (position.dy + offset.dy + widget.height > size.height) {
        y = size.height - widget.height;
      } else if (position.dy + offset.dy < 0) {
        y = 0;
      } else {
        y = position.dy + offset.dy;
      }
      setState(() {
        widget.position.update(Offset(x, y));
      });
      _chatModel?.setChatWindowPosition(position);
    }
  
    checkScreenSize() {}
  
    checkKeyboard() {
      final bottomHeight = MediaQuery.of(context).viewInsets.bottom;
      final currentVisible = bottomHeight != 0;
  
      // save
      if (!_keyboardVisible && currentVisible) {
        _saveHeight = position.dy;
      }
  
      // reset
      if (_lastBottomHeight > 0 && bottomHeight == 0) {
        setState(() {
          widget.position.update(Offset(position.dx, _saveHeight));
        });
      }
  
      // onKeyboardVisible
      if (_keyboardVisible && currentVisible) {
        final sumHeight = bottomHeight + widget.height;
        final contextHeight = MediaQuery.of(context).size.height;
        if (sumHeight + position.dy > contextHeight) {
          final y = contextHeight - sumHeight;
          setState(() {
            widget.position.update(Offset(position.dx, y));
          });
        }
      }
  
      _keyboardVisible = currentVisible;
      _lastBottomHeight = bottomHeight;
    }
  
    @override
    Widget build(BuildContext context) {
      if (widget.checkKeyboard) {
        checkKeyboard();
      }
      if (widget.checkScreenSize) {
        checkScreenSize();
      }
      return Stack(children: [
        Positioned(
            top: position.dy,
            left: position.dx,
            width: widget.width,
            height: widget.height,
            child: widget.builder(context, onPanUpdate))
      ]);
    }
  }
  
  class IOSDraggable extends StatefulWidget {
    const IOSDraggable(
        {Key? key,
        this.chatModel,
        required this.position,
        required this.width,
        required this.height,
        required this.builder})
        : super(key: key);
  
    final DraggableKeyPosition position;
    final ChatModel? chatModel;
    final double width;
    final double height;
    final Widget Function(BuildContext) builder;
  
    @override
    IOSDraggableState createState() =>
        IOSDraggableState(chatModel, width, height);
  }
  
  class IOSDraggableState extends State<IOSDraggable> {
    late ChatModel? _chatModel;
    late double _width;
    late double _height;
    bool _keyboardVisible = false;
    double _saveHeight = 0;
    double _lastBottomHeight = 0;
  
    IOSDraggableState(ChatModel? chatModel, double w, double h) {
      _chatModel = chatModel;
      _width = w;
      _height = h;
    }
  
    DraggableKeyPosition get position => widget.position;
  
    checkKeyboard() {
      final bottomHeight = MediaQuery.of(context).viewInsets.bottom;
      final currentVisible = bottomHeight != 0;
  
      // save
      if (!_keyboardVisible && currentVisible) {
        _saveHeight = position.pos.dy;
      }
  
      // reset
      if (_lastBottomHeight > 0 && bottomHeight == 0) {
        setState(() {
          position.update(Offset(position.pos.dx, _saveHeight));
        });
      }
  
      // onKeyboardVisible
      if (_keyboardVisible && currentVisible) {
        final sumHeight = bottomHeight + _height;
        final contextHeight = MediaQuery.of(context).size.height;
        if (sumHeight + position.pos.dy > contextHeight) {
          final y = contextHeight - sumHeight;
          setState(() {
            position.update(Offset(position.pos.dx, y));
          });
        }
      }
  
      _keyboardVisible = currentVisible;
      _lastBottomHeight = bottomHeight;
    }
  
    @override
    Widget build(BuildContext context) {
      checkKeyboard();
      return Stack(
        children: [
          Positioned(
            left: position.pos.dx,
            top: position.pos.dy,
            child: GestureDetector(
              onPanUpdate: (details) {
                setState(() {
                  position.update(position.pos + details.delta);
                });
                _chatModel?.setChatWindowPosition(position.pos);
              },
              child: Material(
                child: Container(
                  width: _width,
                  height: _height,
                  decoration:
                      BoxDecoration(border: Border.all(color: MyTheme.border)),
                  child: widget.builder(context),
                ),
              ),
            ),
          ),
        ],
      );
    }
  }
  
  class QualityMonitor extends StatelessWidget {
    final QualityMonitorModel qualityMonitorModel;
    QualityMonitor(this.qualityMonitorModel);
  
    Widget _row(String info, String? value, {Color? rightColor}) {
      return Row(
        children: [
          Expanded(
              flex: 8,
              child: AutoSizeText(info,
                  style: TextStyle(color: Color.fromARGB(255, 210, 210, 210)),
                  textAlign: TextAlign.right,
                  maxLines: 1)),
          Spacer(flex: 1),
          Expanded(
              flex: 8,
              child: AutoSizeText(value ?? '',
                  style: TextStyle(color: rightColor ?? Colors.white),
                  maxLines: 1)),
        ],
      );
    }
  
    @override
    Widget build(BuildContext context) => ChangeNotifierProvider.value(
        value: qualityMonitorModel,
        child: Consumer<QualityMonitorModel>(
            builder: (context, qualityMonitorModel, child) => qualityMonitorModel
                    .show
                ? Container(
                    constraints: BoxConstraints(maxWidth: 200),
                    padding: const EdgeInsets.all(8),
                    color: MyTheme.canvasColor.withAlpha(150),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        _row("Speed", qualityMonitorModel.data.speed ?? '-'),
                        _row("FPS", qualityMonitorModel.data.fps ?? '-'),
                        // let delay be 0 if fps is 0
                        _row(
                            "Delay",
                            "${qualityMonitorModel.data.delay == null ? '-' : (qualityMonitorModel.data.fps ?? "").replaceAll(' ', '').replaceAll('0', '').isEmpty ? 0 : qualityMonitorModel.data.delay}ms",
                            rightColor: Colors.green),
                        _row("Target Bitrate",
                            "${qualityMonitorModel.data.targetBitrate ?? '-'}kb"),
                        _row(
                            "Codec", qualityMonitorModel.data.codecFormat ?? '-'),
                        _row("Chroma", qualityMonitorModel.data.chroma ?? '-'),
                      ],
                    ),
                  )
                : const SizedBox.shrink()));
  }
  
  class BlockableOverlayState extends OverlayKeyState {
    final _middleBlocked = false.obs;
  
    VoidCallback? onMiddleBlockedClick; // to-do use listener
  
    RxBool get middleBlocked => _middleBlocked;
  
    void addMiddleBlockedListener(void Function(bool) cb) {
      _middleBlocked.listen(cb);
    }
  
    void setMiddleBlocked(bool blocked) {
      if (blocked != _middleBlocked.value) {
        _middleBlocked.value = blocked;
      }
    }
  
    void applyFfi(FFI ffi) {
      ffi.dialogManager.setOverlayState(this);
      ffi.chatModel.setOverlayState(this);
      // make remote page penetrable automatically, effective for chat over remote
      onMiddleBlockedClick = () {
        setMiddleBlocked(false);
      };
    }
  }
  
  class BlockableOverlay extends StatelessWidget {
    final Widget underlying;
    final List<OverlayEntry>? upperLayer;
  
    final BlockableOverlayState state;
  
    BlockableOverlay(
        {required this.underlying, required this.state, this.upperLayer});
  
    @override
    Widget build(BuildContext context) {
      final initialEntries = [
        OverlayEntry(builder: (_) => underlying),
  
        /// middle layer
        OverlayEntry(
            builder: (context) => Obx(() => Listener(
                onPointerDown: (_) {
                  state.onMiddleBlockedClick?.call();
                },
                child: Container(
                    color:
                        state.middleBlocked.value ? Colors.transparent : null)))),
      ];
  
      if (upperLayer != null) {
        initialEntries.addAll(upperLayer!);
      }
  
      /// set key
      return Overlay(key: state.key, initialEntries: initialEntries);
    }
  }
  