part of web;

class ReadController extends FullLifeCycleController with GetSingleTickerProviderStateMixin{

  final String bookId;
  String chapterId;
  String chapterName;
  final BookDetailModel bookDetailModel;
  final String noteId;
  ReadController({required this.bookId, required this.chapterId,required this.chapterName,required this.bookDetailModel,required this.noteId});

  late InAppWebViewController webViewController;

  // 目录
  List <ChapterModel> chapters = [];
  List <ToolModel> tools = [
    ToolModel(tag: 0,name: '目录',selected: false,icon: 'assets/images/category_unselect.png',activeIcon: 'assets/images/category_select.png'),
    ToolModel(tag: 1,name: '笔记',selected: false,icon: 'assets/images/note_unselect.png',activeIcon:'assets/images/note_select.png'),
    ToolModel(tag: 2,name: '讨论',selected: false,icon: 'assets/images/discuss_unselect.png',activeIcon:'assets/images/discuss_select.png'),
  ];

  late ToolModel toolModel = tools.first;

  // 输入框窗口是否展示
  bool  showChat = false;
  // 0 讨论 1 笔记
  late int chatType = 0;
  // 笔记是否公开
  bool isPublic = false;
  // 笔记位置信息
  String notePosition = '';
  // 话题焦点
  final FocusNode discussTitleFocusNode = FocusNode();
  // 内容焦点
  final FocusNode discussContentFocusNode = FocusNode();
  // 讨论话题标题
  final TextEditingController titleInput = TextEditingController();
  // 讨论内容
  final TextEditingController contentInput = TextEditingController();
  //
  // 讨论添加的图片path数组
  List <String> discussInputImages= [];
  // 讨论添加的语音path数组
  List <AudioModel> discussInputAudios= [];
  // 笔记标题
  String noteTitle = '';

  bool _show = false;
  bool get show => _show;

  // 录音
  final FlutterSoundRecorder _mRecorder = FlutterSoundRecorder(logLevel:Level.error);
  // 录音开始
  bool startRecording = false;
  // 是否存在离线文件
  bool existDownFile= false;

  // 网络状态
  bool netStatus = false;

  // 当前html名称 0-318.html
  late String currentHtmlName = '';

  // 朗读组件
  late FlutterTts flutterTts;

  // 是否展示搜索结果
  bool showSearch = false;

  final int _searchLimit = 10;
  int _searchPage = 1;
  bool _searchNoMore = false;

  // 搜全文
  List<SearchAllModel> searchALlResults = [];

  final EasyRefreshController refreshController = EasyRefreshController(
    controlFinishLoad: true,
    controlFinishRefresh: true,
  );

  late TextEditingController searchInput = TextEditingController();

  just_audio.AudioPlayer audioPlayer = just_audio.AudioPlayer();

  late SearchAllModel sModel = SearchAllModel(bookId: '0');

  // 本地文件地址
  String localHtml5Path = '';




  ///------------------------------------------ 页面 生命周期--------------------------------------------------------
  @override
  void onInit() async {
    initTts();
    netStatus = await Tools.checkCurrentNetStatus();
    discussTitleFocusNode.addListener(_onCommentFocusChanged);
    // 开启防截屏
    // await ScreenProtector.preventScreenshotOn();
    super.onInit();
  }

  @override
  void onReady() async {
    // 上报开始阅读时间
    _addReadTime(type: 'open');
    _getChapters();

    netStatus = await Tools.checkCurrentNetStatus();
    final exist = await _isExistFile(bookId);
    if (!netStatus && exist){

       // 1、通过 chapterId 获取 对应离线的 html路径
       String toReadHtmlPath = await getLocalReadHtml(chapterId);

       // 2、开始读书
       read(toReadHtmlPath);

       // 获取当前 文件名称
       currentHtmlName = path.basename(toReadHtmlPath);

       update();
    }

    super.onReady();
  }

  @override
  void onClose() async {
    // 上报阅读结束时间
    _addReadTime(type: 'close');
    discussTitleFocusNode.removeListener(_onCommentFocusChanged);
    discussTitleFocusNode.dispose();
    titleInput.dispose();
    contentInput.dispose();
    flutterTts.stop();
    searchInput.dispose();
    _mRecorder.closeRecorder();
    _mRecorder == null;
    // 关闭防截屏
    // await ScreenProtector.preventScreenshotOff();
    super.onClose();
  }
  ///------------------------------------------ 页面 生命周期--------------------------------------------------------

