import 'dart:async';
import 'dart:convert';
import 'dart:html';
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui';
import 'dart:js' as js;
import 'package:flutter/material.dart' hide Image;
import 'package:flutter/services.dart';
import 'package:webview_demo/utils/helper.dart';
import 'package:webviewx/webviewx.dart';

class WebViewXPage extends StatefulWidget {
  const WebViewXPage({
    Key? key,
  }) : super(key: key);

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

class _WebViewXPageState extends State<WebViewXPage> {
  late WebViewXController webviewController;
  final executeJsErrorMessage = '脚本执行失败，请检查代码是否正确嵌入';

  /// 预加载的脚本文件
  List<String> scriptsFromAssets = [];
  Size get screenSize => MediaQuery.of(context).size;

  int width = 0;
  int height = 0;
  int depth = 0;
  List<String> images = [];

  String _loadRandomImage(int width, int height) {
    CanvasElement canvas = CanvasElement(width: width, height: height);
    var ctx = canvas.context2D
      ..fillStyle =
          "rgb(${Random().nextInt(255)},${Random().nextInt(255)},${Random().nextInt(255)})"
      ..fillRect(0, 0, width, height);
    var dataUrl = canvas.toDataUrl("image/jpeg", 0.95);
    return dataUrl;
  }

  @override
  void dispose() {
    webviewController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    _setPlatForm();
    return Scaffold(
      appBar: AppBar(
        title: const Text('WebViewX Demo Page'),
      ),
      body: Center(
        child: Container(
          padding: const EdgeInsets.all(10.0),
          child: Row(
            children: [
              Expanded(
                flex: 2,
                child: Column(
                  children: <Widget>[
                    buildSpace(
                        direction: Axis.vertical, amount: 10.0, flex: false),
                    Container(
                      padding: const EdgeInsets.only(bottom: 10.0),
                      child: Text(
                        '通过右侧的按钮操作 webview 注意！不可以在 webview 上面覆盖 Flutter 内容，会导致无法响应触摸',
                        style: Theme.of(context).textTheme.bodyText2,
                      ),
                    ),
                    buildSpace(
                        direction: Axis.vertical, amount: 10.0, flex: false),
                    Container(
                      decoration: BoxDecoration(
                        border: Border.all(width: 0.2),
                      ),
                      child: _buildWebViewX(),
                    ),
                  ],
                ),
              ),
              Expanded(
                child: Scrollbar(
                  isAlwaysShown: true,
                  child: SizedBox(
                    width: 200,
                    child: ListView(
                      children: _buildButtons(),
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildWebViewX() {
    return WebViewX(
      key: const ValueKey('webviewx'),
      initialContent: "<h2> Hello webviewX ! <h2>",
      initialSourceType: SourceType.html,
      height: screenSize.height / 1.5,
      width: min(screenSize.width * 0.8, 1024),
      onWebViewCreated: (controller) => webviewController = controller,
      onPageStarted: (src) => debugPrint('A new page has started loading'),
      onPageFinished: (src) => debugPrint('The page has finished loading'),
      jsContent: scriptsFromAssets.map((e) => EmbeddedJsContent(js: e)).toSet(),
      dartCallBacks: {
        DartCallback(
          name: 'TestDartCallback',
          callBack: (msg) => showSnackBar(msg.toString(), context),
        ),
        DartCallback(
          name: 'Dart_GetClipPlaneData',
          callBack: (msg) {
            var api = _getApiHost();
            return api!.callMethod('getClipPlaneData', [msg]);
          },
        ),
        DartCallback(
          name: "Dart_GetStringData",
          callBack: (msg) {
            print("webview 触发 Dart_GetStringData :$msg");
            // showSnackBar(msg.toString(), context);
            return jsonEncode("{success:true}");
          },
        )
      },
      webSpecificParams: const WebSpecificParams(
        printDebugInfo: true,
      ),
      mobileSpecificParams: const MobileSpecificParams(
        androidEnableHybridComposition: true,
      ),
      navigationDelegate: (navigation) {
        debugPrint(navigation.content.sourceType.toString());
        return NavigationDecision.navigate;
      },
    );
  }

  List<Widget> _buildButtons() {
    return [
      buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Expanded(child: createButton(onTap: _goBack, text: '返回')),
          buildSpace(amount: 12, flex: false),
          Expanded(child: createButton(onTap: _goForward, text: '前进')),
          buildSpace(amount: 12, flex: false),
          Expanded(child: createButton(onTap: _reload, text: '重载')),
        ],
      ),
      buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
      createButton(
        text: '跳转至百度 (UrlBypass)',
        onTap: _setUrlBypass,
      ),
      buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
      createButton(
        text: '事件忽略开关 (点击、滚动等...)',
        onTap: _toggleIgnore,
      ),
      buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
      createButton(
        text: '在浏览器环境全局执行 2+2 计算，结果返回到 flutter',
        onTap: _evalRawJsInGlobalContext,
      ),
      buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
      const Text(
        "👇👇 Threejs 页面相关（建议顺序执行） 👇👇",
        style: TextStyle(fontSize: 20),
      ),
      buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
      createButton(
        text: '1. 载入页面 (从 assets 文件夹)',
        onTap: _setHtmlFromAssets,
      ),
      buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
      createButton(
        text: '2. 执行 initPage() （iframe 中）',
        onTap: _initPage,
      ),
      buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
      createButton(
        text: '3. 执行 setSurface(100,100,100,path) （iframe 中）',
        onTap: _setSurface,
      ),
      buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
      createButton(
        text: '4. 执行 mdlFileLoaded("Carotid","True") （iframe 中）',
        onTap: _mdlFileLoaded,
      ),
      buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
      createButton(
        text: '切换模型 changeSurface （iframe 中）',
        onTap: _changeSurfaceFromShell,
      ),
    ];
  }

  Future<void> _initPage() async {
    try {
      await webviewController.callJsMethod('initPage', []);
    } catch (e) {
      showAlertDialog(
        executeJsErrorMessage,
        context,
      );
    }
  }

  Future<void> _setSurface() async {
    try {
      await webviewController
          .callJsMethod('setSurface', [759, 596, 397, "../3DWeb/FaceImage"]);
    } catch (e) {
      showAlertDialog(
        executeJsErrorMessage,
        context,
      );
    }
  }

  Future<void> _changeSurface() async {
    final faceImages = [
      _loadRandomImage(500, 500),
      _loadRandomImage(500, 500),
      _loadRandomImage(500, 500),
      _loadRandomImage(500, 500),
      _loadRandomImage(500, 500),
      _loadRandomImage(500, 500),
    ];
    // print([759, 596, 397, ...faceImages]);
    try {
      await webviewController
          .callJsMethod('changeSurface', [759, 596, 397, ...faceImages]);
    } catch (e) {
      showAlertDialog(
        executeJsErrorMessage,
        context,
      );
    }
  }

  Future<void> _mdlFileLoaded() async {
    try {
      await webviewController
          .callJsMethod('mdlFileLoaded', ["Carotid", "True"]);
    } catch (e) {
      showAlertDialog(
        executeJsErrorMessage,
        context,
      );
    }
  }

  void _setUrlBypass() {
    webviewController.loadContent(
      'https://www.baidu.com/',
      SourceType.urlBypass,
    );
  }

  Future<void> _setHtmlFromAssets() async {
    return await webviewController.loadContent(
      'threejsview/MainPage.html',
      SourceType.html,
      fromAssets: true,
    );
  }

  Future<void> _goForward() async {
    if (await webviewController.canGoForward()) {
      await webviewController.goForward();
      showSnackBar('Did go forward', context);
    } else {
      showSnackBar('Cannot go forward', context);
    }
  }

  Future<void> _goBack() async {
    if (await webviewController.canGoBack()) {
      await webviewController.goBack();
      showSnackBar('Did go back', context);
    } else {
      showSnackBar('Cannot go back', context);
    }
  }

  void _reload() {
    webviewController.reload();
  }

  /// 事件忽略开关
  void _toggleIgnore() {
    final ignoring = webviewController.ignoresAllGestures;
    webviewController.setIgnoreAllGestures(!ignoring);
    showSnackBar('Ignore events = ${!ignoring}', context);
  }

  /// 在浏览器环境全局执行
  Future<void> _evalRawJsInGlobalContext() async {
    try {
      final result = await webviewController.evalRawJavascript(
        '2+2',
        inGlobalContext: true,
      );
      showSnackBar('The result is $result', context);
    } catch (e) {
      showAlertDialog(
        executeJsErrorMessage,
        context,
      );
    }
  }

  Widget buildSpace({
    Axis direction = Axis.horizontal,
    double amount = 0.2,
    bool flex = true,
  }) {
    return flex
        ? Flexible(
            child: FractionallySizedBox(
              widthFactor: direction == Axis.horizontal ? amount : null,
              heightFactor: direction == Axis.vertical ? amount : null,
            ),
          )
        : SizedBox(
            width: direction == Axis.horizontal ? amount : null,
            height: direction == Axis.vertical ? amount : null,
          );
  }

  void _setPlatForm() async {
    js.context['invokeJs'] = (a, b) {
      var datas = b.split("\$");

      width = int.parse(datas[0]);

      height = int.parse(datas[1]);

      depth = int.parse(datas[2]);

      var imageStr = datas[3];
      images = imageStr.split("surface");
      print(images);
    };
  }

  js.JsObject? _getApiHost() {
    if (js.context.hasProperty('FisShellApi')) {
      final api = js.context['FisShellApi'] as js.JsObject;
      return api;
    }
  }

  Future<void> _changeSurfaceFromShell() async {
    try {
      await webviewController
          .callJsMethod('changeSurface', [width, height, depth, ...images]);
    } catch (e) {
      showAlertDialog(
        executeJsErrorMessage,
        context,
      );
    }
  }
}
