/*
 * @Author: wangyang 1147192855@qq.com
 * @Date: 2022-07-23 11:55:16
 * @LastEditors: wangyang 1147192855@qq.com
 * @LastEditTime: 2022-09-28 17:01:15
 * @FilePath: \marking_app\lib\components\JobPictureOverview.dart
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import 'dart:ui' as ui;
import 'dart:io';
import 'dart:async';
import 'package:achievement_view/achievement_view.dart';
import 'package:crypto/crypto.dart' as crypto;
import 'package:fluttertoast/fluttertoast.dart';
import 'package:image/image.dart' as img;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:marking_app/common/mixin/common.dart';
import 'package:marking_app/common/model/common/base_structure_result.dart';
import 'package:marking_app/common/model/common/upload_img_secret_key.dart';
import 'package:marking_app/common/model/event_bus/bottom_annotation_switch_cleanall.dart';
import 'package:marking_app/common/model/job/gesture_recording.dart';
import 'package:marking_app/common/model/job/job_note_taking_trajectory.dart';
import 'package:marking_app/common/model/job/marking_text_question_job.dart';
import 'package:marking_app/common/model/job/test_questions_image_info.dart';
import 'package:marking_app/common/model/job/upload_file_interface_config.dart';
import 'package:marking_app/common/model/job/upload_file_interface_config_params.dart';
import 'package:marking_app/common/model/marking/annotation_graffiti_switch.dart';
import 'package:marking_app/pages/common/event_bus_mixin.dart';
import 'package:marking_app/pages/homework_correction/components/theExamPaperDrawing.dart';
import 'package:marking_app/pages/homework_correction/components/trajectoryView.dart';
import 'package:marking_app/pages/homework_correction/eventBus/do_papers_job_custom_paint_size_bus.dart';
import 'package:marking_app/pages/homework_correction/eventBus/job_notes_view_bus.dart';
import 'package:marking_app/pages/homework_correction/hooks/use_cached_img_refresh.dart';
import 'package:marking_app/pages/homework_correction/providers/drawing_provider.dart';
import 'package:marking_app/pages/homework_correction/providers/drawing_trajectory_provider.dart';
import 'package:marking_app/provider/annotation_graffiti_switch_provider.dart';
import 'package:marking_app/provider/upload_file_provider.dart';
import 'package:marking_app/utils/index.dart';
import 'package:marking_app/utils/my_text.dart';
import 'package:marking_app/utils/request/rest_client.dart';
import 'package:path_provider/path_provider.dart';

import 'package:uuid/uuid.dart';

import '../eventBus/job_do_papers_switch_operation_sub_bus.dart';

part 'jobPictureOverview.g.dart';

class JobPictureOverview extends StatefulHookConsumerWidget {
  final double containerWidth;
  final double containerHeight;
  final MarkingTextQuestionJob jobData;

  const JobPictureOverview({
    required this.jobData,
    required this.containerWidth,
    required this.containerHeight,
    Key? key,
  }) : super(key: key);

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

class JobPictureOverviewState extends ConsumerState<JobPictureOverview>
    with CommonMixin, EventBusMixin<JobNotesViewBus> {
  final GlobalKey theglobalKey = GlobalKey();
  final GlobalKey<_ExamPaperDrawingState> examPaperDrawingKey = GlobalKey<_ExamPaperDrawingState>();
  final GlobalKey<TrajectoryViewState> trajectoryViewStateKey = GlobalKey<TrajectoryViewState>();

  final int currentIndex = 0;
  late AnnotationGraffitiSwitch graffitiSwitch;
  late RemoveListener _annotationsListener; // 批注关闭监听
  late bool showAnnotations;
  JobNoteTakingTrajectory? _jobNoteTakingTrajectory;
  File? temFile; // 批注临时数据

  bool canScroll = true;

  bool viewHomeworkNotes = false;
  JobNotesViewBus? _oldNotesModel;

  @override
  void initState() {
    showAnnotations = widget.jobData.commentImageUrl != null;
    // questionImgUrl = widget.jobData.commentImageUrl ?? widget.jobData.imageUrl;
    eventOn(callback: (eventModel) async {
      switch (eventModel.runtimeType) {
        case JobNotesViewBus: // 笔记回显
          var notesModel = eventModel as JobNotesViewBus;
          try {
            ToastUtils.showLoading();
            ref.read(jobDrawingTrajectoryProvider.notifier).setVal([]);
            if (notesModel.questionNo == _oldNotesModel?.questionNo) {
              viewHomeworkNotes = false;
              _oldNotesModel = null;
              _jobNoteTakingTrajectory = null;
              toUpState(setState, () {}, mounted);
              return;
            }

            // 切换试题回显数据
            bool switchNotesFlag = false;
            if (viewHomeworkNotes && _oldNotesModel != null && _oldNotesModel?.questionNo != notesModel.questionNo) {
              switchNotesFlag = true;
            }

            JobNoteTakingTrajectory? trajectory =
                await toGetNoteTakingTrajectory(notesModel.paperId, notesModel.questionNo);
            if (trajectory == null) {
              // 重置数据
              viewHomeworkNotes = false;
              _oldNotesModel = null;
              _jobNoteTakingTrajectory = null;
            } else {
              viewHomeworkNotes = true;
              _oldNotesModel = notesModel;
              _jobNoteTakingTrajectory = trajectory;
              if (switchNotesFlag) {
                viewHomeworkNotes = false;
                _oldNotesModel = null;
                _jobNoteTakingTrajectory = null;
                toUpState(setState, () {}, mounted);
                await Future.delayed(Duration(seconds: 1));
                viewHomeworkNotes = true;
                _oldNotesModel = notesModel;
                _jobNoteTakingTrajectory = trajectory;
              }
            }
            toUpState(setState, () {}, mounted);
          } catch (e) {
            print(e);
          } finally {
            ToastUtils.dismiss();
          }

          break;
        case BottomAnnotationSwitchCleanall:
          // 底部清空按钮监听 提示恢复原图
          BottomAnnotationSwitchCleanall theAnnotation = eventModel as BottomAnnotationSwitchCleanall; // 底部动作
          if ((theAnnotation.cleanAll || theAnnotation.previousStep) && showAnnotations) {
            List<dynamic>? pointsPureData = examPaperDrawingKey.currentState?.pointsPureData;
            if (pointsPureData == null || pointsPureData.isEmpty) {
              showDialog<bool?>(
                  // 表示点击灰色背景的时候是否消失弹出框
                  barrierDismissible: false,
                  context: context,
                  builder: (context1) {
                    return AlertDialog(content: quickText("请确定要重置已经批注的试题图片吗？"), actions: <Widget>[
                      TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
                      TextButton(
                          child: quickText("确定", color: Theme.of(context).primaryColor),
                          onPressed: () => Navigator.pop(context1, true))
                    ]);
                  }).then((value) {
                if (value == null || !value) return;
                showAnnotations = false;
                toUpState(setState, () {}, mounted);
              });
            }
          }
          break;
        default:
      }
    });
    // 更新批注记录
    Future.delayed(Duration.zero, () {
      int theTaskdetailId = ref.read(jobHomeWorkIdProvider); // 唯一的任务ID
      List<GestureRecording> taskDatas = ref.read(jobHomeWorkProvider);
      ref.read(jobHomeWorkIdProvider.notifier).setVal(widget.jobData.taskId);
      if (theTaskdetailId > 0 && theTaskdetailId != widget.jobData.taskId && taskDatas.isNotEmpty) {
        ref.read(jobHomeWorkProvider.notifier).setVal([]);
      }
    });
    _annotationsListener = ref.read(annotationGraffitiSwitchProvider.notifier).addListener((state) async {
      graffitiSwitch = state;
      canScroll = !state.magnifier;
      if (mounted) {
        // 清空笔记回显
        Future.delayed(const Duration(seconds: 0)).then((_) {
          ref.read(jobDrawingTrajectoryProvider.notifier).setVal([]);
        });
      }
      toUpState(setState, () {}, mounted);
    });
    super.initState();
  }

  // 获取学生笔记轨迹
  Future<JobNoteTakingTrajectory?> toGetNoteTakingTrajectory(int paperId, String questionNo) async {
    try {
      RestClient _client = await getClient();
      BaseStructureResult<JobNoteTakingTrajectory> baseRes = await _client.getNoteTakingTrajectory(paperId, questionNo);
      if (baseRes.success && baseRes.data != null) {
        return baseRes.data!;
      }
      ToastUtils.showError('获取学生笔记失败，请重试');
    } catch (e) {
      print('${e}');
    }
    return null;
  }

  @override
  void dispose() {
    super.dispose();
    eventCancel();
    _annotationsListener();
    try {
      if (temFile != null) {
        bool flieExist = temFile!.existsSync();
        if (flieExist) temFile!.delete();
      }
    } catch (e) {
      print(e);
    }
  }

  Future<FileResult?> saveImage() async {
    try {
      ToastUtils.showLoading();
      TestQuestionsImageInfo? theImageInfo = examPaperDrawingKey.currentState?.imagInfoModel; // 图片和容器比例数据
      double pixelRatio = 1;
      if (temFile == null) {
        if (examPaperDrawingKey.currentState?.pointsPureData.isEmpty ?? true) return null;
        // 没有图片就上传图片
        RenderRepaintBoundary? boundary = theglobalKey.currentContext!.findRenderObject() as RenderRepaintBoundary?;
        if (boundary == null) return null;
        if (theImageInfo != null) {
          // 计算像素比例
          double imageWidth = theImageInfo.width; // 图片宽
          double boxViewWidth = theImageInfo.boxWidth; // 图片容器宽
          pixelRatio = imageWidth / boxViewWidth; // 得到比例
        }
        ui.Image image = await boundary.toImage(pixelRatio: pixelRatio);
        ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
        if (byteData == null) {
          /// 等于null 已经是异常了
          return null;
        }
        Directory appDocDir = await getApplicationDocumentsDirectory();
        List<int> bytes = byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
        String filePath = '${appDocDir.path}/${Uuid().v1()}.png'; // 文件路径及名称
        File file = File(filePath); // 创建文件对象
        await file.writeAsBytes(bytes); // 将ByteData写入文件
        temFile = file;
      }
      if (theImageInfo != null) {
        // 剪切图片
        List<GestureRecording>? points = examPaperDrawingKey.currentState?.points;
        if (points?.isNotEmpty ?? false) {
          // print('开始打印......................');
          List<double> dyArray =
              points!.where((e) => !e.eraser && e.data != null).map((e) => e.data!.dy).toList(); // 获得有效的批注数据
          if (dyArray.isNotEmpty && theImageInfo.scaleHeight != null) {
            /**
             * 1：图片小于视图 按照最低点和最高点剪切图片
             * 2：图片大于视图 不剪切图片
             */
            double remainingHeight = theImageInfo.boxHeight - theImageInfo.scaleHeight!; // 剩余高度
            /**  不需要再切割，图片和容器已经按照容器中部展示
            if (remainingHeight > 1) {
              // print('全部的数.............');
              // print(dyArray.toString());
              double lowestNumber = dyArray.reduce((currMin, next) => currMin < next ? currMin : next);
              double highestNumber = dyArray.reduce((currMax, next) => currMax > next ? currMax : next);

              // print('最低的数: $lowestNumber');
              // print('最高的数: $highestNumber');

              final img.Image? originalImage = img.decodeImage(temFile!.readAsBytesSync());
              if (originalImage != null) {
                temFile?.delete(); // 删除以后缓存图片
                // 计算裁剪区域的像素坐标
                // 裁剪图像

                double imageStartPositionY = remainingHeight / 2; // 图片起始Y轴位置
                double imageEndPositionY = imageStartPositionY + theImageInfo.scaleHeight!; // 图片结束Y轴位置
                double startY = imageStartPositionY;
                if (lowestNumber < imageStartPositionY) {
                  // 画图最小坐标小于图片 （向上操作了图片）
                  startY = lowestNumber;
                }

                double endY = highestNumber - startY; // 超出
                // endY = endY + 10 > theImageInfo.boxHeight ? endY : endY + 10;

                if (highestNumber < imageEndPositionY) {
                  endY = imageEndPositionY - startY;
                  // endY = endY + 10 > theImageInfo.scaleHeight! ? endY : endY + 10;
                }

                final img.Image clippedImage = img.copyCrop(
                  originalImage,
                  x: 0,
                  y: (startY * pixelRatio).toInt(),
                  height: (endY * pixelRatio).toInt(),
                  width: originalImage.width,
                );

                Directory appDocDir = await getApplicationDocumentsDirectory();
                String filePath = '${appDocDir.path}/${Uuid().v1()}.png'; // 文件路径及名称
                File file = File(filePath); // 创建文件对象
                temFile = await file.writeAsBytes(img.encodePng(clippedImage));
              }
            } */
          }
        }
      }
      crypto.Digest fileMd5 = crypto.md5.convert(await temFile!.readAsBytes());
      String? fileName = temFile?.path.split('/').last;
      // print('获得文件名'); // 打印文件名
      // print(fileName); // 打印文件名

      RestClient _client = await getClient();
      BaseStructureResult<UploadFileInterfaceConfig> resUploadConfig =
          await _client.getUploadFile(UploadFileInterfaceConfigParams(
        fileName: fileName ?? '1.png',
        fileMd5: fileMd5.toString(),
        contentLength: temFile!.lengthSync(),
      ));
      if (!resUploadConfig.success || resUploadConfig.data == null) {
        ToastUtils.getFluttertoast(msg: '获取图片上传配置失败', context: context);
        return null;
      }
      if (resUploadConfig.data!.uploadUri == null) {
        return FileResult(myObject: '', success: true, url: resUploadConfig.data!.downloadUri);
      }
      FileResult? resFile =
          await ref.read(uploadFileProvider.notifier).getUploadFileConfig(resUploadConfig.data!, temFile!);
      // FileResult? resFile = await ref
      //     .read(uploadFileProvider.notifier)
      //     .uploadFile(temFile!.path, widget.imageItems[currentIndex], ref.read(userProvider).id.toString());
      if (resFile != null && resFile.success) {
        resFile.otherParam = currentIndex;
        return resFile;
      }
    } catch (e) {
      toPrint(val: e.toString());
    } finally {
      // ToastUtils.dismiss();
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    if (viewHomeworkNotes && _jobNoteTakingTrajectory?.paperPicture != null && _jobNoteTakingTrajectory != null)
      return InkWell(
        onTap: () {
          if (_oldNotesModel != null) {
            return eventFire(model: _oldNotesModel!); // todo 是否关闭试题
          }
          AchievementView(
            elevation: 5,
            duration: Duration(seconds: 1),
            title: "笔记回显提示",
            subTitle: "当前正处于笔记回显",
            color: Theme.of(context).primaryColor,
          ).show(context);
        },
        child: TrajectoryView(
          key: trajectoryViewStateKey,
          boxWidth: widget.containerWidth,
          boxHeight: widget.containerHeight,
          questionNumber: _oldNotesModel?.questionNo ?? '',
          trajectory: _jobNoteTakingTrajectory!,
        ),
      );
    print('最新图片：${widget.jobData.imageUrl}');
    return Container(
      margin: EdgeInsets.only(top: 1.h),
      width: widget.containerWidth,
      height: widget.containerHeight,
      child: Scrollbar(
        // thumbVisibility: true,
        child: SingleChildScrollView(
          physics: canScroll ? NeverScrollableScrollPhysics() : AlwaysScrollableScrollPhysics(),
          padding: EdgeInsets.zero,
          scrollDirection: Axis.vertical, // 设置垂直滚动
          child: $LocalAndNetworkSwitch(
            boxWidth: widget.containerWidth,
            boxHeight: widget.containerHeight,
            theglobalKey: theglobalKey,
            graffitiSwitch: graffitiSwitch,
            drawFlag: false, // TODO 是否正在绘制批阅
            examGlobalKey: examPaperDrawingKey,
            showAnnotations: showAnnotations,
            questionImgUrl: widget.jobData.imageUrl,
            commentImageUrl: widget.jobData.commentImageUrl,
          ),
        ),
      ),
    );
  }
}