  void chooseTool(ToolModel selectedModel){
    for (var model in tools) {
      // 如果当前遍历到的工具是选中的，并且不是点击的工具，则取消选中
      if (model.selected && model != selectedModel) {
        model.selected = false;
      }
      // 如果当前遍历到的工具是点击的工具，切换选中状态
      else if (model == selectedModel) {
        model.selected = !model.selected;
      }
    }
    toolModel = selectedModel;
    update();
  }

  // // 设置 chapterId 和 chapterName
  // void setChapterInfo({required String id,required String name}){
  //   chapterId = id;
  //   chapterName = name;
  //   writeCurrentReadChapterIdToData(chapters);
  //   update();
  // }
  void setShow(bool value) {
    _show = !value;
    update();
  }

  void setShowSearch(bool show){
    if(show == false){
      searchInput.text = '';
    }
    showSearch = show;
    update();
  }

  // 选择了某个章节
  void selectChapter(ChapterModel model) {
    chapterName = model.name??'';
    chapterId = model.id.toString();
    writeCurrentReadChapterIdToData(chapters);
    update();
  }

  // 读取那个章节
  void readLocalHtml(String chapterId) async {
    // 判断是否有离线文件 如果有使用离线阅读
    final exist = await _isExistFile(bookId);
    if (netStatus && exist){
      // 没有网并且有离线文件 离线阅读
      String path = await _getDirectory();
      String finalPath = '$path/$bookId/0-318.html';
      final content = await readHtmlFileContent(finalPath);
      Console.log('原始内容-----------------$content');
      String htmlStr = EncryptUtil.aesDecrypt(content!);
      Console.log('解密-----------------$htmlStr');
      Console.log('-------------使用本地文件-------------------');
      webViewController.loadData(data: htmlStr??'');

    }

  }

  // 初始化朗读组件
  initTts() {
    flutterTts = FlutterTts();
    _setAwaitOptions();
    if (Platform.isAndroid) {
      _getDefaultEngine();
      _getDefaultVoice();
    }

    flutterTts.setStartHandler(() {
      Console.log('-------------Playing-------------------');

    });

    if (Platform.isAndroid) {
      flutterTts.setInitHandler(() {
        Console.log('-------------TTS Initialized-------------------');
      });
    }

    flutterTts.setCompletionHandler(() {
      Console.log('-------------TTS Complete-------------------');
    });

    flutterTts.setCancelHandler(() {
      Console.log('-------------TTS Cancel-------------------');
    });

    flutterTts.setPauseHandler(() {
      Console.log('-------------TTS Paused-------------------');
    });

    flutterTts.setContinueHandler(() {
      Console.log('-------------TTS Continued-------------------');
    });

    flutterTts.setErrorHandler((msg) {
      Console.log('-------------TTS error-------------------$msg');
    });

  }
  Future _setAwaitOptions() async {
    await flutterTts.awaitSpeakCompletion(true);
  }
  Future _getDefaultEngine() async {
    var engine = await flutterTts.getDefaultEngine;
    if (engine != null) {
      Console.log('engine--------------------------------$engine');
    }
  }

  Future _getDefaultVoice() async {
    var voice = await flutterTts.getDefaultVoice;
    if (voice != null) {
      Console.log('voice--------------------------------$voice');
    }
  }

  Future speak(String text) async {
    await flutterTts.setVolume(0.5);
    await flutterTts.setSpeechRate(0.5);
    await flutterTts.setPitch(1.0);

    if (text != null) {
      if (text!.isNotEmpty) {
        await flutterTts.speak(text!);
      }
    }
  }

