import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui;
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'dart:async';
import 'package:tuple/tuple.dart';
import 'package:wakelock/wakelock.dart';
import 'common.dart';
import 'model.dart';
import 'dart:io';

final initText = '\1' * 1024;

class RemotePage extends StatefulWidget {
  RemotePage({Key key, this.id}) : super(key: key);

  final String id;

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

class _RemotePageState extends State<RemotePage> {
  Timer _interval;
  Timer _timer;
  bool _showBar = true;
  double _bottom = 0;
  String _value = '';
  double _xOffset = 0;
  double _yOffset = 0;
  double _yOffset0 = 0;
  double _scale = 1;
  bool _mouseTools = false;
  var _drag = false;
  var _right = false;
  var _scroll = false;
  var _more = true;
  var _fn = false;
  final FocusNode _focusNode = FocusNode();
  var _showKeyboard = false;
  var _reconnects = 1;

  @override
  void initState() {
    super.initState();
    FFI.connect(widget.id);
    WidgetsBinding.instance.addPostFrameCallback((_) {
      SystemChrome.setEnabledSystemUIOverlays([]);
      showLoading(translate('Connecting...'), context);
      _interval =
          Timer.periodic(Duration(milliseconds: 30), (timer) => interval());
    });
    Wakelock.enable();
    loadingCancelCallback = () => _interval.cancel();
  }

  @override
  void dispose() {
    _focusNode.dispose();
    FFI.close();
    loadingCancelCallback = null;
    _interval.cancel();
    _timer?.cancel();
    dismissLoading();
    SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
    Wakelock.disable();
    super.dispose();
  }

  void resetTool() {
    _scroll = _drag = _right = false;
    FFI.resetModifiers();
  }

  void interval() {
    var v = MediaQuery.of(context).viewInsets.bottom;
    if (v != _bottom) {
      resetTool();
      setState(() {
        _bottom = v;
        if (v < 100) {
          _showKeyboard = false;
          SystemChrome.setEnabledSystemUIOverlays([]);
        }
      });
    }
    FFI.ffiModel.update(widget.id, context, handleMsgbox);
  }

  void handleMsgbox(Map<String, dynamic> evt, String id) {
    var type = evt['type'];
    var title = evt['title'];
    var text = evt['text'];
    if (type == 're-input-password') {
      wrongPasswordDialog(id, context);
    } else if (type == 'input-password') {
      enterPasswordDialog(id, context);
    } else {
      var hasRetry = evt['hasRetry'] == 'true';
      showMsgBox(type, title, text, hasRetry);
    }
  }

  void showMsgBox(String type, String title, String text, bool hasRetry) {
    msgbox(type, title, text, context);
    if (hasRetry) {
      _timer?.cancel();
      _timer = Timer(Duration(seconds: _reconnects), () {
        FFI.reconnect();
        showLoading(translate('Connecting...'), context);
      });
      _reconnects *= 2;
    } else {
      _reconnects = 1;
    }
  }

  void handleInput(String newValue) {
    var oldValue = _value;
    _value = newValue;
    if (Platform.isIOS) {
      var i = newValue.length - 1;
      for (; i >= 0 && newValue[i] != '\1'; --i) {}
      var j = oldValue.length - 1;
      for (; j >= 0 && oldValue[j] != '\1'; --j) {}
      if (i < j) j = i;
      newValue = newValue.substring(j + 1);
      oldValue = oldValue.substring(j + 1);
      var common = 0;
      for (;
          common < oldValue.length &&
              common < newValue.length &&
              newValue[common] == oldValue[common];
          ++common);
      for (i = 0; i < oldValue.length - common; ++i) {
        FFI.inputKey('VK_BACK');
      }
      if (newValue.length > common) {
        var s = newValue.substring(common);
        if (s.length > 1) {
          FFI.setByName('input_string', s);
        } else {
          inputChar(s);
        }
      }
      return;
    }
    if (oldValue.length > 0 &&
        newValue.length > 0 &&
        oldValue[0] == '\1' &&
        newValue[0] != '\1') {
      // clipboard
      oldValue = '';
    }
    if (newValue.length <= oldValue.length) {
      final char = 'VK_BACK';
      FFI.inputKey(char);
    } else {
      final content = newValue.substring(oldValue.length);
      if (content.length > 1) {
        if (oldValue != '' &&
            content.length == 2 &&
            (content == '""' ||
                content == '()' ||
                content == '[]' ||
                content == '<>' ||
                content == "{}" ||
                content == '”“' ||
                content == '《》' ||
                content == '()' ||
                content == '【】')) {
          // can not only input content[0], because when input ], [ are also auo insert, which cause ] never be input
          FFI.setByName('input_string', content);
          openKeyboard();
          return;
        }
        FFI.setByName('input_string', content);
      } else {
        inputChar(content);
      }
    }
  }