// 试卷绘制
class ExamPaperDrawing extends StatefulHookConsumerWidget {
  double boxWidth;
  double boxHeight;
  bool showAnnotations;
  String questionImgUrl;
  String? commentImageUrl;
  BoxDecoration? decoration;
  AnnotationGraffitiSwitch graffitiSwitch;
  List<GestureRecording>? points;
  List<dynamic>? pointsPureData;
  ValueNotifier<Map<String, ui.Image>> imageLoaded;

  GlobalKey globalKey;
  // Function(String) imageCall;
  ExamPaperDrawing({
    required this.boxWidth,
    required this.boxHeight,
    required this.points,
    required this.showAnnotations,
    required this.questionImgUrl,
    required this.commentImageUrl,
    required this.pointsPureData,
    required this.graffitiSwitch,
    required this.globalKey,
    required this.imageLoaded,
    this.decoration,
    Key? key,
  }) : super(key: key);

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

class _ExamPaperDrawingState extends ConsumerState<ExamPaperDrawing> with EventBusMixin {
  ImageStream? imageStream; // 图片监听数据
  TestQuestionsImageInfo? imagInfoModel; // 试题图片数据
  late ImageStreamListener theImageStreamListener;
  // 用于记录手指位置的变量
  late List<GestureRecording> points;
  late List<dynamic> pointsPureData;
  // 用于记录绘图结果的变量
  bool _isEraserPressed = false; // 橡皮擦按下
  Offset? _eraserPosition; // 按下位置
  Offset? globalPosition = null; // 是否正在绘制