  // 初始化录音组件
  Future<void> openTheRecorder() async {
     // 获取权限
     if(await Access.microphone()){
        await _mRecorder.openRecorder();
        final session = await AudioSession.instance;
        await session.configure(AudioSessionConfiguration(
          avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
          avAudioSessionCategoryOptions:
          AVAudioSessionCategoryOptions.allowBluetooth |
          AVAudioSessionCategoryOptions.defaultToSpeaker,
          avAudioSessionMode: AVAudioSessionMode.spokenAudio,
          avAudioSessionRouteSharingPolicy:
          AVAudioSessionRouteSharingPolicy.defaultPolicy,
          avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
          androidAudioAttributes: const AndroidAudioAttributes(
            contentType: AndroidAudioContentType.speech,
            flags: AndroidAudioFlags.none,
            usage: AndroidAudioUsage.voiceCommunication,
          ),
          androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
          androidWillPauseWhenDucked: true,
        ));
     }
     // 没有权限
     else {

     }
  }
  // 开启录音
  void record() async {
    openTheRecorder();
    update();

    startRecording = true;
    String filePath = await Tools.getDirectory();
    String fileName = Tools.generateVoiceFileName();
    String recordePath = '$filePath/$fileName';
    Console.log('record---------------------$recordePath');
    _mRecorder.startRecorder(
      toFile: recordePath,
      audioSource: AudioSource.microphone,
      codec: Codec.aacMP4,

    );

    // _mRecorder?.setSubscriptionDuration(Duration(milliseconds: 100));
    // _mRecorder?.onProgress?.listen((e)
    // {
    //   var date = DateTime.fromMillisecondsSinceEpoch(e.duration.inMilliseconds,isUtc: true);
    //   var  text = DateFormat('mm:ss:').format(date);
    //   print('--------------$text');
    //
    // });
    update();
  }
  // 停止录音
  void stopRecorder() async{
    startRecording = false;
    final path = await _mRecorder.stopRecorder();
    Console.log('stopRecorder-----------path---------------------$path');
    if(path!=null && path.isNotEmpty){
      var duration = await audioPlayer.setFilePath(path!);
      Console.log('-----duration---------------------$duration------');
      AudioModel audioModel =  AudioModel(path: path!,duration: Tools.formatDuration(duration!),currentDuration: '0:00:00');
      if(audioModel.duration !='0:00:00'){
        discussInputAudios.add(audioModel);
      }

    }
    update();
  }

  //
  String formatDuration(Duration duration) {
    String twoDigits(int n) => n.toString().padLeft(2, '0');
    String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
    String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
    return '${duration.inHours}:$twoDigitMinutes:$twoDigitSeconds';
  }


  // 语音文件名称
  String generateVoiceFileName(){
    DateTime now = DateTime.now();
    String formattedDate = DateFormat('yyyyMMddHHmmss').format(now);
    return 'voice_$formattedDate.mp4';
  }

  // 添加语音路径
  void addVoiceFilePath(){

  }

  // 重置所有信息
  void reset(){
    clearAllDiscussInput();
  }

  // 播放音频
  void playAudio(AudioModel audioModel){

    Console.log('-------------播放开始-------------------');
    if(audioPlayer.playerState.playing){
      audioPlayer.stop();
      audioModel.currentDuration = '0:00:00';
      // if(currentPlayMediaModel.id == mediaModel.id){
      //   return;
      // }
    }
    // 本地音频
    audioPlayer.setFilePath(audioModel.path);
    audioPlayer.play();
    StreamSubscription? positionSubscription;
    positionSubscription =audioPlayer.positionStream.listen((position) {
      String temp =  Tools.formatDuration(position);
      Console.log('播放时间---------------------$temp------id-------------${audioModel.path}');
      audioModel.currentDuration = temp;
      if(position >= audioPlayer.duration!){
        Console.log('---------播放结束-----------');
        positionSubscription?.cancel();
      }
      update();
    });
    // currentPlayMediaModel = mediaModel;
  }
  // 添加讨论图片
  void addDiscussInputImages(String path){
    discussInputImages.add(path);
    Console.log('discussInputImages--------------------------------$path');
    update();
  }
  // 删除讨论图片
  void delDiscussInputImages(String path){
    discussInputImages.remove(path);
    Console.log('delDiscussInputImages--------------------------------$path');
    update();
  }
  // 清空讨论图片
  void clearDiscussInputImages(){
    discussInputImages.clear();
    Console.log('clearDiscussInputImages--------------------------------');
    update();
  }
  // 情况语音
  void clearDiscussAudios(){
    discussInputAudios.clear();
    Console.log('clearDiscussAudios--------------------------------');
    update();
  }
  // 清空所有已经填写的数据
  void clearAllDiscussInput(){
    clearDiscussInputImages();
    clearDiscussAudios();
    titleInput.text = '';
    contentInput.text = '';
    isPublic = false;
    noteTitle = '';
    Console.log('clearAllDiscussInput--------------------------------');
    update();
  }
  // 删除音频
  void delAudio(AudioModel audioModel){
    discussInputAudios.remove(audioModel);
    update();
  }


