content: import 'package:flex_color_scheme/flex_color_scheme.dart';
  import 'package:flutter/material.dart';
  
  import '../controllers/theme_controller.dart';
  
  /// A container class with custom application color schemes used in the
  /// examples 4 and 5.
  ///
  /// It also contains helper functions to explain used colors.
  abstract final class AppColor {
    // Create a custom flex scheme color for a light theme.
    static const FlexSchemeColor _myScheme1Light = FlexSchemeColor(
      primary: Color(0xFF00296B),
      primaryContainer: Color(0xFFA0C2ED),
      // TODO(rydmike): Remove the commented light ref colors.
      // primaryLightRef: Color(0xFF00296B),
      secondary: Color(0xFFD26900),
      secondaryContainer: Color(0xFFFFD270),
      // secondaryLightRef: Color(0xFFD26900),
      tertiary: Color(0xFF5C5C95),
      tertiaryContainer: Color(0xFFC8DBF8),
      // tertiaryLightRef: Color(0xFF5C5C95),
      // The built in schemes use their tertiary color as their default
      // custom app bar color, but it can be any color. We use a custom color
      // here. We will see this in example 5 when using the theme and selecting
      // the custom app bar style. We use a light blue that matches branded
      // surface colors, but is a bit stronger than most surface branding.
      appBarColor: Color(0xFFC8DCF8),
      swapOnMaterial3: true,
    );
  // Create a corresponding custom flex scheme color for a dark theme.
    static const FlexSchemeColor _myScheme1Dark = FlexSchemeColor(
      primary: Color(0xFFB1CFF5),
      primaryContainer: Color(0xFF3873BA),
      primaryLightRef: Color(0xFF00296B),
      secondary: Color(0xFFFFD270),
      secondaryContainer: Color(0xFFD26900),
      secondaryLightRef: Color(0xFFD26900),
      tertiary: Color(0xFFC9CBFC),
      tertiaryContainer: Color(0xFF535393),
      tertiaryLightRef: Color(0xFF5C5C95),
      // A custom very dark blue, to match the dark theme mode
      appBarColor: Color(0xFF00102B),
      swapOnMaterial3: true,
    );
  
    // We can build a scheme the long way, by specifying all the required
    // scheme colors, like above, or we can also build schemes from a single
    // primary color. With the `FlexSchemeColor.from` factory. When doing so the
    // only required color is the primary color, the other colors will be
    // computed. We can optionally also provide the `primaryContainer`,
    // `secondary` and `secondaryContainer` colors with the factory, but any
    // color that is not provided will always be computed to get all the required
    // colors in a `FlexSchemeColor`.
  
    // In this example we create our 2nd scheme from just a primary color for the
    // light and dark schemes. The custom `appBarColor` does in this case also
    // receive the same color value as the one that is computed for
    // `secondaryContainer` color. This is its default with the
    // `FlexSchemeColor.from` factory if the color is not specified.
  
    // When you figure out the actual colors you want, it might be a good idea to
    // use the method in the above case for the custom _myScheme1Light and dark,
    // since that can be const and you can then make your entire list
    // of color schemes a const.
  
    // The `brightness` parameter in the `FlexSchemeColor.from` factory is new
    // in version 5. If `brightness` is specified the factory computes missing
    // colors that are well suited for a Material 3 based ColorScheme, of the
    // given brightness. If brightness is not specified, then the algorithm is
    // same as before version 5, but with two more colors. Its produced colors
    // fit better for M2 specification, they do also work in M3, they just don't
    // follow the guidelines with respect to the hue of the main color and its
    // container pair color. When using the `FlexSchemeColor.from` with
    // Material 3 `ColorScheme`, prefer specifying the brightness value for
    // results following the Material 3 design intent.
    static final FlexSchemeColor _myScheme2Light = FlexSchemeColor.from(
      primary: const Color(0xFF065808),
      brightness: Brightness.light,
    );
    static final FlexSchemeColor _myScheme2Dark = FlexSchemeColor.from(
      primary: const Color(0xFF629F80),
      primaryLightRef: _myScheme2Light.primary,
      secondaryLightRef: _myScheme2Light.secondary,
      tertiaryLightRef: _myScheme2Light.tertiary,
      brightness: Brightness.dark,
    );
  
    // For our 3rd custom scheme we will define primary and secondary colors, but
    // no container colors, we will not make any dark scheme definitions either.
    // All these missing colors will be computed, the missing color definitions
    // will get computed by the factory `FlexSchemeColor.from` when it creates the
    // `FlexSchemeColor` object. To make our dark colors for this light scheme, we
    // use the method `toDark` further below with the `_myScheme3Light` instance.
    //
    // Same comment as above, when you do figure out the actual colors you want,
    // it might be a good idea to use the method in the above case for the custom
    // _myScheme1Light and dark, since that can be const and you can then make
    // your entire list of color schemes a const.
    static final FlexSchemeColor _myScheme3Light = FlexSchemeColor.from(
      primary: const Color(0xFF1145A4),
      secondary: const Color(0xFFB61D1D),
      brightness: Brightness.light,
      swapOnMaterial3: true,
    );
  
    // These colors are used as default for the customizable colors in
    // example 5, the Themes Playground.
    static const Color customPrimaryLight = Color(0xFF004881);
    static const Color customPrimaryContainerLight = Color(0xFFD0E4FF);
    static const Color customSecondaryLight = Color(0xFFAC3306);
    static const Color customSecondaryContainerLight = Color(0xFFFFDBCF);
    static const Color customTertiaryLight = Color(0xFF006875);
    static const Color customTertiaryContainerLight = Color(0xFF95F0FF);
  
    static const Color customPrimaryDark = Color(0xFF9FC9FF);
    static const Color customPrimaryContainerDark = Color(0xFF00325B);
    static const Color customSecondaryDark = Color(0xFFFFB59D);
    static const Color customSecondaryContainerDark = Color(0xFF872100);
    static const Color customTertiaryDark = Color(0xFF86D2E1);
    static const Color customTertiaryContainerDark = Color(0xFF004E59);
  
    // Used by example 4, the "All themes" and example 5, the Themes Playground.
    //
    // Finally we create a list with all color schemes we will use. Starting with
    // our custom schemes, since normally when we make custom schemes, those are
    // probably the ones we want to use primarily, so we put them first. After our
    // custom schemes, we add all the pre-defined built-in ones, offering them as
    // options users can switch to and use if they like.
    static final List<FlexSchemeData> customSchemes = <FlexSchemeData>[
      // We add our custom light and dark FlexSchemeColor schemes we defined
      // to a list of FlexSchemeData, where we can bundle each light and dark
      // theme that goes together and give it a name and description too.
      const FlexSchemeData(
        name: 'Example Midnight',
        description: 'Midnight blue theme, created as an in code example by '
            'using custom color values for all colors in the scheme',
        // FlexSchemeData holds separate defined color schemes for light and
        // matching dark theme colors. Dark theme colors typically need to be less
        // saturated versions of their than light counter parts. Using the same
        // colors in light and dark theme modes does not work so well.
        light: _myScheme1Light,
        dark: _myScheme1Dark,
      ),
      // Do the same for our second custom scheme.
      FlexSchemeData(
        name: 'Example Greens',
        description: 'Vivid green theme, created as an in code example from one '
            'primary color in light mode and another primary for dark mode',
        light: _myScheme2Light,
        dark: _myScheme2Dark,
      ),
      // We also do the same for our 3rd custom scheme, BUT we create its matching
      // dark colors, from the light FlexSchemeColor with the toDark method.
      FlexSchemeData(
        name: 'Example Red & Blue',
        description: 'Classic red and blue, created as an in code example from '
            'only light theme mode primary and secondary colors',
        light: _myScheme3Light,
        // We create the dark desaturated colors from the light scheme.
        // The `swapColors` parameter is `true` here. It is new in version 5. It
        // swaps main and container colors values for the primary its container,
        // likewise for secondary and tertiary and their containers.
        //
        // This is done because in Material 3 light mode, the main color should
        // be darker or more saturated than the container, but in dark mode it
        // should be the other way around. By setting the flag to true, this is
        // done before the light theme mode colors are reused as dark theme and
        // desaturated using `whiteBlend` level value.
        dark: _myScheme3Light.defaultError.toDark(30, true),
      ),
      // Use all the built-in FlexColor schemes. This list is a const.
      ...FlexColor.schemesList,
    ];
  
    /// Used by example 5, the Themes Playground.
    /// We add all schemes from example 4 and a placeholder for the custom data.
    ///
    /// We could also make this a static getter that requires the ThemeController,
    /// so we can use it to return a list where the last item is the
    /// FlexSchemeData it defines (controller.customScheme). I tried it, and it
    /// felt sluggish and heavy. It results in creating a new list very
    /// frequently, which was a bit too inefficient.
    ///
    /// Instead we are making it a final and will add a const placeholder for the
    /// last index for the customized colors and use another static helper
    /// function to get right colors see next step.
    static final List<FlexSchemeData> schemes = <FlexSchemeData>[
      // Use all the built-in FlexColor schemes we setup for example 4
      ...customSchemes,
      // and a placeholder for the custom colors, here overriding its name too.
      FlexColor.customColors.copyWith(name: 'Customizable'),
    ];
  
    /// Return current FlexSchemeData based on the Theme controllers schemeIndex.
    ///
    /// Used by example 5, the Themes Playground.
    ///
    /// Instead of getting the colors directly from a list that changes frequently
    /// we make a separate helper function that takes a ThemeController and
    /// returns the right FlexSchemeData from the list, but returning the
    /// `ThemeController.customScheme` for the last item, when the controllers
    /// index is last item in list, otherwise it returns FlexSchemeData at index,
    /// well almost, we include also the logic for computed dark mode colors and
    /// dark mode seed color behavior, in the schemeAtIndex helper it uses to
    /// get the scheme at an index.
    static FlexSchemeData scheme(final ThemeController controller) =>
        schemeAtIndex(controller.schemeIndex, controller);
  
    /// Get the FlexSchemeData at current scheme index.
    ///
    /// Used by example 5, the Themes Playground.
    ///
    /// Helper function to return current FlexSchemeData at given index, also
    /// needed this functionality in theme selector and popup menu, so made
    /// it a sub function to getting the FlexSchemeData at its current index.
    /// We go a bit further and have it help us return the computed dark scheme
    /// when controller says we are using that as well, instead of the defined
    /// dark mode scheme. This simplifies our logic in the MaterialApp
    /// of example 5 and we get right dark colors in ThemeSelector and Popup too.
    static FlexSchemeData schemeAtIndex(
        final int index, final ThemeController controller) {
      if (index == schemes.length - 1) {
        return controller.customScheme.copyWith(
            dark: controller.useKeyColors
                ? controller.customScheme.dark
                : controller.useToDarkMethod
                    ? controller.customScheme.light.defaultError.toDark(
                        controller.toDarkMethodLevel,
                        controller.toDarkSwapPrimaryAndContainer)
                    : null);
      }
      return schemes[index].copyWith(
          dark: controller.useToDarkMethod
              ? schemes[index].light.defaultError.toDark(
                  controller.toDarkMethodLevel,
                  controller.toDarkSwapPrimaryAndContainer)
              : null);
    }
  
    /// Used by example 5, the Themes Playground.
    /// Used to explain the current selection of key colors used to
    /// generate the ColorScheme, when it is activated.
    ///
    /// Normally this is local to the widget where it is used, but I needed
    /// this explanation in different places so I tucked it in here.
    /// It could be placed anywhere as simple top level function too.
    static String explainUsedColors(ThemeController controller) {
      final String errorExplanation = controller.useError
          ? '. Additionally a given error color is used to seed the error palette'
          : '';
      if (!controller.useKeyColors) {
        return 'Material 3 ColorScheme seeding from key colors is OFF and not '
            'used. The effective ColorScheme is based directly on the selected '
            'pre-defined FlexColorScheme colors';
      }
      if (!controller.useSecondary && !controller.useTertiary) {
        return 'Light scheme defined Primary color is used to generate '
            'the Colorscheme. '
            "This is like using Flutter's ColorScheme.fromSeed with the scheme "
            'defined Primary color as seed color$errorExplanation';
      }
      if (controller.useSecondary && !controller.useTertiary) {
        return 'Tonal palettes for the ColorScheme are made with light scheme '
            'defined Primary and Secondary colors as seed keys. Tertiary key '
            'is computed from Primary color$errorExplanation';
      }
      if (!controller.useSecondary && controller.useTertiary) {
        return 'Tonal palettes for the ColorScheme, are made with light scheme '
            'defined Primary and Tertiary colors as seed keys. Secondary key is '
            'computed from Primary color$errorExplanation';
      }
      return 'Light scheme defined Primary, Secondary and Tertiary colors are '
          'used as key colors to generate tonal palettes that define '
          'the ColorScheme$errorExplanation';
    }
  }
  