  int activePointers = 0;

  /// 获取数组指定倒数具体值的下标
  int _findTargetIndex<T>(List<T> list, T target, [int reciprocal = 2]) {
    List<int> indexs = [];
    for (int i = list.length - 1; i >= 0; i--) {
      if (list[i] == target) {
        indexs.add(i);
        if (indexs.length == reciprocal) return i;
      }
    }
    return -1;
  }

  @override
  void initState() {
    points = widget.points ?? [];
    pointsPureData = widget.pointsPureData ?? [];
    // 事件总线监听
    eventOn(callback: (eventModel) {
      switch (eventModel.runtimeType) {
        case BottomAnnotationSwitchCleanall:
          var item = eventModel as BottomAnnotationSwitchCleanall;
          if (item.previousStep) {
            if (points.isEmpty) {
              return ToastUtils.getFluttertoast(context: context, msg: '已经没有批注了', gravity: ToastGravity.BOTTOM);
            }
            // var index = pointsPureData.toList().lastIndexOf(null);
            var index = _findTargetIndex(pointsPureData, null);
            if (index != -1) {
              pointsPureData = pointsPureData.sublist(0, index + 1);
              points = points.sublist(0, index + 1);
              ref.read(jobHomeWorkProvider.notifier).setVal(points);
              toUpState(setState, () {}, mounted);

              // var _nullVal = points[index]; // 需要在最后一笔
              // if (index + 1 == pointsPureData.length) {
              //   pointsPureData = pointsPureData.sublist(0, index);
              //   points.sublist(0, index);
              //   index = pointsPureData.toList().lastIndexOf(null);
              //   index == -1 ? -1 : index + 1;
              // }
              // if (index != -1) {
              //   pointsPureData = pointsPureData.sublist(0, index);
              //   points = points.sublist(0, index);
              //   ref.read(jobHomeWorkProvider.notifier).setVal(points..add(_nullVal));
              //   // toUpState(setState, () {}, mounted);
              // } else {
              //   item.cleanAll = true;
              // }
            } else {
              item.cleanAll = true;
            }
          }

          if (item.cleanAll) {
            pointsPureData.clear();
            points.clear();
            toUpState(setState, () {}, mounted);
          }

          if (item.uploadImage) {
            // 图片确认按钮，生成
            if (pointsPureData.isEmpty) {
              ToastUtils.showInfo('请先批注再提交');
              return;
            }
          }
          break;
        case JobDoPapersSwitchOperationSubBus: // 关闭底部功能栏 有笔画需要改变笔画位置
          // var item = eventModel as JobDoPapersSwitchOperationSubBus;
          // bool annotationSwitch = item.open;
          // double heightVal = item.height;
          // if (points.isNotEmpty) {
          //   // item.height
          //   points.forEach((point) {
          //     bool open = point.annotationSwitch; // 是打开的功能栏的记录
          //     Offset? locatPoint = point.data;
          //     if (annotationSwitch != open && locatPoint != null) {
          //       point.annotationSwitch = annotationSwitch;
          //       locatPoint =
          //           Offset(locatPoint.dx, annotationSwitch ? locatPoint.dy - heightVal : locatPoint.dy + heightVal);
          //       point.data = locatPoint;
          //     }
          //   });
          //   ref.read(jobHomeWorkProvider.notifier).setVal(List.from(points));
          // }
          break;
        default:
      }
    });

    theImageStreamListener = ImageStreamListener((ImageInfo info, bool _) {
      // 获取图片的宽高
      imagInfoModel = TestQuestionsImageInfo(
        boxHeight: widget.boxHeight,
        boxWidth: widget.boxWidth,
        url: widget.showAnnotations ? (widget.commentImageUrl ?? widget.questionImgUrl) : widget.questionImgUrl,
        height: info.image.height.toDouble(),
        width: info.image.width.toDouble(),
      );
      var theHeight = imagInfoModel?.scaleHeight;
      if (theHeight != null) eventFire(model: DoPapersJobCustomPaintSizeBus(theHeight));
      print(imagInfoModel?.toJson());
    });

    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
    try {
      imageStream?.removeListener(theImageStreamListener);
    } catch (e) {}
    eventCancel();
  }