  // 上传文件
  Future<String> upload({
    required String path
  }) async {
    // String result  = await CommonAPI.upload(path:path,fileTypes: 'comment');
    // return result;
    OssTool tool = OssTool('zxts-comment-file');
    final response = await tool.putObjectFile(path);
    Console.log('------response--------------------------${response.realUri}');
    return response.realUri.toString();
  }

  // 提交
  Future<bool> submit() async {
    // 音频链接数组
    List<String> audios = [];
    // 图片链接数组
    List<String> images = [];

    final status = await Tools.checkCurrentNetStatus();

    if(chatType == 0){
      if(contentInput.text.isEmpty && discussInputImages.isEmpty){
        Toast.show('话题必须填写内容或选择图片');
        return false;
      }
    }
    else if(chatType == 1){
      if(contentInput.text.isEmpty && discussInputImages.isEmpty && discussInputAudios.isEmpty){
        Toast.show('笔记必须填写内容或选择图片或音频');
        return false;
      }
    }

    // 有网情况下 先直传oss 获取到url
    if (status){
      CustomToast.loading();
      // 循环上传图片获取地址
      for(String path in discussInputImages){
        final url = await upload(path: path);
        images.add(url);
      }

      // 循环上传音频获取地址
      for(AudioModel model in discussInputAudios){
        final url = await upload(path: model.path);
        audios.add(url);
      }
      CustomToast.dismiss();
    }
    // 没有网的情况下 存储到本地数据库
    else{
      for(String path in discussInputImages){
        images.add(path);
      }

      // 循环上传音频获取地址
      for(AudioModel model in discussInputAudios){
        audios.add(model.path);
      }
    }


    Map<String,dynamic> contentMap = {
      'text':contentInput.text,
      'audio':audios,
      'image':images
    };

    // 话题 只有有网的时候才能发起话题
    if (chatType == 0){
      final result = await addDiscuss(contentMap);
      return result;
    }
    // 笔记 有网没有网都能发起笔记
    else if (chatType == 1){
      final result = addNote(contentMap);
      return result;
    }
    return false;
  }

  // 添加笔记
  Future<bool> addNote(Map<String,dynamic> contentMap) async {
    final status = await Tools.checkCurrentNetStatus();
    bool result ;
    // 有网直接上传接口
    if (status){
      result = await LibraryAPI.addNote(
          bookId: bookId,
          chapterId: chapterId,
          content: noteTitle,
          isOpen: isPublic?'1':'0',
          positioning: notePosition,
          noteContent: jsonEncode(contentMap)
      );
    }
    // 没有网存储本地数据库
    else{
      Map<String, dynamic> data = {
        'types': 3,
        'book_id': int.parse(bookId),
        'chapter_id': int.parse(chapterId),
        'is_open': isPublic?1:0,
        'color': '#FF0000',
        'content': noteTitle,
        'upload': 0,
        'positioning': notePosition,
        'note_content': jsonEncode(contentMap),
        'node_id': 0,
      };

      result =  await SqlManager.insertData(data);
    }

    if(result){
      Toast.show('笔记发表成功');
    }
    else{
      Toast.show('笔记发表失败');
    }

    // 重置所有信息
    reset();
    setShowChat(false);

    return result;

  }

  // 发表评论
  // {String commentId ='0',String quoteContent ='',String title = ''}
  Future<bool> addDiscuss(Map<String,dynamic> contentMap) async{

    final result = await LibraryAPI.addDiscuss(
        bookId: bookId,
        chapterId: chapterId,
        commentId: '0',
        quoteContent: noteTitle,
        title: titleInput.text,
        content: jsonEncode(contentMap)
    );
    if(result){
      Toast.show('话题发表成功');
    }
    else{
      Toast.show('话题发表失败');
    }

    // 重置所有信息
    reset();
    setShowChat(false);
    return result;

  }



  // AnimationController get controller => _controller;

  void _onCommentFocusChanged() {
    if (discussTitleFocusNode.hasFocus || discussContentFocusNode.hasFocus) {
      setShowChat(true);
    } else {
      setShowChat(false);
    }
  }

  // 展示输入框
  void setShowChat(bool value) {
    showChat = value;
    if(value == false){
      reset();
    }
    update();
  }
  // 显示输入框类型
  void setChatType(int type){
    chatType = type;
  }
  // 设置笔记是否公开
  void setIsPublic(){
    isPublic = !isPublic;
    update();
  }