  void inputChar(String char) {
    if (char == '\n') {
      char = 'VK_RETURN';
    } else if (char == ' ') {
      char = 'VK_SPACE';
    }
    FFI.inputKey(char);
  }

  void openKeyboard() {
    // destroy first, so that our _value trick can work
    _value = initText;
    resetMouse();
    setState(() => _showKeyboard = false);
    _timer?.cancel();
    _timer = Timer(Duration(milliseconds: 30), () {
      // show now, and sleep a while to requestFocus to
      // make sure edit ready, so that keyboard wont show/hide/show/hide happen
      setState(() => _showKeyboard = true);
      _timer?.cancel();
      _timer = Timer(Duration(milliseconds: 30), () {
        SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
        SystemChannels.textInput.invokeMethod('TextInput.show');
        _focusNode.requestFocus();
      });
    });
  }

  void resetMouse() {
    _drag = false;
    _scroll = false;
    _right = false;
    _mouseTools = false;
  }

  @override
  Widget build(BuildContext context) {
    final pi = Provider.of<FfiModel>(context).pi;
    final hideKeyboard = Platform.isIOS && _showKeyboard;
    final showActionButton = !_showBar || hideKeyboard;
    EasyLoading.instance.loadingStyle = EasyLoadingStyle.light;
    return WillPopScope(
      onWillPop: () async {
        close();
        return false;
      },
      child: Scaffold(
          floatingActionButton: !showActionButton
              ? null
              : FloatingActionButton(
                  mini: !hideKeyboard,
                  child: Icon(
                      hideKeyboard ? Icons.expand_more : Icons.expand_less),
                  backgroundColor: MyTheme.accent50,
                  onPressed: () {
                    setState(() {
                      if (hideKeyboard) {
                        _showKeyboard = !_showKeyboard;
                      } else {
                        _showBar = !_showBar;
                      }
                    });
                  }),
          bottomNavigationBar: _showBar && pi.displays != null
              ? BottomAppBar(
                  elevation: 10,
                  color: MyTheme.accent,
                  child: Row(
                    mainAxisSize: MainAxisSize.max,
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      Row(children: [
                        IconButton(
                          color: Colors.white,
                          icon: Icon(Icons.clear),
                          onPressed: () {
                            close();
                          },
                        ),
                        IconButton(
                            color: Colors.white,
                            icon: Icon(Icons.keyboard),
                            onPressed: openKeyboard),
                        IconButton(
                          color: Colors.white,
                          icon: Icon(Icons.tv),
                          onPressed: () {
                            setState(() => _showKeyboard = false);
                            showOptions(context);
                          },
                        ),
                        Container(
                            color: _mouseTools ? Colors.blue[500] : null,
                            child: IconButton(
                              color: Colors.white,
                              icon: Icon(Icons.mouse),
                              onPressed: () {
                                setState(() {
                                  _mouseTools = !_mouseTools;
                                  resetTool();
                                  if (_mouseTools) _drag = true;
                                });
                              },
                            )),
                        IconButton(
                          color: Colors.white,
                          icon: Icon(Icons.more_vert),
                          onPressed: () {
                            setState(() => _showKeyboard = false);
                            showActions(context);
                          },
                        ),
                      ]),
                      IconButton(
                          color: Colors.white,
                          icon: Icon(Icons.expand_more),
                          onPressed: () {
                            setState(() => _showBar = !_showBar);
                          }),
                    ],
                  ),
                )
              : null,
          body: FlutterEasyLoading(
            child: GestureDetector(
                onLongPress: () {
                  if (_drag || _scroll) return;
                  FFI.tap(true);
                },
                onTap: () {
                  if (_drag || _scroll) return;
                  FFI.tap(_right);
                },
                onScaleStart: (details) {
                  _scale = 1;
                  _xOffset = details.focalPoint.dx;
                  _yOffset = _yOffset0 = details.focalPoint.dy;
                  if (_drag) {
                    FFI.sendMouse('down', 'left');
                  }
                },
                onScaleUpdate: (details) {
                  var scale = details.scale;
                  if (scale == 1) {
                    if (!_scroll) {
                      var x = details.focalPoint.dx;
                      var y = details.focalPoint.dy;
                      var dx = x - _xOffset;
                      var dy = y - _yOffset;
                      FFI.cursorModel.updatePan(dx, dy);
                      _xOffset = x;
                      _yOffset = y;
                    } else {
                      _xOffset = details.focalPoint.dx;
                      _yOffset = details.focalPoint.dy;
                    }
                  } else if (!_drag && !_scroll) {
                    FFI.canvasModel.updateScale(scale / _scale);
                    _scale = scale;
                  }
                },
                onScaleEnd: (details) {
                  if (_drag) {
                    FFI.sendMouse('up', 'left');
                    setState(resetMouse);
                  } else if (_scroll) {
                    var dy = (_yOffset - _yOffset0) / 10;
                    if (dy.abs() > 0.1) {
                      if (dy > 0 && dy < 1) dy = 1;
                      if (dy < 0 && dy > -1) dy = -1;
                      FFI.scroll(dy);
                    }
                  }
                },
                child: Container(
                    color: Colors.black,
                    child: SafeArea(
                        child: Container(
                            color: MyTheme.canvasColor,
                            child: Stack(children: [
                              ImagePaint(),
                              CursorPaint(),
                              getHelpTools(),
                              SizedBox(
                                width: 0,
                                height: 0,
                                child: !_showKeyboard
                                    ? Container()
                                    : TextFormField(
                                        textInputAction:
                                            TextInputAction.newline,
                                        autocorrect: false,
                                        enableSuggestions: false,
                                        focusNode: _focusNode,
                                        maxLines: null,
                                        initialValue:
                                            _value, // trick way to make backspace work always
                                        keyboardType: TextInputType.multiline,
                                        onChanged: handleInput,
                                      ),
                              ),
                            ]))))),
          )),
    );
  }