  @override
  Widget build(BuildContext context) {
    String imgUrl = widget.showAnnotations ? (widget.commentImageUrl ?? widget.questionImgUrl) : widget.questionImgUrl;
    return Listener(
      onPointerDown: (PointerDownEvent event) {
        // 判断当前是否已经有触摸点，如果有则忽略该触摸事件

        // 处理单个触摸点按下的逻辑
        activePointers++;
      },
      onPointerMove: (PointerMoveEvent event) {
        try {
          // imagInfoModel==null 图片加载完成
          if (activePointers != 1 || imagInfoModel == null) return;

          if (widget.graffitiSwitch.openBrush || widget.graffitiSwitch.openEraser) {
            Offset localPosition = event.localPosition;
            double imageHeightOffset = (imagInfoModel?.imageHeightOffsetStart ?? 0);
            if (imageHeightOffset > 0) {
              if (localPosition.dy < imageHeightOffset) return;
              if (localPosition.dy > imagInfoModel!.imageHeightOffsetend!) return;
            }
            bool annotationSwitch = ref.read(annotationGraffitiSwitchProvider).annotationSwitch; // 底部开关是否打开

            //
            double remainingHeight = imagInfoModel!.boxHeight - imagInfoModel!.scaleHeight!; // 剩余高度
            if (remainingHeight > 1) {
              double imageStartPositionY = remainingHeight / 2; // 图片起始Y轴位置
              localPosition = Offset(localPosition.dx, localPosition.dy - imageStartPositionY);
            }

            pointsPureData = List.from(pointsPureData)..add(localPosition);
            points = List.from(points)
              ..add(GestureRecording(
                  eraser: widget.graffitiSwitch.openEraser, data: localPosition, annotationSwitch: annotationSwitch));
            _eraserPosition = localPosition;
            _isEraserPressed = true;
            ref.read(jobHomeWorkProvider.notifier).setVal(points);
          }
        } catch (e) {
          toPrint(val: '进入报错');
        }
      },
      onPointerUp: (PointerUpEvent event) {
        // 处理单个触摸点抬起的逻辑
        activePointers--;
        globalPosition = null;
        if (widget.graffitiSwitch.openBrush || widget.graffitiSwitch.openEraser) {
          pointsPureData.add(null); // 增加空点以分隔不同的线段
          bool annotationSwitch = ref.read(annotationGraffitiSwitchProvider).annotationSwitch; // 底部开关是否打开
          points.add(GestureRecording(eraser: widget.graffitiSwitch.openEraser, annotationSwitch: annotationSwitch));
          _isEraserPressed = false;
          _eraserPosition = null;
          ref.read(jobHomeWorkProvider.notifier).setVal(points);
        }
      },
      child: Stack(
        alignment: const FractionalOffset(0.5, 0.5),
        children: [
          Container(
            color: Colors.transparent,
            constraints: BoxConstraints(minHeight: widget.boxHeight),
            alignment: Alignment.center,
            child: $TheCachedNetworkImage(
              (context, imageProvider) {
                Image imageWidget = Image(image: imageProvider, fit: BoxFit.fitWidth);
                imageStream?.removeListener(theImageStreamListener);
                imageStream = imageWidget.image.resolve(ImageConfiguration())..addListener(theImageStreamListener);
                return imageWidget;
              },
              imageUrl: widget.questionImgUrl,
            ),
          ),
          if (widget.showAnnotations && widget.commentImageUrl != null)
            $AnnotationImageDisplay(widget.boxHeight, widget.commentImageUrl!),
          // Container(
          //   constraints: BoxConstraints(minHeight: widget.boxHeight),
          //   alignment: Alignment.center,
          //   child: $TheCachedNetworkImage(
          //     (context, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
          //     imageUrl: widget.commentImageUrl!,
          //   ),
          // ),
          RepaintBoundary(
            key: widget.globalKey,
            child: TheExamPaperDrawing(
              imageUrl: imgUrl,
              commentImageUrl: widget.commentImageUrl,
              boxWidth: widget.boxWidth,
              boxHeight: widget.boxHeight,
              openEraser: widget.graffitiSwitch.openEraser,
              showAnnotations: widget.showAnnotations && widget.commentImageUrl != null,
            ),
          ),
        ],
      ),
    );
  }
}

// CustomPaint(
//   foregroundPainter: DrawingPainter(
//     points: points,
//     isErasing: widget.graffitiSwitch.openEraser,
//   ),
//   child: RepaintBoundary(
//     child: Container(
//       constraints: BoxConstraints(minHeight: widget.boxHeight),
//       child: $TheCachedNetworkImage(
//         (context, imageProvider) {
//           Image imageWidget = Image(image: imageProvider, fit: BoxFit.fitWidth);

//           imageStream?.removeListener(theImageStreamListener);
//           imageStream = imageWidget.image.resolve(ImageConfiguration())..addListener(theImageStreamListener);

//           return imageWidget;
//         },
//         imageUrl: widget.imgUrl,
//       ),
//     ),
//   ),
// ),

@hcwidget
Widget $annotationImageDisplay(WidgetRef ref, double boxHeight, String commentImageUrl) {
  List<GestureRecording> datas = ref.watch(jobHomeWorkProvider);
  if (datas.isNotEmpty) return Container();
  return Container(
    alignment: Alignment.center,
    child: $TheCachedNetworkImage(
      (context, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
      imageUrl: commentImageUrl,
    ),
  );
}

@hwidget
Widget $theCachedNetworkImage(ImageWidgetBuilder imageBuilder, {required String imageUrl}) {
  UseCachedImgRefresh _useImgRefsh = UseCachedImgRefresh.use();

  return CachedNetworkImage(
    key: _useImgRefsh.imageKey.value,
    fit: BoxFit.fitWidth,
    width: double.infinity,
    imageUrl: imageUrl,
    imageBuilder: imageBuilder,
    placeholder: (context, url) => Center(child: SpinKitWave(color: Theme.of(context).primaryColor, size: 50.r)),
    errorWidget: (context, url, error) {
      return GestureDetector(
        onTap: () => (_useImgRefsh.imageKey.value = UniqueKey()),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Image.asset('assets/images/test_paper_loading_failed.png'),
            quickText('加载失败，点击重试', color: Color.fromRGBO(148, 163, 182, 1), size: 12.sp),
          ],
        ),
      );
    },
  );
}

class DrawingPainter extends CustomPainter {
  final List<GestureRecording> points;
  final bool isErasing;
  final bool openErasing;

  DrawingPainter({required this.points, required this.isErasing})
      : openErasing = points.isNotEmpty && isErasing,
        super();

  Paint paintBrush = Paint()
    ..color = Colors.red
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 1.5;

  Paint eraser = Paint()
    ..blendMode = BlendMode.clear
    ..color = Colors.transparent
    ..style = PaintingStyle.stroke
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 100;

  final emptyPaint = Paint();
  final emptyPaintWithWidth = Paint()..strokeWidth = 60.0;

  @override
  void paint(Canvas canvas, Size size) {
    if (points.isNotEmpty) {
      // canvas.saveLayer(destRect, emptyPaintWithWidth);    // 只绘制图片大小区域
      canvas.saveLayer(Rect.largest, emptyPaintWithWidth); // 整个视图区域

      canvas.drawColor(Colors.transparent, BlendMode.clear);
    }

    for (int i = 0; i < points.length - 1; i++) {
      GestureRecording item = points[i];
      GestureRecording nextItem = points[i + 1];
      Offset? offsetData = item.data;
      Offset? nextOffsetData = nextItem.data;
      if (offsetData != null && nextOffsetData != null) {
        canvas.drawLine(offsetData, nextOffsetData, !item.eraser ? paintBrush : eraser);
      }
    }

    // 恢复画布状态.
    if (points.isNotEmpty) canvas.restore();
  }

  // @override
  // bool shouldRepaint(DrawingPainter oldDelegate) {
  //   List<GestureRecording> thePoints = oldDelegate.points;
  //   // var flag = oldDelegate.points != points || oldDelegate.isErasing != isErasing;
  //   return  thePoints != points;
  // }