  // void setNoteTitle(String title){
  //   noteTitle = title;
  //   // update();
  // }



  ///------------------------------------------离线逻辑--------------------------------------------------------

  // 下载文件
  Future<void> extractZipFileFromCache(String url) async {
    // 从缓存中获取 ZIP 文件
    var file = await DefaultCacheManager().getSingleFile(url);
    if (file != null) {
      // 读取 ZIP 文件内容
      Uint8List bytes = await file.readAsBytes();
      // 解压缩 ZIP 文件
      Archive archive = ZipDecoder().decodeBytes(bytes);
      // 获取设备上的临时目录
      String tempPath = await Tools.getDirectory();
      // 将解压缩后的文件保存到临时目录中
      for (var file in archive) {
        if (file.isFile) {
          String fileName = file.name;
          String filePath = '$tempPath/$bookId/$fileName';
          File(filePath)
            ..createSync(recursive: true)
            ..writeAsBytesSync(file.content as List<int>);
          Console.log('解压缩文件：$fileName，保存路径：$filePath');
        }
      }
      CustomToast.dismiss();
      Toast.show('离线成功');
      final exit = await _isExistFile(bookId);
      update();

    } else {
      Console.log('未找到缓存中的文件或文件不存在');
    }
  }

  // 判断是否存在离线文件
  Future<bool> _isExistFile(String bookId) async {
    String directoryPath = await Tools.getDirectory();
    Directory directory = Directory(directoryPath);
    bool directoryExists = await directory.exists();
    if (directoryExists) {
      Console.log('存在名为 "$bookId" 的文件夹');
      existDownFile =  await Directory('${directory.path}/$bookId').exists();

    }
    else {
      Console.log('不存在名为 "$bookId" 的文件夹');
      existDownFile = false;
    }
    update();
    return existDownFile;

  }

  Future<String> getLocalReadHtml(String chapterId) async{
    String docPath = await Tools.getDirectory();
    String filePath = '$docPath/$bookId';
    Directory directory = Directory(filePath);
    // 获取目录下的所有文件
    List<FileSystemEntity> files = directory.listSync(recursive: true);
    for (var file in files) {
      if (file is File && file.path.toLowerCase().endsWith('.html')) {
        String fileName = path.basenameWithoutExtension(file.path);
        if (fileName.split('-').last == chapterId){
          return  file.path;
        }
      }
    }
    return '';

  }

  // 开始阅读离线
  void read(String toReadHtmlPath) async {
    // 3、获取 离线文件的内容
    final content = await readHtmlFileContent(toReadHtmlPath);
    // 4、解密离线的内容
    String htmlStr = EncryptUtil.aesDecrypt(content!);
    // 5、将离线内容写入临时文件
    String directoryPath = path.dirname(toReadHtmlPath);
    String writeFilePath = '$directoryPath/read.html';
    // 6、创建临时文件
    File file = File(writeFilePath);
    // 7、将HTML内容写入文件
    await file.writeAsString(htmlStr);

    localHtml5Path = writeFilePath;
    String url = writeFilePath;
    if(Platform.isIOS){
      url = 'file://$writeFilePath';
    }
    webViewController.loadUrl(urlRequest: URLRequest(
      // url: Uri.parse(writeFilePath),
        url: WebUri.uri(Uri.parse(url))
    ));

    final result = await SqlManager.updateReadHistoryByBookId(int.parse(bookId), int.parse(chapterId));
    Console.log('Sql----readread---存入数据库读到的章节----------------book_id:$bookId-----chapterId:$chapterId---------result:$result--');

    queryLocalNote();
  }

  // 本地阅读 读取上一章节 或 下一章节
  // 0 上一章节
  // 1 下一章节
  Future<void> readChapter({required int type}) async {
    Console.log('---------------获取所有html---------------------------------');

    String docPath = await _getDirectory();
    String filePath = '$docPath/$bookId';
    Directory directory = Directory(filePath);
    // 获取目录下的所有文件
    List<FileSystemEntity> files = directory.listSync(recursive: true);

    int findIndex = int.parse(currentHtmlName.split('-').first);
    if(type == 0){
      findIndex--;
      if(findIndex <0){
        Toast.show('前面已没有章节');
        // 已到最前
        return ;
      }
    }
    else{
      findIndex++;
      if(findIndex >files.length -1){
        // 已到最后
        return ;
      }
    }

    String toReadHtmlPath = '';

    // 打印所有 HTML 文件路径
    for (var file in files) {
      if (file is File && file.path.toLowerCase().endsWith('.html')) {
        String fileName = path.basenameWithoutExtension(file.path);
        if (int.parse(fileName.split('-').first) == findIndex){
          print('HTML File--------------------------------${file.path}');
          toReadHtmlPath = file.path;
          chapterId = fileName.split('-').last;
          chapterName = getChapterName(chapterId);

          break;
        }
      }
    }


    // 开始读书
    read(toReadHtmlPath);

    update();

  }

