content: import 'dart:convert';
  
  import 'package:file_picker/file_picker.dart';
  import 'package:flutter/material.dart';
  import 'package:flutter_hbb/common.dart';
  import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
  import 'package:flutter_hbb/models/platform_model.dart';
  import 'package:flutter_hbb/models/state_model.dart';
  import 'package:get/get.dart';
  import 'package:path/path.dart';
  import 'package:url_launcher/url_launcher_string.dart';
  import 'package:window_manager/window_manager.dart';
  
  class InstallPage extends StatefulWidget {
    const InstallPage({Key? key}) : super(key: key);
  
    @override
    State<InstallPage> createState() => _InstallPageState();
  }
  
  class _InstallPageState extends State<InstallPage> {
    final tabController = DesktopTabController(tabType: DesktopTabType.main);
  
    _InstallPageState() {
      Get.put<DesktopTabController>(tabController);
      const label = "install";
      tabController.add(TabInfo(
          key: label,
          label: label,
          closable: false,
          page: _InstallPageBody(
            key: const ValueKey(label),
          )));
    }
  
    @override
    void dispose() {
      super.dispose();
      Get.delete<DesktopTabController>();
    }
  
    @override
    Widget build(BuildContext context) {
      return DragToResizeArea(
        resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
        enableResizeEdges: windowManagerEnableResizeEdges,
        child: Container(
          child: Scaffold(
              backgroundColor: Theme.of(context).colorScheme.background,
              body: DesktopTab(controller: tabController)),
        ),
      );
    }
  }
  
  class _InstallPageBody extends StatefulWidget {
    const _InstallPageBody({Key? key}) : super(key: key);
  
    @override
    State<_InstallPageBody> createState() => _InstallPageBodyState();
  }
  
  class _InstallPageBodyState extends State<_InstallPageBody>
      with WindowListener {
    late final TextEditingController controller;
    final RxBool startmenu = true.obs;
    final RxBool desktopicon = true.obs;
    final RxBool printer = true.obs;
    final RxBool showProgress = false.obs;
    final RxBool btnEnabled = true.obs;
  
    // todo move to theme.
    final buttonStyle = OutlinedButton.styleFrom(
      textStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.normal),
      padding: EdgeInsets.symmetric(vertical: 15, horizontal: 12),
    );
  
    _InstallPageBodyState() {
      controller = TextEditingController(text: bind.installInstallPath());
      final installOptions = jsonDecode(bind.installInstallOptions());
      startmenu.value = installOptions['STARTMENUSHORTCUTS'] != '0';
      desktopicon.value = installOptions['DESKTOPSHORTCUTS'] != '0';
      printer.value = installOptions['PRINTER'] != '0';
    }
  
    @override
    void initState() {
      windowManager.addListener(this);
      super.initState();
    }
  
    @override
    void dispose() {
      windowManager.removeListener(this);
      super.dispose();
    }
  
    @override
    void onWindowClose() {
      gFFI.close();
      super.onWindowClose();
      windowManager.setPreventClose(false);
      windowManager.close();
    }
  
    InkWell Option(RxBool option, {String label = ''}) {
      return InkWell(
        // todo mouseCursor: "SystemMouseCursors.forbidden" or no cursor on btnEnabled == false
        borderRadius: BorderRadius.circular(6),
        onTap: () => btnEnabled.value ? option.value = !option.value : null,
        child: Row(
          children: [
            Obx(
              () => Checkbox(
                visualDensity: VisualDensity(horizontal: -4, vertical: -4),
                value: option.value,
                onChanged: (v) =>
                    btnEnabled.value ? option.value = !option.value : null,
              ).marginOnly(right: 8),
            ),
            Expanded(
              child: Text(translate(label)),
            ),
          ],
        ),
      );
    }
  
    @override
    Widget build(BuildContext context) {
      final double em = 13;
      final isDarkTheme = MyTheme.currentThemeMode() == ThemeMode.dark;
      return Scaffold(
          backgroundColor: null,
          body: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(translate('Installation'),
                    style: Theme.of(context).textTheme.headlineMedium),
                Row(
                  children: [
                    Text('${translate('Installation Path')}:')
                        .marginOnly(right: 10),
                    Expanded(
                      child: TextField(
                        controller: controller,
                        readOnly: true,
                        decoration: InputDecoration(
                          contentPadding: EdgeInsets.all(0.75 * em),
                        ),
                      ).workaroundFreezeLinuxMint().marginOnly(right: 10),
                    ),
                    Obx(
                      () => OutlinedButton.icon(
                        icon: Icon(Icons.folder_outlined, size: 16),
                        onPressed: btnEnabled.value ? selectInstallPath : null,
                        style: buttonStyle,
                        label: Text(translate('Change Path')),
                      ),
                    )
                  ],
                ).marginSymmetric(vertical: 2 * em),
                Option(startmenu, label: 'Create start menu shortcuts')
                    .marginOnly(bottom: 7),
                Option(desktopicon, label: 'Create desktop icon')
                    .marginOnly(bottom: 7),
                Option(printer, label: 'Install {$appName} Printer'),
                Container(
                    padding: EdgeInsets.all(12),
                    decoration: BoxDecoration(
                      color: isDarkTheme
                          ? Color.fromARGB(135, 87, 87, 90)
                          : Colors.grey[100],
                      borderRadius: BorderRadius.circular(8),
                      border: Border.all(color: Colors.grey),
                    ),
                    child: Row(
                      children: [
                        Icon(Icons.info_outline_rounded, size: 32)
                            .marginOnly(right: 16),
                        Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(translate('agreement_tip'))
                                .marginOnly(bottom: em),
                            InkWell(
                              hoverColor: Colors.transparent,
                              onTap: () => launchUrlString(
                                  'https://rustdesk.com/privacy.html'),
                              child: Tooltip(
                                message: 'https://rustdesk.com/privacy.html',
                                child: Row(children: [
                                  Icon(Icons.launch_outlined, size: 16)
                                      .marginOnly(right: 5),
                                  Text(
                                    translate('End-user license agreement'),
                                    style: const TextStyle(
                                        decoration: TextDecoration.underline),
                                  )
                                ]),
                              ),
                            ),
                          ],
                        )
                      ],
                    )).marginSymmetric(vertical: 2 * em),
                Row(
                  children: [
                    Expanded(
                      // NOT use Offstage to wrap LinearProgressIndicator
                      child: Obx(() => showProgress.value
                          ? LinearProgressIndicator().marginOnly(right: 10)
                          : Offstage()),
                    ),
                    Obx(
                      () => OutlinedButton.icon(
                        icon: Icon(Icons.close_rounded, size: 16),
                        label: Text(translate('Cancel')),
                        onPressed:
                            btnEnabled.value ? () => windowManager.close() : null,
                        style: buttonStyle,
                      ).marginOnly(right: 10),
                    ),
                    Obx(
                      () => ElevatedButton.icon(
                        icon: Icon(Icons.done_rounded, size: 16),
                        label: Text(translate('Accept and Install')),
                        onPressed: btnEnabled.value ? install : null,
                        style: buttonStyle,
                      ),
                    ),
                    Offstage(
                      offstage: bind.installShowRunWithoutInstall(),
                      child: Obx(
                        () => OutlinedButton.icon(
                          icon: Icon(Icons.screen_share_outlined, size: 16),
                          label: Text(translate('Run without install')),
                          onPressed: btnEnabled.value
                              ? () => bind.installRunWithoutInstall()
                              : null,
                          style: buttonStyle,
                        ).marginOnly(left: 10),
                      ),
                    ),
                  ],
                )
              ],
            ).paddingSymmetric(horizontal: 4 * em, vertical: 3 * em),
          ));
    }
  
    void install() {
      do_install() {
        btnEnabled.value = false;
        showProgress.value = true;
        String args = '';
        if (startmenu.value) args += ' startmenu';
        if (desktopicon.value) args += ' desktopicon';
        if (printer.value) args += ' printer';
        bind.installInstallMe(options: args, path: controller.text);
      }
  
      do_install();
    }
  
    void selectInstallPath() async {
      String? install_path = await FilePicker.platform
          .getDirectoryPath(initialDirectory: controller.text);
      if (install_path != null) {
        controller.text = join(install_path, await bind.mainGetAppName());
      }
    }
  }
  