package dev.cf.recipe;

import dev.crossfly.ui.*;
import dev.crossfly.ui.color.ColorUtils;
import dev.crossfly.ui.graphics.*;
import dev.crossfly.ui.image.Image;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;

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

/**
 * Images, and the two questions that come up before the picture does.
 *
 * <p><b>How does it fill its box?</b> {@link ImageView.ImageSize} is the whole answer, and picking
 * wrong is the most visible mistake in an app: a portrait photo stretched into a landscape tile is
 * noticed by everybody and reported by nobody, because it looks like a bad photo rather than a bug.
 * Cropping to fill is almost always right for a photo; fitting inside is almost always right for a
 * logo.</p>
 *
 * <p><b>Where does the image come from?</b> An app's own assets, or bytes it was handed — a camera
 * capture, a database blob, a download. Both end up as an {@link Image}, and neither belongs in
 * {@code onPaint}: decoding is expensive, and doing it per frame is the classic reason a list
 * stutters exactly where the pictures are.</p>
 *
 * <p>Drawn here rather than loaded from a file, so the recipe carries no asset of its own — but a
 * generated image and a decoded one behave identically from this point on.</p>
 */
public class R26Images extends Recipe
{
   public String title()
   {
      return "Images";
   }

   public String note()
   {
      return "Decode once, keep the Image, reuse it. Loading inside onPaint or inside a list's bind"
         + " step is what makes scrolling stutter where the pictures are.";
   }

   public void build(Component body) throws Exception
   {
      // Made once, held, reused by all three views below — the point of the recipe.
      Image swatch = gradient(240, 160);

      // The names repay one careful reading, because two of them are the opposite of what they
      // sound like. RATIO fits the whole picture INSIDE the box; FIT scales until it COVERS the
      // box and crops what hangs over. Guessing between those two is how a portrait photo ends up
      // letterboxed in a tile that had room to crop it properly.
      show(body, "RATIO — the whole picture, inside the box", swatch, ImageSize.RATIO);
      show(body, "FIT — covers the box, crops the overflow", swatch, ImageSize.FIT);
      show(body, "STRETCH — fills by distorting. Almost never right", swatch, ImageSize.STRETCH);
   }

   private void show(Component body, String caption, Image img, ImageSize size)
      throws Exception
   {
      Card c = new Card();
      c.add(LAY_AUTO, label(caption, 1f, true, Styles.textColor, Position.START));
      ImageView v = new ImageView(img, size);
      v.getStyle()
         .autoHeight(Relative.ofFont(6f));
      c.add(LAY_AUTO, outgap(v, 0, .4f, 0, 0));
      c.getStyle()
         .outgap(Gap.ofFont(0, 0, 0, .6f));
      body.add(LAY_AUTO, c);
   }

   /**
    * Builds an image in memory from the theme's own colours.
    *
    * <p>An {@code Image} is pixels; where they came from stops mattering the moment you have one.
    * The same object could have arrived from {@code PhotoPicker}, from a SQLite blob or from a
    * download, and every line after this one would be unchanged.</p>
    */
   private static Image gradient(int w, int h) throws Exception
   {
      Image img = Image.createSurface(w, h);
      int[] px = new int[w * h];
      int from = Styles.primaryColor.color(), to = tertiary().color();
      for (int y = 0; y < h; y++)
      {
         int rowColor = ColorUtils.lerpColor(from, to, y / (float) h);
         for (int x = 0; x < w; x++)
            // Diagonal bands, so cropping and stretching are both obvious at a glance.
            px[y * w + x] = ((x + y) / 18) % 2 == 0 ? rowColor
               : ColorUtils.lerpColor(rowColor, Styles.backColor.color(), .35f);
      }
      img.setPixels(px);
      // Pixels written, texture stale: applyChanges is what pushes them to the GPU. Forgetting it
      // gives you an image that is correct in memory and blank on screen.
      img.applyChanges();
      return img;
   }
}