  // 读取html内容
  Future<String?> readHtmlFileContent(String filePath) async {
    try {
      File htmlFile = File(filePath);
      String fileContent = await htmlFile.readAsString();
      return fileContent;
    }
    catch (e){
      Console.log('Error reading file: $e');
      return '';
    }

  }

  // 通过 chapter_id 获取 chapter_name
  String getChapterName(String chapterId){
    for (ChapterModel model in chapters){
      if ('${model.id}' == chapterId){
        return model.name??'';
      }
      if (model.children !=null){
        for (ChapterModel subModel in model.children!){
            if ('${subModel.id}' == chapterId){
              return subModel.name??'';
            }
        }
      }

    }
    return '';
  }

  // 查询本地划线高亮笔记
  void queryLocalNote() async {
    final result = await SqlManager.queryLocalNote(bookId: int.parse(bookId), chapterId: int.parse(chapterId));
    Console.log('前端-----------queryLocalNote---------------------$result');
    webViewController.evaluateJavascript(source: 'querySuccessCallBack($result)');
  }
  // 本地添加划线高亮笔记
  void addLocalNote(Map<String, dynamic> data) async {
    data['book_id'] = int.parse(bookId);
    data['chapter_id'] = int.parse(chapterId);
    final result = await SqlManager.addLocalNote(data);
    Console.log('前端-----------addLocalNote---------------------$result');
    webViewController.evaluateJavascript(source: 'addSuccessCallBack($result)');
  }
  // 本地删除划线高亮笔记
  void delLocalNote({required int id}) async {
    final result = await SqlManager.delLocalNote(id: id);
    Console.log('前端-----------delLocalNote---------------------$result');
    webViewController.evaluateJavascript(source: 'delSuccessCallBack($result)');
  }
  // 修改本地划线高亮笔记
  void updateLocalNote({required int id,required Map<String, dynamic> data}) async {
    final result  = await SqlManager.updateLocalNote(id: id, data: data);
    Console.log('前端-----------updateLocalNote---------------------$result');
    webViewController.evaluateJavascript(source: 'updateSuccessCallBack($result)');
  }

  //获取存储目录
  Future<String> _getDirectory() async {
    // getTemporaryDirectory
    final directory = await getExternalStorageDirectory();
    return directory!.path;
  }

  /// 刷新token
  Future<String?> refreshToken() async {
    final result = await CommonAPI.refreshToken();
    return result;
  }
  /// 获取目录信息
  void _getChapters() async {
    chapters = await LibraryAPI.chapters(bookId: bookId);
    writeCurrentReadChapterIdToData(chapters);
    update();
  }

  void writeCurrentReadChapterIdToData(List<ChapterModel> data) {
    for(ChapterModel cModel in data){
      cModel.currentRead = false;
      if(cModel.id == int.parse(chapterId)){
        cModel.currentRead = true;
        cModel.selected = true;
      }
      else{
        writeCurrentReadChapterIdToData(cModel.children!);
      }
    }

    ChapterModel? tModel = findChapterById(chapters, int.parse(chapterId));
    if(tModel != null){
      updateParentsStatus(chapters, tModel!);
    }

  }

  ChapterModel? findChapterById(List<ChapterModel> data,num id){
    for (ChapterModel cModel in data){
      if(cModel.id == id){
        return cModel;
      }
      ChapterModel? tModel = findChapterById(cModel.children!, id);
      if(tModel !=null){
        return tModel;
      }
    }
    return null;
  }

  void updateParentsStatus(List<ChapterModel> data, ChapterModel model) {
    ChapterModel? parentNode = findParentChapter(data, model);
    while (parentNode != null) {
      parentNode.selected = true;
      parentNode = findParentChapter(data, parentNode);
    }
  }