  void close() {
    msgbox('', 'Close', 'Are you sure to close the connection?', context);
  }

  Widget getHelpTools() {
    final keyboard = _showKeyboard;
    if (!_mouseTools && !keyboard) {
      return SizedBox();
    }
    final size = MediaQuery.of(context).size;
    var wrap =
        (String text, void Function() onPressed, [bool active, IconData icon]) {
      return TextButton(
          style: TextButton.styleFrom(
            minimumSize: Size(0, 0),
            padding: EdgeInsets.symmetric(
                vertical: 10,
                horizontal: 9.75), //adds padding inside the button
            tapTargetSize: MaterialTapTargetSize
                .shrinkWrap, //limits the touch area to the button area
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(5.0),
            ),
            backgroundColor: active == true ? MyTheme.accent80 : null,
          ),
          child: icon != null
              ? Icon(icon, size: 17, color: Colors.white)
              : Text(translate(text),
                  style: TextStyle(color: Colors.white, fontSize: 11)),
          onPressed: onPressed);
    };
    final mouse = <Widget>[
      wrap('Drag', () {
        setState(() {
          _drag = !_drag;
          if (_drag) {
            _scroll = false;
            _right = false;
          }
        });
      }, _drag),
      wrap('Scroll', () {
        setState(() {
          _scroll = !_scroll;
          if (_scroll) {
            _drag = false;
            _right = false;
          }
        });
      }, _scroll),
      wrap('Right', () {
        setState(() {
          _right = !_right;
          if (_right) {
            _scroll = false;
            _drag = false;
          }
        });
      }, _right)
    ];
    final pi = FFI.ffiModel.pi;
    final isMac = pi.platform == "Mac OS";
    final modifiers = <Widget>[
      wrap('Ctrl ', () {
        setState(() => FFI.ctrl = !FFI.ctrl);
      }, FFI.ctrl),
      wrap(' Alt ', () {
        setState(() => FFI.alt = !FFI.alt);
      }, FFI.alt),
      wrap('Shift', () {
        setState(() => FFI.shift = !FFI.shift);
      }, FFI.shift),
      wrap(isMac ? ' Cmd ' : ' Win ', () {
        setState(() => FFI.command = !FFI.command);
      }, FFI.command),
    ];
    final keys = <Widget>[
      wrap(
          ' Fn ',
          () => setState(
                () {
                  _fn = !_fn;
                  if (_fn) {
                    _more = false;
                  }
                },
              ),
          _fn),
      wrap(
          ' ... ',
          () => setState(
                () {
                  _more = !_more;
                  if (_more) {
                    _fn = false;
                  }
                },
              ),
          _more),
    ];
    final fn = <Widget>[
      SizedBox(width: 9999),
    ];
    for (var i = 1; i <= 12; ++i) {
      final name = 'F' + i.toString();
      fn.add(wrap(name, () {
        FFI.inputKey('VK_' + name);
      }));
    }
    final more = <Widget>[
      SizedBox(width: 9999),
      wrap('Esc', () {
        FFI.inputKey('VK_ESCAPE');
      }),
      wrap('Tab', () {
        FFI.inputKey('VK_TAB');
      }),
      wrap('Home', () {
        FFI.inputKey('VK_HOME');
      }),
      wrap('End', () {
        FFI.inputKey('VK_END');
      }),
      wrap('Del', () {
        FFI.inputKey('VK_DELETE');
      }),
      wrap('PgUp', () {
        FFI.inputKey('VK_PRIOR');
      }),
      wrap('PgDn', () {
        FFI.inputKey('VK_NEXT');
      }),
      SizedBox(width: 9999),
      wrap('', () {
        FFI.inputKey('VK_LEFT');
      }, false, Icons.keyboard_arrow_left),
      wrap('', () {
        FFI.inputKey('VK_UP');
      }, false, Icons.keyboard_arrow_up),
      wrap('', () {
        FFI.inputKey('VK_DOWN');
      }, false, Icons.keyboard_arrow_down),
      wrap('', () {
        FFI.inputKey('VK_RIGHT');
      }, false, Icons.keyboard_arrow_right),
      wrap(isMac ? 'Cmd+C' : 'Ctrl+C', () {
        sendPrompt(isMac, 'VK_C');
      }),
      wrap(isMac ? 'Cmd+V' : 'Ctrl+V', () {
        sendPrompt(isMac, 'VK_V');
      }),
      wrap(isMac ? 'Cmd+S' : 'Ctrl+S', () {
        sendPrompt(isMac, 'VK_S');
      }),
    ];
    final space = size.width > 320 ? 4.0 : 2.0;
    return Container(
        color: Color(0xAA000000),
        padding: EdgeInsets.only(
            top: keyboard ? 24 : 4, left: 0, right: 0, bottom: 8),
        child: Wrap(
          spacing: space,
          runSpacing: space,
          children: <Widget>[SizedBox(width: 9999)] +
              (keyboard
                  ? modifiers + keys + (_fn ? fn : []) + (_more ? more : [])
                  : mouse + modifiers),
        ));
  }
}

