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.*;

/**
 * Filling a form without touching the screen.
 *
 * <p>Every CrossFly app runs on the desktop, and on the desktop a form is filled with the keyboard.
 * Tab walks the fields in the order they were <b>added</b>, which is why building a form in reading
 * order is not only tidier — it is the tab order, for free. Build it out of order and the user
 * tabs from the postcode back up to the surname.</p>
 *
 * <p>Two style flags decide who can be reached. {@code focusOnTouch} means tapping this gives it
 * the keyboard; {@code focusOnKey} means a keypress can move focus into it. A button that should
 * be reachable by Tab needs the second, and a display-only panel wants neither — a Tab stop on
 * something that does nothing is a Tab stop the user has to press through every time.</p>
 *
 * <p>The other keyboard is the one you bring with you: {@link NumericKeypad} is an on-screen pad
 * for a device that has no keys at all, or for a value that is only ever digits — a warehouse
 * terminal, a scale, a till.</p>
 */
public class R27Focus extends Recipe
{
   private Label reached;

   public String title()
   {
      return "Keyboard and focus";
   }

   public String note()
   {
      return "Tab follows the order you ADDED the fields, not where they ended up on screen. Build"
         + " in reading order and the tab order is correct without being designed.";
   }

   public void build(Component body) throws Exception
   {
      body.add(LAY_AUTO, outgap(label("Press Tab. It walks these in the order they were added.", 1f,
         false, Styles.captionColor, Position.START), 0, 0, 0, .8f));

      field(body, "Name");
      field(body, "Company");
      field(body, "Postcode");

      reached = label("Focus has not reached the button yet.", 1f, false, Styles.captionColor,
         Position.START);
      body.add(LAY_AUTO, outgap(reached, 0, .4f, 0, .6f));

      Button send = Tones.action("Send");
      // Reachable by keyboard, not only by pointer. Without this the last thing in the form is the
      // one thing a keyboard user cannot get to, and they have to reach for the mouse to finish.
      send.getStyle()
         .focusOnKey(true)
         .focusOnTouch(true);
      send.onAction(c -> Hint.show("Sent", 1500));
      send.testTag("focus.send");
      body.add(LAY_AUTO, outgap(send, 0, 0, 0, 1.4f));

      body.add(LAY_AUTO, Tones.head(0xE312, "A keypad of your own", Tones.highlight()));
      body.add(LAY_AUTO, outgap(label("For a device with no keyboard, or a value that is only ever"
         + " digits.", 1f, false, Styles.captionColor, Position.START), 0, 0, 0, .6f));

      final TextField total = new TextField();
      total.caption("Total");
      total.setMode(EditMode.CURRENCY);
      total.testTag("focus.total");
      body.add(LAY_AUTO, outgap(total, 0, 0, 0, .6f));

      // The keypad reports TOKENS, not characters — "0".."9", "," and BACK — so the screen decides
      // what each one means. That is what lets the same pad drive a currency field, a quantity and
      // a PIN without the pad knowing about any of them.
      NumericKeypad pad = new NumericKeypad(token ->
      {
         String now = total.getText();
         if (NumericKeypad.BACK.equals(token))
            total.setText(now.isEmpty() ? "" : now.substring(0, now.length() - 1));
         else
            total.setText(now + token);
         Screen.repaint();
      });
      pad.testTag("focus.keypad");
      body.add(LAY_AUTO, pad);
   }

   private void field(Component body, String caption) throws Exception
   {
      TextField f = new TextField();
      f.caption(caption);
      f.testTag("focus." + caption.toLowerCase());
      // Fired when the field takes the keyboard. On a phone this is where a screen scrolls the
      // field clear of the on-screen keyboard, which is what Device.shiftScreen is for.
      f.onFocus(c ->
      {
         reached.setText("Focus is on: " + caption);
         reached.setValidLayout(false);
         Screen.repaint();
      });
      body.add(LAY_AUTO, outgap(f, 0, 0, 0, .6f));
   }
}
