downloadAllFontsZipFile method

Future<void> downloadAllFontsZipFile(
  1. int fontIndex
)

Downloads a zip file containing all fonts for the specified font index.

This method asynchronously downloads a zip file that contains all the fonts associated with the given fontIndex. The downloaded file will be saved locally for later use.

fontIndex - The index of the font set to be downloaded.

Returns a Future that completes when the download is finished.

Implementation

Future<void> downloadAllFontsZipFile(int fontIndex) async {
  // if (GetStorage().read(StorageConstants().isDownloadedCodeV2Fonts) ??
  //     false || state.isDownloadingFonts.value) {
  //   return Future.value();
  // }

  // فحص الاتصال بالإنترنت أولاً (خاص بـ macOS)
  // Check internet connectivity first (specific for macOS)
  if (Platform.isMacOS) {
    final hasConnection = await _checkNetworkConnectivity();
    if (!hasConnection) {
      _showMacOSNetworkTroubleshooting();
      return;
    }
  }

  try {
    state.isPreparingDownload.value = true;
    state.isDownloadingFonts.value = true;
    update(['fontsDownloadingProgress']);

    // قائمة بالروابط البديلة للتحميل
    // List of alternative download URLs
    final urls = [
      'https://github.com/alheekmahlib/Islamic_database/raw/refs/heads/main/quran_database/Quran%20Font/quran_fonts.zip',
      'https://rawgit.flutter-io.cn/alheekmahlib/Islamic_database/main/quran_database/Quran%20Font/quran_fonts.zip',
    ];
    // تحميل الملف باستخدام Dio

    // تحميل الملف باستخدام http.Client مع إعدادات محسنة للماك
    // Download file using http.Client with improved settings for macOS
    // final response = dio.get(
    //   'https://github.com/alheekmahlib/Islamic_database/raw/refs/heads/main/quran_database/Quran%20Font/quran_fonts.zip',
    //   options: Options(responseType: ResponseType.stream),
    // );
    final dio = Dio();
    String? successUrl;
    late Response response;

    // جرب كل رابط حتى ينجح واحد منها
    // Try each URL until one succeeds
    for (String url in urls) {
      try {
        log('Attempting to download from: $url', name: 'FontsDownload');

        response = await dio.get(url,
            options: Options(
              responseType: ResponseType.stream,
              sendTimeout: Duration(seconds: 30),
              headers: {
                'User-Agent': 'Flutter/Quran-Library',
                'Accept': '*/*',
                'Connection': 'keep-alive',
                'Accept-Encoding': 'identity',
              },
            ));

// التحقق من نجاح الاتصال بأحد الروابط
        // Check if connection to any URL succeeded
        if (response.statusCode != 200) {
          log('Failed to connect to $url: ${response.statusCode}',
              name: 'FontsDownload');
          break;
        }

        log('Download started successfully from: $successUrl',
            name: 'FontsDownload');
        if (response.statusCode == 200) {
          successUrl = url;
          log('Successfully connected to: $url', name: 'FontsDownload');
          break;
        }
      } catch (e) {
        log('Failed to connect to $url: $e', name: 'FontsDownload');
        continue;
      }
    }

    // تحديد المسار الذي سيتم حفظ الملف فيه
    final fontsDir = Directory('${_dir.path}/quran_fonts');
    if (!await fontsDir.exists()) {
      await fontsDir.create(recursive: true);
    }

    // حفظ ملف ZIP إلى التطبيق
    final zipFile = File('${_dir.path}/quran_fonts.zip');
    final fileSink = zipFile.openWrite();

    // حجم الملف الإجمالي
    final contentLength = int.tryParse(
            response.headers.value(Headers.contentLengthHeader) ?? '0') ??
        0;
    int totalBytesDownloaded = 0;

    // متابعة التدفق وكتابة البيانات في الملف مع حساب نسبة التحميل
    (response.data as ResponseBody).stream.listen(
      (List<int> chunk) {
        totalBytesDownloaded += chunk.length;
        fileSink.add(chunk);
        state.isDownloadingFonts.value = true;
        state.isPreparingDownload.value = false;
        // حساب نسبة التحميل
        if (contentLength > 0) {
          double progress = totalBytesDownloaded / contentLength * 100;
          state.fontsDownloadProgress.value = progress;
          log('Download progress: ${progress.toStringAsFixed(2)}%',
              name: 'FontsDownload');
          update(['fontsDownloadingProgress']);
        }
      },
      onDone: () async {
        await fileSink.flush();
        await fileSink.close();

        // فك ضغط الـ ZIP بعد إغلاق الملف بنجاح
        try {
          // التحقق من أن الملف تم تنزيله بالكامل
          final zipFileSize = await zipFile.length();
          log('Downloaded ZIP file size: $zipFileSize bytes');

          if (zipFileSize == 0) {
            throw Exception('Downloaded ZIP file is empty');
          }

          // فك ضغط الـ ZIP
          final bytes = zipFile.readAsBytesSync();
          final archive = ZipDecoder().decodeBytes(bytes);

          if (archive.isEmpty) {
            throw FormatException(
                'Failed to extract ZIP file: Archive is empty');
          }

          // استخراج الملفات إلى مجلد الخطوط
          for (final file in archive) {
            final filename = '${fontsDir.path}/${file.name}';
            if (file.isFile) {
              final outFile = File(filename);
              await outFile.create(recursive: true);
              await outFile.writeAsBytes(file.content as List<int>);
              // log('Extracted file: $filename'); // سجل لاستخراج الملف
            } else {
              log('Skipped directory: ${file.name}');
            }
          }

          // تحقق من وجود الملفات في المجلد
          final files = await fontsDir.list().toList();
          if (files.isEmpty) {
            log('No files found in fontsDir after extraction');
          } else {
            log('Files in fontsDir after extraction: ${files.map((file) => file.path).join(', ')}');
          }
          await QuranCtrl.instance.loadFontsQuran();
          // حفظ حالة التحميل في التخزين المحلي
          // Save download status in local storage
          GetStorage()
              .write(_StorageConstants().isDownloadedCodeV2Fonts, true);
          state.fontsDownloadedList.add(fontIndex);
          GetStorage().write(_StorageConstants().fontsDownloadedList,
              state.fontsDownloadedList);

          // تحديث حالة التحميل وإكمال شريط التقدم
          // Update download status and complete progress bar
          state.isDownloadedV2Fonts.value = true;
          state.isDownloadingFonts.value = false;
          state.isPreparingDownload.value = false;
          state.fontsDownloadProgress.value = 100.0;

          update(['fontsDownloadingProgress']);
          Get.forceAppUpdate();

          log('Fonts unzipped successfully', name: 'FontsDownload');
          // Get.back();
        } catch (e) {
          log('Failed to extract ZIP file: $e');
        }
      },
      onError: (error) {
        log('Error during download: $error');
        dio.close();
      },
      cancelOnError: true,
    );
  } catch (e) {
    log('Failed to Download Code_v2 fonts: $e', name: 'FontsDownload');

    // معالجة خاصة لأخطاء macOS
    // Special handling for macOS errors
    if (Platform.isMacOS &&
        e.toString().contains('Operation not permitted')) {
      _showMacOSNetworkTroubleshooting();
    }

    // تحديث حالة التحميل في حالة فشل العملية
    // Update download status if operation fails
    state.isDownloadingFonts.value = false;
    state.isPreparingDownload.value = false;
    state.fontsDownloadProgress.value = 0.0;
    update(['fontsDownloadingProgress']);

    // رمي استثناء ليتم التعامل معه في الدالة الأم
    // Throw exception to be handled in parent function
    throw Exception('Download failed: $e');
  }
}