class ImagePaint extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final m = Provider.of<ImageModel>(context);
    final c = Provider.of<CanvasModel>(context);
    final adjust = FFI.cursorModel.adjustForKeyboard();
    var s = c.scale;
    return CustomPaint(
      painter: new ImagePainter(
          image: m.image, x: c.x / s, y: (c.y - adjust) / s, scale: s),
    );
  }
}

class CursorPaint extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final m = Provider.of<CursorModel>(context);
    final c = Provider.of<CanvasModel>(context);
    final adjust = FFI.cursorModel.adjustForKeyboard();
    var s = c.scale;
    return CustomPaint(
      painter: new ImagePainter(
          image: m.image,
          x: m.x * s - m.hotx + c.x,
          y: m.y * s - m.hoty + c.y - adjust,
          scale: 1),
    );
  }
}

class ImagePainter extends CustomPainter {
  ImagePainter({
    this.image,
    this.x,
    this.y,
    this.scale,
  });

  ui.Image image;
  double x;
  double y;
  double scale;

  @override
  void paint(Canvas canvas, Size size) {
    if (image == null) return;
    canvas.scale(scale, scale);
    canvas.drawImage(image, new Offset(x, y), new Paint());
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return oldDelegate != this;
  }
}

void enterPasswordDialog(String id, BuildContext context) {
  final controller = TextEditingController();
  var remember = FFI.getByName('remember', id) == 'true';
  showAlertDialog(
      context,
      (setState) => Tuple3(
            Text(translate('Password Required')),
            Column(mainAxisSize: MainAxisSize.min, children: [
              PasswordWidget(controller: controller),
              CheckboxListTile(
                contentPadding: const EdgeInsets.all(0),
                dense: true,
                controlAffinity: ListTileControlAffinity.leading,
                title: Text(
                  translate('Remember password'),
                ),
                value: remember,
                onChanged: (v) {
                  setState(() => remember = v);
                },
              ),
            ]),
            [
              TextButton(
                style: flatButtonStyle,
                onPressed: () {
                  Navigator.pop(context);
                  Navigator.pop(context);
                },
                child: Text(translate('Cancel')),
              ),
              TextButton(
                style: flatButtonStyle,
                onPressed: () {
                  var text = controller.text.trim();
                  if (text == '') return;
                  FFI.login(text, remember);
                  showLoading(translate('Logging in...'), null);
                  Navigator.pop(context);
                },
                child: Text(translate('OK')),
              ),
            ],
          ));
}