  @override
  bool shouldRepaint(DrawingPainter oldDelegate) => true;
}

/// 网络图和本地图切换
@hwidget
Widget $localAndNetworkSwitch(
  BuildContext context, {
  required double boxWidth,
  required double boxHeight,
  required bool showAnnotations,
  required String questionImgUrl,
  required String? commentImageUrl,
  required bool drawFlag, // 是否打开绘画操作
  required GlobalKey theglobalKey,
  required GlobalKey<_ExamPaperDrawingState> examGlobalKey,
  required AnnotationGraffitiSwitch graffitiSwitch,
}) {
  UseLocalAndNetworkSwitch _useSwitch = UseLocalAndNetworkSwitch.use();
  return ExamPaperDrawing(
    boxWidth: boxWidth,
    boxHeight: boxHeight,
    key: examGlobalKey,
    globalKey: theglobalKey,
    graffitiSwitch: graffitiSwitch,
    points: _useSwitch.points.value,
    imageLoaded: _useSwitch.imageLoaded,
    pointsPureData: _useSwitch.pointsPureData.value,
    decoration: const BoxDecoration(color: const Color.fromRGBO(249, 250, 254, 1)),
    questionImgUrl: questionImgUrl,
    commentImageUrl: commentImageUrl,
    showAnnotations: showAnnotations,
  );
}

class UseLocalAndNetworkSwitch {
  ValueNotifier<bool> showZoomImg;
  ValueNotifier<File?> temFile;

  ValueNotifier<List<GestureRecording>?> points;
  ValueNotifier<List<dynamic>?> pointsPureData;
  ValueNotifier<Map<String, ui.Image>> imageLoaded;

  UseLocalAndNetworkSwitch._({
    required this.showZoomImg,
    required this.temFile,
    required this.points,
    required this.pointsPureData,
    required this.imageLoaded,
  });

  // 工厂构造函数
  factory UseLocalAndNetworkSwitch.use([bool defaultVal = true]) {
    return UseLocalAndNetworkSwitch._(
      points: useState(null),
      pointsPureData: useState(null),
      showZoomImg: useState(defaultVal),
      temFile: useState(null),
      imageLoaded: useState({}),
    );
  }

  Future<File?> createTempFile(
    BuildContext context, {
    required GlobalKey theglobalKey,
    required GlobalKey<_ExamPaperDrawingState> examGlobalKey,
  }) async {
    Timer? _timer;
    try {
      _timer = Timer(Duration(seconds: 1), () {
        // 执行操作的代码
        ToastUtils.showLoading();
      });
      if (examGlobalKey.currentState?.pointsPureData.isEmpty ?? true) {
        try {
          temFile.value?.delete();
        } catch (e) {}
        temFile.value = null;
        return null;
      }

      RenderRepaintBoundary? boundary = theglobalKey.currentContext!.findRenderObject() as RenderRepaintBoundary?;
      if (boundary == null) return null;
      ui.Image image = await boundary.toImage(pixelRatio: 3.0);
      ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
      if (byteData != null) {
        Directory appDocDir = await getApplicationDocumentsDirectory();
        List<int> bytes = byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
        String filePath = '${appDocDir.path}/${Uuid().v1()}.png'; // 文件路径及名称
        File file = File(filePath); // 创建文件对象
        await file.writeAsBytes(bytes); // 将ByteData写入文件

        temFile.value?.delete();
        temFile.value = file; // 保存临时文件

        points.value = examGlobalKey.currentState?.points;
        pointsPureData.value = examGlobalKey.currentState?.pointsPureData;
        toPrint(val: '图片保存成功：');
        return temFile.value;
      }
    } catch (e) {
      toPrint(val: '图片生成错误：${e}');
      toPrint(val: e.toString());
      ToastUtils.getFluttertoast(context: context, msg: '保存图片报错，请稍后重试');
    } finally {
      _timer?.cancel();
      ToastUtils.dismiss();
    }
    return null;
  }
}

