package dev.cf.recipe;

import dev.cf.Tones;

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

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

/**
 * Capturing a signature, and the three details that separate one that works in the field.
 *
 * <p>None of them is about drawing — {@link DrawingBoard} handles that. They are about what happens
 * around it.</p>
 */
public class R19Signature extends Recipe
{
   private DrawingBoard board;
   private Button confirm;
   private boolean drawn;

   public String title()
   {
      return "Capturing a signature";
   }

   public String note()
   {
      return "White pad, not the theme surface: the image is captured to be printed. Under dark"
         + " mode a themed pad captures white ink on navy, which is unusable downstream.";
   }

   public void build(Component body) throws Exception
   {
      Component pad = new Component();
      pad.setLayout(new VerticalLayout(Position.START, Position.START));
      // 1 · The pad has a colour of its own. This is the ONE place ignoring the theme is right,
      // because the output is not a screen — it is an attachment on a document.
      pad.getStyle()
         .backColor(0xFFFFFFFF)
         .borderColor(Styles.borderColor)
         .borderThick(Styles.borderThick)
         .borderRadius(Styles.borderRadius)
         .autoHeight(Relative.ofFont(10f));
      pad.testTag("sign.pad");

      // 2 · The DrawingBoard reports strokes by letting you override its touch handlers. There is no
      // listener to look for, and adding one to the framework for this would be the wrong fix.
      board = new DrawingBoard()
      {
         @Override
         public void onTouchUp(float x, float y, int button) throws Exception
         {
            super.onTouchUp(x, y, button);
            setDrawn(true);
         }
      };
      board.setPenColor(0xFF111111);
      pad.add(LAY_FILL, board);
      body.add(LAY_AUTO, outgap(pad, 0, 0, 0, .8f));

      // Ink and pen, as in the DrawingBoard sample: a few ink colours and the thin/thick toggle.
      // A signature was black and hairline only (asked for on the iPhone, 17/10/2026).
      body.add(LAY_AUTO, outgap(inkPalette(), 0, 0, 0, .8f));

      Component row = new Component();
      row.setLayout(new GridLayout(Position.START, Position.CENTER));

      Button clear = new Button("Clear");
      clear.testTag("sign.clear");
      // Clear asks nothing: it is instantly and obviously reversible — you sign again.
      clear.onAction(c ->
      {
         board.clear();
         setDrawn(false);
         Screen.repaint();
      });

      // 3 · Confirm is dead until something is drawn. An empty signature is the easiest thing to
      // capture by accident and is indistinguishable from a real one once it is stored.
      confirm = Tones.action("Confirm");
      confirm.testTag("sign.confirm");
      confirm.setEnabled(false);
      pintarConfirm();
      confirm.onAction(c -> Hint.show("Signature captured", 2000));

      row.add(.5f, .5f, clear, confirm);
      body.add(LAY_AUTO, row);
   }

   /** Ink colours a signature is made with: black, navy, blue. */
   private static final int[] INKS = {0xFF111111, 0xFF1A237E, 0xFF1E88E5};

   /**
    * The ink row: one dot per ink and the pen-thickness toggle - the DrawingBoard sample's palette.
    *
    * @return the row
    * @throws Exception when building fails
    */
   private Component inkPalette() throws Exception
   {
      Component row = new Component();
      row.setLayout(new GridLayout(Position.START, Position.CENTER));
      Object[] params = new Object[(INKS.length + 1) * 2];
      final Button[] dots = new Button[INKS.length];
      for (int i = 0; i < INKS.length; i++)
      {
         final int ink = INKS[i], index = i;
         ComponentStyle dot = Styles.button()
            .copy()
            .backColor(0)
            .borderColor(0)
            .iconColor(ink)
            .ingap(Gap.ofFont(.4f, .3f, .4f, .3f));
         Button b = new Button(inkIcon(i == 0)).style(dot);
         b.testTag("sign.ink." + i);
         // The chosen ink carries a check: a pen colour you cannot see until you draw is a guess
         // (found by Test73, 19/10/2026 - tapping a dot changed nothing on the screen).
         b.onAction(e ->
         {
            board.setPenColor(ink);
            for (int k = 0; k < dots.length; k++)
            {
               dots[k].icon(inkIcon(k == index), dots[k].textPos);
               dots[k].setValidLayout(false);
            }
            Screen.repaint();
         });
         dots[i] = b;
         params[i] = 0f;
         params[INKS.length + 1 + i] = b;
      }
      ComponentStyle pen = Styles.button()
         .copy()
         .backColor(0)
         .borderColor(0)
         .iconColor(Styles.textColor)
         .ingap(Gap.ofFont(.4f, .3f, .4f, .3f));
      // brush, with the pen it gives: like the ink, the thickness in force is on the screen.
      Button thick = new Button("Thin", new MaterialIcon("\ue3ae", 1.4f), TextPos.TEXT_AT_RIGHT).style(pen);
      thick.testTag("sign.thick");
      thick.onAction(e ->
      {
         board.thick = !board.thick;
         thick.setText(board.thick ? "Thick" : "Thin");
      });
      params[INKS.length] = 0f;
      params[INKS.length * 2 + 1] = thick;
      row.add(params);
      return row;
   }

   /** A filled dot, and the same dot with a check for the ink in force. */
   private static MaterialIcon inkIcon(boolean chosen)
   {
      return new MaterialIcon(chosen ? "" : "", 1.5f);
   }

   private void setDrawn(boolean b)
   {
      if (drawn != b)
      {
         drawn = b;
         confirm.setEnabled(b);
         pintarConfirm();
      }
   }

   /**
    * Fills Confirm only while it can be pressed.
    *
    * <p>{@code setEnabled(false)} dims the label and stops the touch, and on the skin's outlined
    * button that is the whole story. A FILLED button is different: the fill keeps shouting "press
    * me" at a control that will not answer, which is worse than no styling at all. So the fill is
    * part of the state, not part of the button.</p>
    */
   private void pintarConfirm()
   {
      confirm.getStyle()
         .backColor(drawn ? Tones.accent() : Tones.accent().withAlpha(0x22))
         .textColor(drawn ? Styles.colorScheme().onPrimary : Tones.accent().withAlpha(0x88));
      confirm.setValidLayout(false);
   }
}