void wrongPasswordDialog(String id, BuildContext context) {
  showAlertDialog(
      context,
      (_) => Tuple3(Text(translate('Wrong Password')),
              Text(translate('Do you want to enter again?')), [
            TextButton(
              style: flatButtonStyle,
              onPressed: () {
                Navigator.pop(context);
                Navigator.pop(context);
              },
              child: Text(translate('Cancel')),
            ),
            TextButton(
              style: flatButtonStyle,
              onPressed: () {
                enterPasswordDialog(id, context);
              },
              child: Text(translate('Retry')),
            ),
          ]));
}

void showOptions(BuildContext context) {
  String quality = FFI.getByName('image_quality');
  if (quality == '') quality = 'balanced';
  var displays = <Widget>[];
  final pi = FFI.ffiModel.pi;
  final image = FFI.ffiModel.getConnectionImage();
  if (image != null)
    displays.add(Padding(padding: const EdgeInsets.only(top: 8), child: image));
  if (pi.displays.length > 1) {
    final cur = pi.currentDisplay;
    final children = <Widget>[];
    for (var i = 0; i < pi.displays.length; ++i)
      children.add(InkWell(
          onTap: () {
            if (i == cur) return;
            FFI.setByName('switch_display', i.toString());
            Navigator.pop(context);
          },
          child: Ink(
              width: 40,
              height: 40,
              decoration: BoxDecoration(
                  border: Border.all(color: Colors.black87),
                  color: i == cur ? Colors.black87 : Colors.white),
              child: Center(
                  child: Text((i + 1).toString(),
                      style: TextStyle(
                          color: i == cur ? Colors.white : Colors.black87))))));
    displays.add(Padding(
        padding: const EdgeInsets.only(top: 8),
        child: Wrap(
          alignment: WrapAlignment.center,
          spacing: 8,
          children: children,
        )));
  }
  if (displays.isNotEmpty) {
    displays.add(Divider(color: MyTheme.border));
  }
  showAlertDialog(context, (setState) {
    final more = <Widget>[];
    if (FFI.ffiModel.permissions['audio'] != false) {
      more.add(CheckboxListTile(
          value: FFI.getByName('toggle_option', 'disable-audio') == 'true',
          onChanged: (v) {
            setState(() {
              FFI.setByName('toggle_option', 'disable-audio');
            });
          },
          title: Text(translate('Mute'))));
    }
    if (FFI.ffiModel.permissions['keyboard'] != false) {
      more.add(CheckboxListTile(
          value: FFI.getByName('toggle_option', 'lock-after-session-end') ==
              'true',
          onChanged: (v) {
            setState(() {
              FFI.setByName('toggle_option', 'lock-after-session-end');
            });
          },
          title: Text(translate('Lock after session end'))));
    }
    return Tuple3(
        null,
        Column(
            mainAxisSize: MainAxisSize.min,
            children: displays +
                <Widget>[
                  RadioListTile<String>(
                    controlAffinity: ListTileControlAffinity.trailing,
                    title: Text(translate('Good image quality')),
                    value: 'best',
                    groupValue: quality,
                    onChanged: (String value) {
                      setState(() {
                        quality = value;
                        FFI.setByName('image_quality', value);
                      });
                    },
                  ),
                  RadioListTile<String>(
                    controlAffinity: ListTileControlAffinity.trailing,
                    title: Text(translate('Balanced')),
                    value: 'balanced',
                    groupValue: quality,
                    onChanged: (String value) {
                      setState(() {
                        quality = value;
                        FFI.setByName('image_quality', value);
                      });
                    },
                  ),
                  RadioListTile<String>(
                    controlAffinity: ListTileControlAffinity.trailing,
                    title: Text(translate('Optimize reaction time')),
                    value: 'low',
                    groupValue: quality,
                    onChanged: (String value) {
                      setState(() {
                        quality = value;
                        FFI.setByName('image_quality', value);
                      });
                    },
                  ),
                  Divider(color: MyTheme.border),
                  CheckboxListTile(
                      value: FFI.getByName(
                              'toggle_option', 'show-remote-cursor') ==
                          'true',
                      onChanged: (v) {
                        setState(() {
                          FFI.setByName('toggle_option', 'show-remote-cursor');
                        });
                      },
                      title: Text(translate('Show remote cursor'))),
                ] +
                more),
        null);
  }, () async => true, true, 0);
}