/** 旧数据 

/*
 * @Author: wangyang 1147192855@qq.com
 * @Date: 2022-07-23 11:55:16
 * @LastEditors: wangyang 1147192855@qq.com
 * @LastEditTime: 2022-09-28 17:01:15
 * @FilePath: \marking_app\lib\components\JobPictureOverview.dart
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import 'dart:ui' as ui;
import 'dart:io';
import 'dart:async';
import 'package:crypto/crypto.dart' as crypto;
import 'package:image/image.dart' as img;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:marking_app/common/mixin/common.dart';
import 'package:marking_app/common/model/common/base_structure_result.dart';
import 'package:marking_app/common/model/common/upload_img_secret_key.dart';
import 'package:marking_app/common/model/event_bus/bottom_annotation_switch_cleanall.dart';
import 'package:marking_app/common/model/job/gesture_recording.dart';
import 'package:marking_app/common/model/job/job_note_taking_trajectory.dart';
import 'package:marking_app/common/model/job/test_questions_image_info.dart';
import 'package:marking_app/common/model/job/upload_file_interface_config.dart';
import 'package:marking_app/common/model/job/upload_file_interface_config_params.dart';
import 'package:marking_app/common/model/marking/annotation_graffiti_switch.dart';
import 'package:marking_app/pages/common/event_bus_mixin.dart';
import 'package:marking_app/pages/homework_correction/components/theExamPaperDrawing.dart';
import 'package:marking_app/pages/homework_correction/components/trajectoryView.dart';
import 'package:marking_app/pages/homework_correction/hooks/use_cached_img_refresh.dart';
import 'package:marking_app/pages/homework_correction/providers/drawing_provider.dart';
import 'package:marking_app/pages/homework_correction/providers/drawing_trajectory_provider.dart';
import 'package:marking_app/provider/annotation_graffiti_switch_provider.dart';
import 'package:marking_app/provider/upload_file_provider.dart';
import 'package:marking_app/utils/index.dart';
import 'package:marking_app/utils/my_text.dart';
import 'package:marking_app/utils/request/rest_client.dart';
import 'package:path_provider/path_provider.dart';

import 'package:uuid/uuid.dart';

part 'jobPictureOverview.g.dart';

class JobPictureOverview extends StatefulHookConsumerWidget {
  final int taskdetailId;
  final double containerWidth;
  final double containerHeight;
  final List imageItems; //图片列表
  final bool annotationsFlag;
  final String? questionImgUrl;
  final Map<String, String> commentImageMap;

  const JobPictureOverview({
    required this.taskdetailId,
    required this.containerWidth,
    required this.containerHeight,
    required this.imageItems,
    required this.annotationsFlag,
    required this.commentImageMap,
    this.questionImgUrl,
    Key? key,
  }) : super(key: key);

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

class JobPictureOverviewState extends ConsumerState<JobPictureOverview> with CommonMixin {
  final GlobalKey theglobalKey = GlobalKey();
  final GlobalKey<_ExamPaperDrawingState> examPaperDrawingKey = GlobalKey<_ExamPaperDrawingState>();

  final int currentIndex = 0;
  late AnnotationGraffitiSwitch graffitiSwitch;
  late RemoveListener _annotationsListener; // 批注关闭监听
  JobNoteTakingTrajectory? _jobNoteTakingTrajectory;
  File? temFile; // 批注临时数据

  ScrollController? _scrollController; // 滚动条
  double _scrollPosition = 0;
  bool canScroll = true;

  @override
  void initState() {
    _scrollController = ScrollController()
      ..addListener(() {
        _scrollPosition = _scrollController!.position.pixels;
      });

    // 更新批注记录
    Future.delayed(Duration.zero, () {
      int theTaskdetailId = ref.read(jobHomeWorkIdProvider); // 唯一的任务ID
      List<GestureRecording> taskDatas = ref.read(jobHomeWorkProvider);
      ref.read(jobHomeWorkIdProvider.notifier).setVal(widget.taskdetailId);
      if (theTaskdetailId > 0 && theTaskdetailId != widget.taskdetailId && taskDatas.isNotEmpty) {
        ref.read(jobHomeWorkProvider.notifier).setVal([]);
      }
    });
    _annotationsListener = ref.read(annotationGraffitiSwitchProvider.notifier).addListener((state) async {
      graffitiSwitch = state;
      canScroll = !state.magnifier;
      if (graffitiSwitch.trajectoryDisplay) {
        if (widget.questionImgUrl == null) {
          // AchievementView(
          //   elevation: 1,
          //   duration: Duration(milliseconds: 800),
          //   title: "回显提示",
          //   subTitle: "没有试题图片，无法回显试题",
          //   color: Color.fromRGBO(46, 91, 255, 1),
          // ).show(context);
          ToastUtils.showInfo('没有试题图片，无法回显笔记');
          ref.read(annotationGraffitiSwitchProvider.notifier).setTrajectory();
          return;
        }
        JobNoteTakingTrajectory? trajectory = await toGetNoteTakingTrajectory();
        if (trajectory == null) return;
      } else {
        if (mounted)
          Future.delayed(const Duration(seconds: 0)).then((_) {
            ref.read(jobDrawingTrajectoryProvider.notifier).setVal([]);
          });
      }
      toUpState(setState, () {}, mounted);
    });
    super.initState();
  }

  // 获取学生笔记轨迹
  Future<JobNoteTakingTrajectory?> toGetNoteTakingTrajectory() async {
    if (_jobNoteTakingTrajectory != null) {
      return null;
    }
    RestClient _client = await getClient();
    BaseStructureResult<JobNoteTakingTrajectory> baseRes = await _client.getNoteTakingTrajectory(widget.taskdetailId);
    if (baseRes.success && baseRes.data != null) {
      _jobNoteTakingTrajectory = baseRes.data!;
      return _jobNoteTakingTrajectory;
    }
    ToastUtils.showError('获取学生笔记失败，请重试');
    ref.read(annotationGraffitiSwitchProvider.notifier).setTrajectory();
    return null;
  }

  @override
  void dispose() {
    super.dispose();
    _scrollController?.dispose();
    _annotationsListener();
    try {
      if (temFile != null) {
        bool flieExist = temFile!.existsSync();
        if (flieExist) temFile!.delete();
      }
    } catch (e) {
      print('1111111111111111111111');
      print(e);
    }
  }

  Future<FileResult?> saveImage() async {
    try {
      ToastUtils.showLoading();
      if (temFile == null) {
        if (examPaperDrawingKey.currentState?.pointsPureData.isEmpty ?? true) return null;
        // 没有图片就上传图片
        RenderRepaintBoundary? boundary = theglobalKey.currentContext!.findRenderObject() as RenderRepaintBoundary?;

        // imagInfoModel
        if (boundary == null) return null;
        // double dpr = MediaQuery.of(context).devicePixelRatio;
        // print('设备像素比：$dpr');
        ui.Image image = await boundary.toImage();
        ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
        if (byteData == null) {
          /// 等于null 已经是异常了
          return null;
        }
        Directory appDocDir = await getApplicationDocumentsDirectory();
        List<int> bytes = byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
        String filePath = '${appDocDir.path}/${Uuid().v1()}.png'; // 文件路径及名称
        File file = File(filePath); // 创建文件对象
        await file.writeAsBytes(bytes); // 将ByteData写入文件
        temFile = file;
      }
      TestQuestionsImageInfo? theImageInfo = examPaperDrawingKey.currentState?.imagInfoModel;
      if (theImageInfo != null) {
        // 剪切图片
        List<GestureRecording>? points = examPaperDrawingKey.currentState?.points;
        if (points?.isNotEmpty ?? false) {
          // print('开始打印......................');
          List<double> dyArray = points!
              .where((e) => !e.eraser && e.data != null)
              .map((e) => double.parse(e.data!.dy.toStringAsFixed(2)))
              .toList(); // 获得有效的批注数据
          if (dyArray.isNotEmpty && theImageInfo.scaleHeight != null) {
            /**
             * 1：图片小于视图 按照最低点和最高点剪切图片
             * 2：图片大于视图 不剪切图片
             */
            double remainingHeight = theImageInfo.boxHeight - theImageInfo.scaleHeight!; // 剩余高度
            if (remainingHeight > 20) {
              // print('全部的数.............');
              // print(dyArray.toString());
              double lowestNumber = dyArray.reduce((currMin, next) => currMin < next ? currMin : next);
              double highestNumber = dyArray.reduce((currMax, next) => currMax > next ? currMax : next);

              // print('最低的数: $lowestNumber');
              // print('最高的数: $highestNumber');

              final img.Image? originalImage = img.decodeImage(temFile!.readAsBytesSync());
              if (originalImage != null) {
                temFile?.delete(); // 删除以后缓存图片
                // 计算裁剪区域的像素坐标
                // 裁剪图像

                double imageStartPositionY = remainingHeight / 2; // 图片起始Y轴位置
                double imageEndPositionY = imageStartPositionY + theImageInfo.scaleHeight!; // 图片结束Y轴位置
                int startY = imageStartPositionY.toInt();
                if (lowestNumber < imageStartPositionY) {
                  // 画图最小坐标小于图片 （向上操作了图片）
                  startY = lowestNumber.toInt();
                }

                int endY = (highestNumber - startY).toInt(); // 超出
                endY = endY + 10 > theImageInfo.boxHeight ? endY : endY + 10;

                if (highestNumber < imageEndPositionY) {
                  endY = (imageEndPositionY - startY).toInt();
                  endY = endY + 10 > theImageInfo.scaleHeight! ? endY : endY + 10;
                }

                final img.Image clippedImage = img.copyCrop(
                  originalImage,
                  x: 0,
                  y: startY,
                  height: endY,
                  width: originalImage.width,
                );

                Directory appDocDir = await getApplicationDocumentsDirectory();
                String filePath = '${appDocDir.path}/${Uuid().v1()}.png'; // 文件路径及名称
                File file = File(filePath); // 创建文件对象
                temFile = await file.writeAsBytes(img.encodePng(clippedImage));
              }
            }
          }
        }
      }
      /** */
      crypto.Digest fileMd5 = crypto.md5.convert(await temFile!.readAsBytes());
      String? fileName = temFile?.path.split('/').last;
      // print('获得文件名'); // 打印文件名
      // print(fileName); // 打印文件名

      RestClient _client = await getClient();
      BaseStructureResult<UploadFileInterfaceConfig> resUploadConfig =
          await _client.getUploadFile(UploadFileInterfaceConfigParams(
        fileName: fileName ?? '1.png',
        fileMd5: fileMd5.toString(),
        contentLength: temFile!.lengthSync(),
      ));
      if (!resUploadConfig.success || resUploadConfig.data == null) {
        ToastUtils.getFluttertoast(msg: '获取图片上传配置失败', context: context);
        return null;
      }
      if (resUploadConfig.data!.uploadUri == null) {
        return FileResult(myObject: '', success: true, url: resUploadConfig.data!.downloadUri);
      }
      FileResult? resFile =
          await ref.read(uploadFileProvider.notifier).getUploadFileConfig(resUploadConfig.data!, temFile!);
      // FileResult? resFile = await ref
      //     .read(uploadFileProvider.notifier)
      //     .uploadFile(temFile!.path, widget.imageItems[currentIndex], ref.read(userProvider).id.toString());
      if (resFile != null && resFile.success) {
        resFile.otherParam = currentIndex;
        return resFile;
      }
    } catch (e) {
      toPrint(val: e.toString());
    } finally {
      // ToastUtils.dismiss();
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    if (graffitiSwitch.trajectoryDisplay && widget.questionImgUrl != null && _jobNoteTakingTrajectory != null)
      return TrajectoryView(
        imageUrl: widget.questionImgUrl!,
        boxWidth: widget.containerWidth,
        boxHeight: widget.containerHeight,
        trajectory: _jobNoteTakingTrajectory!,
        // imageUrl: 'https://cdn9-banquan.ituchong.com/weili/image/l/903493495151657025.jpeg',
        // imageUrl: 'https://cdn6-banquan.ituchong.com/weili/image/l/903121241746964534.jpeg',
      );

    return Container(
      margin: EdgeInsets.only(top: 1.h),
      width: widget.containerWidth,
      height: widget.containerHeight,
      child: Scrollbar(
        // thumbVisibility: true,
        controller: _scrollController,
        child: SingleChildScrollView(
          physics: canScroll ? NeverScrollableScrollPhysics() : AlwaysScrollableScrollPhysics(),
          padding: EdgeInsets.only(right: 1.w, left: 1.w),
          scrollDirection: Axis.vertical, // 设置垂直滚动
          child: $LocalAndNetworkSwitch(
            boxWidth: widget.containerWidth,
            boxHeight: widget.containerHeight,
            theglobalKey: theglobalKey,
            graffitiSwitch: graffitiSwitch,
            drawFlag: widget.annotationsFlag,
            examGlobalKey: examPaperDrawingKey,
            imageUrl: widget.commentImageMap[widget.imageItems[currentIndex]]!,
          ),
        ),
      ),
    );
  }
}

