content: import 'dart:async';
  import 'dart:io';
  
  import 'package:flutter/foundation.dart';
  import 'package:flutter/material.dart';
  import 'package:logging/logging.dart';
  import 'package:perfect_freehand/perfect_freehand.dart';
  import 'package:saber/components/canvas/_canvas_background_painter.dart';
  import 'package:saber/components/navbar/responsive_navbar.dart';
  import 'package:saber/components/settings/nextcloud_profile.dart';
  import 'package:saber/data/codecs/base64_codec.dart';
  import 'package:saber/data/codecs/quota_codec.dart';
  import 'package:saber/data/editor/pencil_sound.dart';
  import 'package:saber/data/flavor_config.dart';
  import 'package:saber/data/sentry/sentry_consent.dart';
  import 'package:saber/data/tools/_tool.dart';
  import 'package:saber/data/tools/highlighter.dart';
  import 'package:saber/data/tools/pen.dart';
  import 'package:stow/stow.dart';
  import 'package:stow_codecs/stow_codecs.dart';
  import 'package:stow_plain/stow_plain.dart';
  import 'package:stow_secure/stow_secure.dart';
  
  /// If false, all stows are stuck at their default values.
  bool _isOnMainIsolate = false;
  
  final stows = Stows();
  
  class Stows {
    Stows() {
      recentColorsLength.addListener(() {
        // truncate if needed
        while (recentColorsLength.value < recentColorsPositioned.value.length) {
          // remove oldest color
          final removed = recentColorsChronological.value.removeAt(0);
          recentColorsPositioned.value.remove(removed);
        }
      });
    }
  
    /// Call this before [runApp] to set [_isOnMainIsolate] to true.
    static void markAsOnMainIsolate() {
      _isOnMainIsolate = true;
    }
  
    final log = Logger('Stows');
  
    final customDataDir =
        PlainStow<String?>('customDataDir', null, volatile: !_isOnMainIsolate);
  
    final allowInsecureConnections = SecureStow.bool(
        'allowInsecureConnections', false,
        volatile: !_isOnMainIsolate);
    final url = SecureStow('url', '', volatile: !_isOnMainIsolate);
    final username = SecureStow('username', '', volatile: !_isOnMainIsolate);
  
    /// the password used to login to Nextcloud
    final ncPassword = SecureStow('ncPassword', '', volatile: !_isOnMainIsolate);
  
    /// the password used to encrypt/decrypt notes
    final encPassword =
        SecureStow('encPassword', '', volatile: !_isOnMainIsolate);
  
    /// Whether the user is logged in and has provided both passwords.
    /// Please ensure that the relevant Prefs are loaded before using this.
    bool get loggedIn =>
        username.value.isNotEmpty &&
        ncPassword.value.isNotEmpty &&
        encPassword.value.isNotEmpty;
  
    final key = SecureStow('key', '', volatile: !_isOnMainIsolate);
    final iv = SecureStow('iv', '', volatile: !_isOnMainIsolate);
  
    final pfp = PlainStow<Uint8List?>('pfp', null,
        codec: const Base64StowCodec(), volatile: !_isOnMainIsolate);
    final syncInBackground =
        PlainStow('syncInBackground', true, volatile: !_isOnMainIsolate);
  
    final appTheme = PlainStow('appTheme', ThemeMode.system,
        codec: const EnumCodec(ThemeMode.values), volatile: !_isOnMainIsolate);
  
    /// The type of platform to theme. Default value is [defaultTargetPlatform].
    final platform = PlainStow('platform', defaultTargetPlatform,
        codec: const EnumCodec(TargetPlatform.values), volatile: !_isOnMainIsolate);
    final layoutSize = PlainStow('layoutSize', LayoutSize.auto,
        codec: LayoutSize.codec, volatile: !_isOnMainIsolate);
  
    /// The accent color of the app. If 0, the system accent color will be used.
    final accentColor = PlainStow<Color?>('accentColor', null,
        codec: const ColorCodec(), volatile: !_isOnMainIsolate);
    final hyperlegibleFont =
        PlainStow('hyperlegibleFont', false, volatile: !_isOnMainIsolate);
  
    final editorToolbarAlignment = PlainStow(
        'editorToolbarAlignment', AxisDirection.down,
        codec: const EnumCodec(AxisDirection.values), volatile: !_isOnMainIsolate);
    final editorToolbarShowInFullscreen = PlainStow(
        'editorToolbarShowInFullscreen', true,
        volatile: !_isOnMainIsolate);
    final editorFingerDrawing =
        PlainStow('editorFingerDrawing', true, volatile: !_isOnMainIsolate);
    final editorAutoInvert =
        PlainStow('editorAutoInvert', true, volatile: !_isOnMainIsolate);
    final preferGreyscale =
        PlainStow('preferGreyscale', false, volatile: !_isOnMainIsolate);
    final editorPromptRename =
        PlainStow('editorPromptRename', isDesktop, volatile: !_isOnMainIsolate);
    final autosaveDelay =
        PlainStow('autosaveDelay', 10000, volatile: !_isOnMainIsolate);
    final shapeRecognitionDelay =
        PlainStow('shapeRecognitionDelay', 500, volatile: !_isOnMainIsolate);
    final autoStraightenLines =
        PlainStow('autoStraightenLines', true, volatile: !_isOnMainIsolate);
    final pencilSound = PlainStow(
        'pencilSound', PencilSoundSetting.onButNotInSilentMode,
        codec: PencilSoundSetting.codec, volatile: !_isOnMainIsolate);
  
    final simplifiedHomeLayout =
        PlainStow('simplifiedHomeLayout', false, volatile: !_isOnMainIsolate);
    final hideHomeBackgrounds =
        PlainStow('hideHomeBackgrounds', false, volatile: !_isOnMainIsolate);
    final printPageIndicators =
        PlainStow('printPageIndicators', false, volatile: !_isOnMainIsolate);
  
    final maxImageSize =
        PlainStow<double>('maxImageSize', 1000, volatile: !_isOnMainIsolate);
  
    final autoClearWhiteboardOnExit = PlainStow(
        'autoClearWhiteboardOnExit', false,
        volatile: !_isOnMainIsolate);
  
    final disableEraserAfterUse =
        PlainStow('disableEraserAfterUse', false, volatile: !_isOnMainIsolate);
    final hideFingerDrawingToggle =
        PlainStow('hideFingerDrawingToggle', false, volatile: !_isOnMainIsolate);
  
    final recentColorsChronological = PlainStow(
        'recentColorsChronological', <String>[],
        volatile: !_isOnMainIsolate);
    final recentColorsPositioned = PlainStow('recentColorsPositioned', <String>[],
        volatile: !_isOnMainIsolate);
    final pinnedColors =
        PlainStow('pinnedColors', <String>[], volatile: !_isOnMainIsolate);
    final recentColorsDontSavePresets =
        PlainStow('dontSavePresetColors', false, volatile: !_isOnMainIsolate);
    final recentColorsLength =
        PlainStow('recentColorsLength', 5, volatile: !_isOnMainIsolate);
  
    final lastTool = PlainStow('lastTool', ToolId.fountainPen,
        codec: ToolId.codec, volatile: !_isOnMainIsolate);
    static StrokeOptions _strokeOptionsFromJson(Object json) =>
        StrokeOptions.fromJson(json as Map<String, dynamic>);
    final lastFountainPenOptions = PlainStow.json(
            'lastFountainPenProperties', Pen.fountainPenOptions,
            fromJson: _strokeOptionsFromJson, volatile: !_isOnMainIsolate),
        lastBallpointPenOptions = PlainStow.json(
            'lastBallpointPenProperties', Pen.ballpointPenOptions,
            fromJson: _strokeOptionsFromJson, volatile: !_isOnMainIsolate),
        lastHighlighterOptions = PlainStow.json(
            'lastHighlighterProperties', Pen.highlighterOptions,
            fromJson: _strokeOptionsFromJson, volatile: !_isOnMainIsolate),
        lastPencilOptions = PlainStow.json(
            'lastPencilProperties', Pen.pencilOptions,
            fromJson: _strokeOptionsFromJson, volatile: !_isOnMainIsolate),
        lastShapePenOptions = PlainStow.json(
            'lastShapePenProperties', Pen.shapePenOptions,
            fromJson: _strokeOptionsFromJson, volatile: !_isOnMainIsolate);
    final lastFountainPenColor = PlainStow(
            'lastFountainPenColor', Colors.black.toARGB32(),
            volatile: !_isOnMainIsolate),
        lastBallpointPenColor = PlainStow(
            'lastBallpointPenColor', Colors.black.toARGB32(),
            volatile: !_isOnMainIsolate),
        lastHighlighterColor = PlainStow('lastHighlighterColor',
            Colors.yellow.withAlpha(Highlighter.alpha).toARGB32(),
            volatile: !_isOnMainIsolate),
        lastPencilColor = PlainStow('lastPencilColor', Colors.black.toARGB32(),
            volatile: !_isOnMainIsolate),
        lastShapePenColor = PlainStow(
            'lastShapePenColor', Colors.black.toARGB32(),
            volatile: !_isOnMainIsolate);
    final lastBackgroundPattern = PlainStow(
        'lastBackgroundPattern', CanvasBackgroundPattern.none,
        codec: CanvasBackgroundPattern.codec, volatile: !_isOnMainIsolate);
    static const defaultLineHeight = 40;
    static const defaultLineThickness = 3;
    final lastLineHeight = PlainStow('lastLineHeight', defaultLineHeight,
        volatile: !_isOnMainIsolate);
    final lastLineThickness = PlainStow('lastLineThickness', defaultLineThickness,
        volatile: !_isOnMainIsolate);
    final lastZoomLock =
            PlainStow('lastZoomLock', false, volatile: !_isOnMainIsolate),
        lastSingleFingerPanLock = PlainStow('lastSingleFingerPanLock', false,
            volatile: !_isOnMainIsolate),
        lastAxisAlignedPanLock = PlainStow('lastAxisAlignedPanLock', false,
            volatile: !_isOnMainIsolate);
  
    final recentFiles =
        PlainStow('recentFiles', <String>[], volatile: !_isOnMainIsolate);
  
    /// File paths that have been deleted locally
    final fileSyncAlreadyDeleted = PlainStow('fileSyncAlreadyDeleted', <String>{},
        volatile: !_isOnMainIsolate);
  
    /// File paths that are known to be corrupted on Nextcloud
    final fileSyncCorruptFiles = PlainStow('fileSyncCorruptFiles', <String>{},
        volatile: !_isOnMainIsolate);
  
    /// Set when we want to resync everything.
    /// Files on the server older than this date will be
    /// reuploaded with the local version.
    /// By default, we resync everything uploaded before v0.18.4, since uploads before then resulted in 0B files.
    final fileSyncResyncEverythingDate = PlainStow('fileSyncResyncEverythingDate',
        DateTime.parse('2023-12-10T10:06:31.000Z'),
        codec: const DateTimeCodec(), volatile: !_isOnMainIsolate);
  
    /// The last storage quota that was fetched from Nextcloud
    final lastStorageQuota = PlainStow<Quota?>('lastStorageQuota', null,
        codec: const QuotaCodec(), volatile: !_isOnMainIsolate);
  
    final shouldCheckForUpdates = PlainStow('shouldCheckForUpdates',
        FlavorConfig.shouldCheckForUpdatesByDefault && !Platform.isLinux,
        volatile: !_isOnMainIsolate);
    final shouldAlwaysAlertForUpdates = PlainStow('shouldAlwaysAlertForUpdates',
        (kDebugMode || FlavorConfig.dirty) ? true : false,
        volatile: !_isOnMainIsolate);
  
    final locale = PlainStow('locale', '', volatile: !_isOnMainIsolate);
  
    final sentryConsent = PlainStow('sentryConsent', SentryConsent.unknown,
        codec: SentryConsent.codec, volatile: !_isOnMainIsolate);
  
    static bool get isDesktop =>
        Platform.isLinux || Platform.isWindows || Platform.isMacOS;
  }
  
  /// An [Stow] that transforms the value of another [Stow].
  class TransformedStow<T_in, T_out> extends Stow<dynamic, T_out, dynamic> {
    final Stow<dynamic, T_in, dynamic> parent;
    final T_out Function(T_in) transform;
    final T_in Function(T_out) reverseTransform;
  
    @override
    T_out get value => transform(parent.value);
  
    @override
    set value(T_out value) => parent.value = reverseTransform(value);
  
    TransformedStow(this.parent, this.transform, this.reverseTransform)
        : super(parent.key, transform(parent.defaultValue), volatile: true) {
      parent.addListener(notifyListeners);
    }
  
    @override
    Future<dynamic> protectedRead() async => null;
  
    @override
    Future<void> protectedWrite(dynamic value) async {}
  
    @override
    String toString() {
      return 'TransformedPref<$T_in, $T_out>(from ${parent.key}, $value)';
    }
  
    @override
    void dispose() {
      parent.removeListener(notifyListeners);
      super.dispose();
    }
  }
  