  ChapterModel? findParentChapter(List<ChapterModel> data, ChapterModel model){
    for (ChapterModel tModel in data){
      if(tModel.id == model.pid){
        return tModel;
      }
      ChapterModel? cModel = findParentChapter(tModel.children!, model);
      if(cModel != null) {
        return cModel;
      }
    }
    return null;
  }

  /// 添加阅读时长
  void _addReadTime({required type}) async{
    final result = await LibraryAPI.addReadTime(bookId: bookId, readTypes: type);
  }
  /// 获取离线文件路径
  void getBookDown() async{
    final exit = await _isExistFile(bookId);
    // 存在离线文件
    if (exit){

    }
    else{
      CustomToast.loading();
      final result = await LibraryAPI.getbookDownloadParam(bookId: bookId);
      Console.log('----------_getBookDown------------------${result.download}');

      // final String savePath = await _getDirectory();
      // LibraryAPI.downBookByUrl(url: result.download!,savePath: '$savePath$bookId.zip',onReceiveProgress: (int received,int total){
      //   if (total !=-1){
      //     double progress = (received / total) * 100;
      //     Console.log('Download progress: $progress%');
      //   }
      // });
      extractZipFileFromCache(result.download!);
    }

  }

  /// 搜全文
  Future<void> searchAll([bool isRefresh = false]) async {
    if (isRefresh) _searchPage = 1;
    // 网路请求
    final result = await LibraryAPI.searchAll(
        page: _searchPage,
        limit: _searchLimit,
        bookId: bookId.toString(),
        key: searchInput.text
    );
    Console.log('--------------------------------');
    // 如果是刷新 清理数据
    if (isRefresh) searchALlResults.clear();
    searchALlResults.addAll(result);
    _searchPage ++;
    _searchNoMore = result.length < _searchLimit;
    update();

  }

  void onRefreshSearch() async {
    try {
      await searchAll(true);
      refreshController.finishRefresh(IndicatorResult.success);
      refreshController.resetFooter();
    } catch (error) {
      Console.log('--error-----------------------------$error-');
      refreshController.finishRefresh(IndicatorResult.fail);
    }
  }

  void onLoadingSearch() async {
    if (_searchNoMore) {
      refreshController.finishLoad(IndicatorResult.noMore);
      return;
    }
    try {
      await searchAll();
      refreshController.finishLoad();
    } catch (error) {
      refreshController.finishLoad(IndicatorResult.fail);
    }
  }


  ///------------------------------------------ app 生命周期--------------------------------------------------------

  // 当应用程序从后台切换到前台并变为活动状态时调用。这通常在用户从其他应用程序返回到你的应用程序时发生
  void onResumed(){
    Console.log('onResumed');
    webViewController.evaluateJavascript(source: 'activeState("1");');
    // open
    // 上报开始阅读时间
    _addReadTime(type: 'open');
  }
  // 当应用程序失去焦点并进入非活动状态时调用。这可能是因为用户切换到其他应用程序或将应用程序最小化
  void onPaused(){
    // close
    Console.log('onPaused');
    webViewController.evaluateJavascript(source: 'activeState("0");');
    // 上报阅读结束时间
    _addReadTime(type: 'close');
  }
  // 当应用程序失去焦点但仍然可见时调用。通常，在用户切换到另一个应用程序或显示系统对话框时，应用程序可能会处于非活动状态，但仍然是可见的
  void onInactive(){
    Console.log('onInactive');
    webViewController.evaluateJavascript(source: 'activeState("0");');
    // close
    // 上报阅读结束时间
    _addReadTime(type: 'close');
  }
  // 当应用程序被挂起，可能是由于用户关闭应用程序或系统资源不足时调用。在这个状态下，应用程序的代码将不再运行，并且可能被系统终止
  void onDetached(){
    Console.log('onDetached');
    webViewController.evaluateJavascript(source: 'activeState("0");');
    // close
    // 上报阅读结束时间
    _addReadTime(type: 'close');
  }
  ///------------------------------------------ app 生命周期--------------------------------------------------------

}

class ToolModel {
  ToolModel({required this.tag, required this.selected, required this.name,required this.icon,required this.activeIcon});
  int tag;
  String name;
  bool selected;
  String icon;
  String activeIcon;
}

class AudioModel {
  AudioModel({required this.path, required this.duration,required this.currentDuration});
  String path;
  String duration;
  String currentDuration;
}
