content: import 'dart:convert';
  import 'dart:math';
  
  import 'package:flutter/material.dart';
  import 'package:flutter_hbb/consts.dart';
  import 'package:flutter_hbb/models/peer_model.dart';
  import 'package:flutter_hbb/models/platform_model.dart';
  import 'package:get/get.dart';
  
  import '../common.dart';
  import 'model.dart';
  
  enum PeerTabIndex {
    recent,
    fav,
    lan,
    ab,
    group,
  }
  
  class PeerTabModel with ChangeNotifier {
    WeakReference<FFI> parent;
    int get currentTab => _currentTab;
    int _currentTab = 0; // index in tabNames
    static const int maxTabCount = 5;
    static const List<String> tabNames = [
      'Recent sessions',
      'Favorites',
      'Discovered',
      'Address book',
      'Accessible devices',
    ];
    static const List<IconData> icons = [
      Icons.access_time_filled,
      Icons.star,
      Icons.explore,
      IconFont.addressBook,
      IconFont.deviceGroupFill,
    ];
    List<bool> isEnabled = List.from([
      true,
      true,
      !isWeb && bind.mainGetLocalOption(key: "disable-discovery-panel") != "Y",
      !(bind.isDisableAb() || bind.isDisableAccount()),
      !(bind.isDisableGroupPanel() || bind.isDisableAccount()),
    ]);
    final List<bool> _isVisible = List.filled(maxTabCount, true, growable: false);
    List<bool> get isVisibleEnabled => () {
          final list = _isVisible.toList();
          for (int i = 0; i < maxTabCount; i++) {
            list[i] = list[i] && isEnabled[i];
          }
          return list;
        }();
    final List<int> orders =
        List.generate(maxTabCount, (index) => index, growable: false);
    List<int> get visibleEnabledOrderedIndexs =>
        orders.where((e) => isVisibleEnabled[e]).toList();
    List<Peer> _selectedPeers = List.empty(growable: true);
    List<Peer> get selectedPeers => _selectedPeers;
    bool _multiSelectionMode = false;
    bool get multiSelectionMode => _multiSelectionMode;
    List<Peer> _currentTabCachedPeers = List.empty(growable: true);
    List<Peer> get currentTabCachedPeers => _currentTabCachedPeers;
    bool _isShiftDown = false;
    bool get isShiftDown => _isShiftDown;
    String _lastId = '';
    String get lastId => _lastId;
  
    PeerTabModel(this.parent) {
      // visible
      try {
        final option = bind.getLocalFlutterOption(k: kOptionPeerTabVisible);
        if (option.isNotEmpty) {
          List<dynamic> decodeList = jsonDecode(option);
          if (decodeList.length == _isVisible.length) {
            for (int i = 0; i < _isVisible.length; i++) {
              if (decodeList[i] is bool) {
                _isVisible[i] = decodeList[i];
              }
            }
          }
        }
      } catch (e) {
        debugPrint("failed to get peer tab visible list:$e");
      }
      // order
      try {
        final option = bind.getLocalFlutterOption(k: kOptionPeerTabOrder);
        if (option.isNotEmpty) {
          List<dynamic> decodeList = jsonDecode(option);
          if (decodeList.length == maxTabCount) {
            var sortedList = decodeList.toList();
            sortedList.sort();
            bool valid = true;
            for (int i = 0; i < maxTabCount; i++) {
              if (sortedList[i] is! int || sortedList[i] != i) {
                valid = false;
              }
            }
            if (valid) {
              for (int i = 0; i < orders.length; i++) {
                orders[i] = decodeList[i];
              }
            }
          }
        }
      } catch (e) {
        debugPrint("failed to get peer tab order list: $e");
      }
      // init currentTab
      _currentTab =
          int.tryParse(bind.getLocalFlutterOption(k: kOptionPeerTabIndex)) ?? 0;
      if (_currentTab < 0 || _currentTab >= maxTabCount) {
        _currentTab = 0;
      }
      _trySetCurrentTabToFirstVisibleEnabled();
    }
  
    setCurrentTab(int index) {
      if (_currentTab != index) {
        _currentTab = index;
        notifyListeners();
      }
    }
  
    String tabTooltip(int index) {
      if (index >= 0 && index < tabNames.length) {
        return translate(tabNames[index]);
      }
      return index.toString();
    }
  
    IconData tabIcon(int index) {
      if (index >= 0 && index < icons.length) {
        return icons[index];
      }
      return Icons.help;
    }
  
    setMultiSelectionMode(bool mode) {
      _multiSelectionMode = mode;
      if (!mode) {
        _selectedPeers.clear();
        _lastId = '';
      }
      notifyListeners();
    }
  
    select(Peer peer) {
      if (!_multiSelectionMode) {
        // https://github.com/flutter/flutter/issues/101275#issuecomment-1604541700
        // After onTap, the shift key should be pressed for a while when not in multiselection mode,
        // because onTap is delayed when onDoubleTap is not null
        if (isDesktop || isWebDesktop) return;
        _multiSelectionMode = true;
      }
      final cached = _currentTabCachedPeers.map((e) => e.id).toList();
      int thisIndex = cached.indexOf(peer.id);
      int lastIndex = cached.indexOf(_lastId);
      if (_isShiftDown && thisIndex >= 0 && lastIndex >= 0) {
        int start = min(thisIndex, lastIndex);
        int end = max(thisIndex, lastIndex);
        bool remove = isPeerSelected(peer.id);
        for (var i = start; i <= end; i++) {
          if (remove) {
            if (isPeerSelected(cached[i])) {
              _selectedPeers.removeWhere((p) => p.id == cached[i]);
            }
          } else {
            if (!isPeerSelected(cached[i])) {
              _selectedPeers.add(_currentTabCachedPeers[i]);
            }
          }
        }
      } else {
        if (isPeerSelected(peer.id)) {
          _selectedPeers.removeWhere((p) => p.id == peer.id);
        } else {
          _selectedPeers.add(peer);
        }
      }
      _lastId = peer.id;
      notifyListeners();
    }
  
    // `notifyListeners()` will cause many rebuilds.
    // So, we need to reduce the calls to "notifyListeners()" only when necessary.
    // A better way is to use a new model.
    setCurrentTabCachedPeers(List<Peer> peers) {
      Future.delayed(Duration.zero, () {
        final isPreEmpty = _currentTabCachedPeers.isEmpty;
        _currentTabCachedPeers = peers;
        final isNowEmpty = _currentTabCachedPeers.isEmpty;
        if (isPreEmpty != isNowEmpty) {
          notifyListeners();
        }
      });
    }
  
    selectAll() {
      _selectedPeers = _currentTabCachedPeers.toList();
      notifyListeners();
    }
  
    bool isPeerSelected(String id) {
      return selectedPeers.firstWhereOrNull((p) => p.id == id) != null;
    }
  
    setShiftDown(bool v) {
      if (_isShiftDown != v) {
        _isShiftDown = v;
        if (_multiSelectionMode) {
          notifyListeners();
        }
      }
    }
  
    setTabVisible(int index, bool visible) {
      if (index >= 0 && index < maxTabCount) {
        if (_isVisible[index] != visible) {
          _isVisible[index] = visible;
          if (index == _currentTab && !visible) {
            _trySetCurrentTabToFirstVisibleEnabled();
          } else if (visible && visibleEnabledOrderedIndexs.length == 1) {
            _currentTab = index;
          }
          try {
            bind.setLocalFlutterOption(
                k: kOptionPeerTabVisible, v: jsonEncode(_isVisible));
          } catch (_) {}
          notifyListeners();
        }
      }
    }
  
    _trySetCurrentTabToFirstVisibleEnabled() {
      if (!visibleEnabledOrderedIndexs.contains(_currentTab)) {
        if (visibleEnabledOrderedIndexs.isNotEmpty) {
          _currentTab = visibleEnabledOrderedIndexs.first;
        }
      }
    }
  
    reorder(int oldIndex, int newIndex) {
      if (oldIndex < newIndex) {
        newIndex -= 1;
      }
      if (oldIndex < 0 || oldIndex >= visibleEnabledOrderedIndexs.length) {
        return;
      }
      if (newIndex < 0 || newIndex >= visibleEnabledOrderedIndexs.length) {
        return;
      }
      final oldTabValue = visibleEnabledOrderedIndexs[oldIndex];
      final newTabValue = visibleEnabledOrderedIndexs[newIndex];
      int oldValueIndex = orders.indexOf(oldTabValue);
      int newValueIndex = orders.indexOf(newTabValue);
      final list = orders.toList();
      if (oldIndex != -1 && newIndex != -1) {
        list.removeAt(oldValueIndex);
        list.insert(newValueIndex, oldTabValue);
        for (int i = 0; i < list.length; i++) {
          orders[i] = list[i];
        }
        bind.setLocalFlutterOption(k: kOptionPeerTabOrder, v: jsonEncode(orders));
        notifyListeners();
      }
    }
  }
  