// 试卷绘制
class ExamPaperDrawing extends StatefulHookConsumerWidget {
  double boxWidth;
  double boxHeight;
  String imgUrl;
  BoxDecoration? decoration;
  AnnotationGraffitiSwitch graffitiSwitch;
  List<GestureRecording>? points;
  List<dynamic>? pointsPureData;
  ValueNotifier<Map<String, ui.Image>> imageLoaded;

  GlobalKey globalKey;
  // Function(String) imageCall;
  ExamPaperDrawing({
    required this.boxWidth,
    required this.boxHeight,
    required this.imgUrl,
    required this.points,
    required this.pointsPureData,
    required this.graffitiSwitch,
    required this.globalKey,
    required this.imageLoaded,
    this.decoration,
    Key? key,
  }) : super(key: key);

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

class _ExamPaperDrawingState extends ConsumerState<ExamPaperDrawing>
    with EventBusMixin<BottomAnnotationSwitchCleanall> {
  ImageStream? imageStream; // 图片监听数据
  TestQuestionsImageInfo? imagInfoModel; // 试题图片数据
  late ImageStreamListener theImageStreamListener;
  // 用于记录手指位置的变量
  late List<GestureRecording> points;
  late List<dynamic> pointsPureData;
  // 用于记录绘图结果的变量
  bool _isEraserPressed = false; // 橡皮擦按下
  Offset? _eraserPosition; // 按下位置
  Offset? globalPosition = null; // 是否正在绘制

  int activePointers = 0;

  @override
  void initState() {
    points = widget.points ?? [];
    pointsPureData = widget.pointsPureData ?? [];
    // 事件总线监听
    eventOn(callback: (BottomAnnotationSwitchCleanall item) {
      if (item.previousStep) {
        if (points.isEmpty) {
          ToastUtils.showInfo('批注已清空');
          return;
        }
        var index = pointsPureData.toList().lastIndexOf(null);
        if (index != -1) {
          if (index + 1 == pointsPureData.length) {
            pointsPureData = pointsPureData.sublist(0, index);
            points.sublist(0, index);
            index = pointsPureData.toList().lastIndexOf(null);
            index == -1 ? -1 : index + 1;
          }
          if (index != -1) {
            pointsPureData = pointsPureData.sublist(0, index);
            points = points.sublist(0, index);
            ref.read(jobHomeWorkProvider.notifier).setVal(points);
            // toUpState(setState, () {}, mounted);
          } else {
            item.cleanAll = true;
          }
        } else {
          item.cleanAll = true;
        }
      }

      if (item.cleanAll) {
        pointsPureData.clear();
        points.clear();
        toUpState(setState, () {}, mounted);
      }

      if (item.uploadImage) {
        // 图片确认按钮，生成
        if (pointsPureData.isEmpty) {
          ToastUtils.showInfo('请先批注再提交');
          return;
        }
      }
    });

    theImageStreamListener = ImageStreamListener((ImageInfo info, bool _) {
      // 获取图片的宽高
      imagInfoModel = TestQuestionsImageInfo(
        boxHeight: widget.boxHeight,
        boxWidth: widget.boxWidth,
        url: widget.imgUrl,
        height: info.image.height.toDouble(),
        width: info.image.width.toDouble(),
      );
    });

    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
    try {
      imageStream?.removeListener(theImageStreamListener);
    } catch (e) {}
    eventCancel();
  }

  @override
  Widget build(BuildContext context) {
    return Listener(
      onPointerDown: (PointerDownEvent event) {
        // 判断当前是否已经有触摸点，如果有则忽略该触摸事件

        // 处理单个触摸点按下的逻辑
        activePointers++;
        print('按下：$activePointers');
      },
      onPointerMove: (PointerMoveEvent event) {
        try {
          if (activePointers != 1) return;

          if (widget.graffitiSwitch.openBrush || widget.graffitiSwitch.openEraser) {
            Offset localPosition = event.localPosition;
            pointsPureData = List.from(pointsPureData)..add(localPosition);
            points = List.from(points)
              ..add(GestureRecording(eraser: widget.graffitiSwitch.openEraser, data: localPosition));
            _eraserPosition = localPosition;
            _isEraserPressed = true;
            // setState(() {});
            ref.read(jobHomeWorkProvider.notifier).setVal(points);
          }
        } catch (e) {
          toPrint(val: '进入报错');
        }
      },
      onPointerUp: (PointerUpEvent event) {
        // 处理单个触摸点抬起的逻辑
        activePointers--;
        print('抬起：$activePointers');
        globalPosition = null;
        if (widget.graffitiSwitch.openBrush || widget.graffitiSwitch.openEraser) {
          pointsPureData.add(null); // 增加空点以分隔不同的线段
          points.add(GestureRecording(eraser: widget.graffitiSwitch.openEraser));
          _isEraserPressed = false;
          _eraserPosition = null;
          ref.read(jobHomeWorkProvider.notifier).setVal(points);
        }
      },
      child: RepaintBoundary(
          key: widget.globalKey,
          child: TheExamPaperDrawing(
            imageUrl: widget.imgUrl,
            boxHeight: widget.boxHeight,
            openEraser: widget.graffitiSwitch.openEraser,
            imageBuilder: (context, imageProvider) {
              Image imageWidget = Image(image: imageProvider, fit: BoxFit.fitWidth);
              imageStream?.removeListener(theImageStreamListener);
              imageStream = imageWidget.image.resolve(ImageConfiguration())..addListener(theImageStreamListener);
              return imageWidget;
            },
          )
          // CustomPaint(
          //   foregroundPainter: DrawingPainter(
          //     points: points,
          //     isErasing: widget.graffitiSwitch.openEraser,
          //   ),
          //   child: RepaintBoundary(
          //     child: Container(
          //       constraints: BoxConstraints(minHeight: widget.boxHeight),
          //       child: $TheCachedNetworkImage(
          //         (context, imageProvider) {
          //           Image imageWidget = Image(image: imageProvider, fit: BoxFit.fitWidth);

          //           imageStream?.removeListener(theImageStreamListener);
          //           imageStream = imageWidget.image.resolve(ImageConfiguration())..addListener(theImageStreamListener);

          //           return imageWidget;
          //         },
          //         imageUrl: widget.imgUrl,
          //       ),
          //     ),
          //   ),
          // ),
          ),
    );
  }
}

@hwidget
Widget $theCachedNetworkImage(ImageWidgetBuilder imageBuilder, {required String imageUrl}) {
  UseCachedImgRefresh _useImgRefsh = UseCachedImgRefresh.use();

  return CachedNetworkImage(
    key: _useImgRefsh.imageKey.value,
    fit: BoxFit.fitWidth,
    width: double.infinity,
    imageUrl: imageUrl,
    imageBuilder: imageBuilder,
    placeholder: (context, url) => Center(child: SpinKitWave(color: Theme.of(context).primaryColor, size: 50.r)),
    errorWidget: (context, url, error) {
      return GestureDetector(
        onTap: () => (_useImgRefsh.imageKey.value = UniqueKey()),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Image.asset('assets/images/test_paper_loading_failed.png'),
            quickText('加载失败，点击重试', color: Color.fromRGBO(148, 163, 182, 1), size: 12.sp),
          ],
        ),
      );
    },
  );
}