void showActions(BuildContext context) {
  final size = MediaQuery.of(context).size;
  final x = 120.0;
  final y = size.height;
  final more = <PopupMenuItem<String>>[];
  if (FFI.ffiModel.pi.version.isNotEmpty) {
    more.add(PopupMenuItem<String>(
        child: Text(translate('Refresh')), value: 'refresh'));
  }
  if (FFI.ffiModel.permissions['keyboard'] != false &&
      FFI.ffiModel.permissions['clipboard'] != false) {
    more.add(
        PopupMenuItem<String>(child: Text(translate('Paste')), value: 'paste'));
  }
  more.add(PopupMenuItem<String>(
      child: Row(
          children: ([
        Text(translate('OS Password')),
        TextButton(
          style: flatButtonStyle,
          onPressed: () {
            Navigator.pop(context);
            showSetOSPassword(context, false);
          },
          child: Icon(Icons.edit),
        )
      ])),
      value: 'enter_os_password'));
  () async {
    var value = await showMenu(
      context: context,
      position: RelativeRect.fromLTRB(x, y, x, y),
      items: [
            PopupMenuItem<String>(
                child: Text(translate('Insert') + ' Ctrl + Alt + Del'),
                value: 'cad'),
            PopupMenuItem<String>(
                child: Text(translate('Insert Lock')), value: 'lock'),
          ] +
          more,
      elevation: 8,
    );
    if (value == 'cad') {
      FFI.setByName('ctrl_alt_del');
    } else if (value == 'lock') {
      FFI.setByName('lock_screen');
    } else if (value == 'refresh') {
      FFI.setByName('refresh');
    } else if (value == 'paste') {
      () async {
        ClipboardData data = await Clipboard.getData(Clipboard.kTextPlain);
        if (data.text != null) {
          FFI.setByName('input_string', '${data.text}');
        }
      }();
    } else if (value == 'enter_os_password') {
      var password = FFI.getByName('peer_option', "os-password");
      if (password != "") {
        FFI.setByName('input_os_password', password);
      } else {
        showSetOSPassword(context, true);
      }
    }
  }();
}

void showSetOSPassword(BuildContext context, bool login) {
  final controller = TextEditingController();
  var password = FFI.getByName('peer_option', "os-password");
  var autoLogin = FFI.getByName('peer_option', "auto-login") != "";
  controller.text = password;
  showAlertDialog(
      context,
      (setState) => Tuple3(
            Text(translate('OS Password')),
            Column(mainAxisSize: MainAxisSize.min, children: [
              PasswordWidget(controller: controller),
              CheckboxListTile(
                contentPadding: const EdgeInsets.all(0),
                dense: true,
                controlAffinity: ListTileControlAffinity.leading,
                title: Text(
                  translate('Auto Login'),
                ),
                value: autoLogin,
                onChanged: (v) {
                  setState(() => autoLogin = v);
                },
              ),
            ]),
            [
              TextButton(
                style: flatButtonStyle,
                onPressed: () {
                  Navigator.pop(context);
                },
                child: Text(translate('Cancel')),
              ),
              TextButton(
                style: flatButtonStyle,
                onPressed: () {
                  var text = controller.text.trim();
                  FFI.setByName('peer_option',
                      '{"name": "os-password", "value": "${text}"}');
                  FFI.setByName('peer_option',
                      '{"name": "auto-login", "value": "${autoLogin ? 'Y' : ''}"}');
                  if (text != "" && login) {
                    FFI.setByName('input_os_password', text);
                  }
                  Navigator.pop(context);
                },
                child: Text(translate('OK')),
              ),
            ],
          ));
}

void sendPrompt(bool isMac, String key) {
  final old = isMac ? FFI.command : FFI.ctrl;
  if (isMac) {
    FFI.command = true;
  } else {
    FFI.ctrl = true;
  }
  FFI.inputKey(key);
  if (isMac) {
    FFI.command = old;
  } else {
    FFI.ctrl = old;
  }
}