package dev.cf.recipe;

import dev.crossfly.ui.*;
import dev.crossfly.ui.color.ColorScheme;
import dev.crossfly.ui.graphics.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;

import static dev.crossfly.ui.Widgets.*;

/**
 * Declaring a brand: one seed, and everything else follows.
 *
 * <p>You do not pick colours in a CrossFly app. You declare one seed, and the framework derives the
 * rest — primary and what reads on it, surfaces, containers, outlines, the tonal fill behind an
 * active tab, both themes. Tap the swatches below: nothing on this screen is rebuilt, and every
 * colour moves at once, because none of them was ever written down.</p>
 *
 * <p><b>Skip this and your app is not "unstyled", it is grey</b> — the skin's neutral palette is
 * what you get when nobody declared anything, and it reads as a rendering bug rather than as a
 * default. That is the first thing to check when a screen looks broken and nothing is broken.</p>
 *
 * <p>Two calls, and the difference between them matters exactly once. {@code setBrand} restyles
 * every open window; {@code setBrandSilent} does not, and exists for code running before there is
 * anything to restyle — class initialisation, the line before the first window is shown.</p>
 */
public class R29Brand extends Recipe
{
   /** A few seeds. Any ARGB works: the palette is generated, not looked up. */
   private static final int[] SEED = { 0xFF1565C0, 0xFFE2622C, 0xFF2E7D32, 0xFF6A1B9A,
      0xFFB71C1C };
   private static final String[] SEED_NAME = { "Blue", "Orange", "Green", "Purple", "Red" };

   /** What was in force when this recipe opened, so it can be handed back. */
   private Integer previousBrand;
   private ColorScheme previousScheme;

   public String title()
   {
      return "Brand and dark mode";
   }

   public String note()
   {
      return "One seed generates the whole palette, for both themes. Nothing below names a colour,"
         + " which is why the switch needs no rebuild.";
   }

   public void build(Component body) throws Exception
   {
      remember();

      body.add(LAY_AUTO, outgap(label("THE SEED", 1f, true, Styles.captionColor, Position.START), 0,
         0, 0, .5f));

      Component swatches = new Component();
      swatches.setLayout(new HorizontalLayout(Position.START, Position.CENTER));
      for (int i = 0; i < SEED.length; i++)
      {
         final int seed = SEED[i];
         Button b = new Button(SEED_NAME[i]);
         b.getStyle()
            .backColor(seed)
            .textColor(0xFFFFFFFF)
            .outgap(Gap.ofFont(0, 0, .4f, 0));
         b.onAction(c -> Styles.setBrand(seed));
         swatches.add(LAY_AUTO, b);
      }
      body.add(LAY_AUTO, outgap(swatches, 0, 0, 0, 1f));

      Switch dark = new Switch("Dark mode");
      dark.checked(Styles.isDarkMode());
      // One call. Every PalColor token re-resolves in place and every window is restyled — no
      // screen tracks a flag of its own, and none rebuilds.
      dark.onAction(c -> Styles.setDarkMode(dark.isChecked()));
      body.add(LAY_AUTO, outgap(dark, 0, 0, 0, 1.2f));

      body.add(LAY_AUTO, outgap(label("WHAT FOLLOWS, WITHOUT BEING TOLD", 1f, true,
         Styles.captionColor, Position.START), 0, 0, 0, .5f));

      Card c = new Card();
      c.add(LAY_AUTO, label("A card on a surface", 1.2f, true, Styles.textColor, Position.START));
      c.add(LAY_AUTO, outgap(label("Caption text, the tone below the title.", 1f, false,
         Styles.captionColor, Position.START), 0, .2f, 0, .5f));
      c.add(LAY_AUTO, outgap(new TextField().caption("A field"), 0, 0, 0, .5f));
      c.add(LAY_AUTO, outgap(new Button("A button"), 0, 0, 0, .5f));
      Component chips = new Component();
      chips.setLayout(new HorizontalLayout(Position.START, Position.CENTER));
      chips.add(LAY_AUTO, outgap(toneBadge(Styles.primaryColor, "primary"), 0, 0, .4f, 0));
      chips.add(LAY_AUTO, outgap(toneBadge(secondary(), "secondary"), 0, 0, .4f, 0));
      chips.add(LAY_AUTO, toneBadge(tertiary(), "tertiary"));
      c.add(LAY_AUTO, chips);
      body.add(LAY_AUTO, c);
   }

   /**
    * Snapshots the palette, so leaving this recipe does not repaint the app it was opened from.
    *
    * <p>A <b>copy</b>, never the reference: {@link Styles} keeps one {@link ColorScheme} instance
    * for the life of the app and mutates its roles in place on every seed change — deliberately, so
    * that a {@code PalColor} captured into a style at build time keeps resolving. Save the
    * reference and you have saved the object that is about to be overwritten.</p>
    */
   private void remember()
   {
      previousBrand = Styles.hasBrand() ? Integer.valueOf(Styles.brand()) : null;
      ColorScheme live = Styles.colorScheme();
      if (live != null)
      {
         previousScheme = ColorScheme.fromSeed(SEED[0], live.isDark);
         previousScheme.copyFrom(live);
      }
   }

   @Override
   public void onRemove()
   {
      super.onRemove();
      if (previousBrand != null)
         Styles.setBrand(previousBrand.intValue());
      else
      {
         Styles.clearBrand();
         if (previousScheme != null)
            Styles.setColorScheme(previousScheme);
      }
   }
}