class DrawingPainter extends CustomPainter {
  final List<GestureRecording> points;
  final bool isErasing;
  final bool openErasing;

  DrawingPainter({required this.points, required this.isErasing})
      : openErasing = points.isNotEmpty && isErasing,
        super();

  Paint paintBrush = Paint()
    ..color = Colors.red
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 1.5;

  Paint eraser = Paint()
    ..blendMode = BlendMode.clear
    ..color = Colors.transparent
    ..style = PaintingStyle.stroke
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 100;

  final emptyPaint = Paint();
  final emptyPaintWithWidth = Paint()..strokeWidth = 60.0;

  @override
  void paint(Canvas canvas, Size size) {
    if (points.isNotEmpty) {
      // canvas.saveLayer(destRect, emptyPaintWithWidth);    // 只绘制图片大小区域
      canvas.saveLayer(Rect.largest, emptyPaintWithWidth); // 整个视图区域

      canvas.drawColor(Colors.transparent, BlendMode.clear);
    }

    for (int i = 0; i < points.length - 1; i++) {
      GestureRecording item = points[i];
      GestureRecording nextItem = points[i + 1];
      Offset? offsetData = item.data;
      Offset? nextOffsetData = nextItem.data;
      if (offsetData != null && nextOffsetData != null) {
        canvas.drawLine(offsetData, nextOffsetData, !item.eraser ? paintBrush : eraser);
      }
    }

    // 恢复画布状态.
    if (points.isNotEmpty) canvas.restore();
  }

  // @override
  // bool shouldRepaint(DrawingPainter oldDelegate) {
  //   List<GestureRecording> thePoints = oldDelegate.points;
  //   // var flag = oldDelegate.points != points || oldDelegate.isErasing != isErasing;
  //   return  thePoints != points;
  // }

  @override
  bool shouldRepaint(DrawingPainter oldDelegate) => true;
}

/// 网络图和本地图切换
@hwidget
Widget $localAndNetworkSwitch(
  BuildContext context, {
  required double boxWidth,
  required double boxHeight,
  required String imageUrl, // 图片
  required bool drawFlag, // 是否打开绘画操作
  required GlobalKey theglobalKey,
  required GlobalKey<_ExamPaperDrawingState> examGlobalKey,
  required AnnotationGraffitiSwitch graffitiSwitch,
}) {
  UseLocalAndNetworkSwitch _useSwitch = UseLocalAndNetworkSwitch.use();
  print('localAndNetworkSwitch..........build..............');
  return ExamPaperDrawing(
    boxWidth: boxWidth,
    boxHeight: boxHeight,
    imgUrl: imageUrl,
    key: examGlobalKey,
    globalKey: theglobalKey,
    graffitiSwitch: graffitiSwitch,
    points: _useSwitch.points.value,
    imageLoaded: _useSwitch.imageLoaded,
    pointsPureData: _useSwitch.pointsPureData.value,
    decoration: const BoxDecoration(color: const Color.fromRGBO(249, 250, 254, 1)),
  );
}

class UseLocalAndNetworkSwitch {
  ValueNotifier<bool> showZoomImg;
  ValueNotifier<File?> temFile;

  ValueNotifier<List<GestureRecording>?> points;
  ValueNotifier<List<dynamic>?> pointsPureData;
  ValueNotifier<Map<String, ui.Image>> imageLoaded;

  UseLocalAndNetworkSwitch._({
    required this.showZoomImg,
    required this.temFile,
    required this.points,
    required this.pointsPureData,
    required this.imageLoaded,
  });

  // 工厂构造函数
  factory UseLocalAndNetworkSwitch.use([bool defaultVal = true]) {
    return UseLocalAndNetworkSwitch._(
      points: useState(null),
      pointsPureData: useState(null),
      showZoomImg: useState(defaultVal),
      temFile: useState(null),
      imageLoaded: useState({}),
    );
  }

  Future<File?> createTempFile(
    BuildContext context, {
    required GlobalKey theglobalKey,
    required GlobalKey<_ExamPaperDrawingState> examGlobalKey,
  }) async {
    Timer? _timer;
    try {
      _timer = Timer(Duration(seconds: 1), () {
        // 执行操作的代码
        ToastUtils.showLoading();
      });
      if (examGlobalKey.currentState?.pointsPureData.isEmpty ?? true) {
        try {
          temFile.value?.delete();
        } catch (e) {}
        temFile.value = null;
        return null;
      }

      RenderRepaintBoundary? boundary = theglobalKey.currentContext!.findRenderObject() as RenderRepaintBoundary?;
      if (boundary == null) return null;
      ui.Image image = await boundary.toImage(pixelRatio: 3.0);
      ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
      if (byteData != null) {
        Directory appDocDir = await getApplicationDocumentsDirectory();
        List<int> bytes = byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
        String filePath = '${appDocDir.path}/${Uuid().v1()}.png'; // 文件路径及名称
        File file = File(filePath); // 创建文件对象
        await file.writeAsBytes(bytes); // 将ByteData写入文件

        temFile.value?.delete();
        temFile.value = file; // 保存临时文件

        points.value = examGlobalKey.currentState?.points;
        pointsPureData.value = examGlobalKey.currentState?.pointsPureData;
        toPrint(val: '图片保存成功：');
        return temFile.value;
      }
    } catch (e) {
      toPrint(val: '图片生成错误：${e}');
      toPrint(val: e.toString());
      ToastUtils.getFluttertoast(context: context, msg: '保存图片报错，请稍后重试');
    } finally {
      _timer?.cancel();
      ToastUtils.dismiss();
    }
    return null;
  }
}



*/
