/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright, All rights reserved
*         DTU Physics, Kgs. Lyngby, Denmark
*         Synchrotron SOLEIL, Saint-Aubin, France
*
* Component: Text
*
* %Identification
* Written by: Emmanuel Farhi
* Date: Oct 10, 2026
* Origin: SOLEIL
* Release: McStas 3.9
*
* Draw a text
*
* %Description
* A text with given size and orientation. The text is drawn in upper letters.
*
* Example: Text(text="SOLEIL", size=0.5, plane="XY")
*
* %Parameters
* INPUT PARAMETERS
*
* text:       [str] Text to draw.
* size:       [m]   Size of text.
* plane:      [str] "XY","YZ",or "XZ"
* pen_thickness: [1] Stroke diameter as fraction of text size. 0 draws thin lines, >0 draws each stroke as a cylinder with spherical joints.
* %End
*******************************************************************************/
DEFINE COMPONENT Text

SETTING PARAMETERS(string text, size=0.5, string plane="XY", pen_thickness=0.1)

SHARE %{

  /* ------------------------------------------------------------------ */
  /* Stroke font (4 wide x 6 tall grid). NULL = blank (space).          */
  /* Extend it by adding entries here.                                  */
  /* ------------------------------------------------------------------ */
  static const char *glyph[128] = {
      ['A'] = "0,0 2,6 4,0|1,2 3,2",
      ['B'] = "0,6 0,0 3,0 4,1 4,2 3,3 0,3|3,3 4,4 4,5 3,6 0,6",
      ['C'] = "4,5 3,6 1,6 0,5 0,1 1,0 3,0 4,1",
      ['D'] = "0,6 0,0 2,0 4,2 4,4 2,6 0,6",
      ['E'] = "4,6 0,6 0,0 4,0|0,3 3,3",
      ['F'] = "4,6 0,6 0,0|0,3 3,3",
      ['G'] = "4,5 3,6 1,6 0,5 0,1 1,0 3,0 4,1 4,3 2,3",
      ['H'] = "0,6 0,0|4,6 4,0|0,3 4,3",
      ['I'] = "1,6 3,6|2,6 2,0|1,0 3,0",
      ['J'] = "3,6 3,1 2,0 1,0 0,1",
      ['K'] = "0,6 0,0|0,2 4,6|1,3 4,0",
      ['L'] = "0,6 0,0 4,0",
      ['M'] = "0,0 0,6 2,3 4,6 4,0",
      ['N'] = "0,0 0,6 4,0 4,6",
      ['O'] = "1,0 3,0 4,1 4,5 3,6 1,6 0,5 0,1 1,0",
      ['P'] = "0,0 0,6 3,6 4,5 4,4 3,3 0,3",
      ['Q'] = "1,0 3,0 4,1 4,5 3,6 1,6 0,5 0,1 1,0|2,2 4,0",
      ['R'] = "0,0 0,6 3,6 4,5 4,4 3,3 0,3|2,3 4,0",
      ['S'] = "4,5 3,6 1,6 0,5 0,4 1,3 3,3 4,2 4,1 3,0 1,0 0,1",
      ['T'] = "0,6 4,6|2,6 2,0",
      ['U'] = "0,6 0,1 1,0 3,0 4,1 4,6",
      ['V'] = "0,6 2,0 4,6",
      ['W'] = "0,6 1,0 2,4 3,0 4,6",
      ['X'] = "0,0 4,6|0,6 4,0",
      ['Y'] = "0,6 2,3 4,6|2,3 2,0",
      ['Z'] = "0,6 4,6 0,0 4,0",
      ['0'] = "1,0 3,0 4,1 4,5 3,6 1,6 0,5 0,1 1,0|0,1 4,5",
      ['1'] = "1,5 2,6 2,0|0,0 4,0",
      ['2'] = "0,5 1,6 3,6 4,5 4,4 0,0 4,0",
      ['3'] = "0,6 4,6 2,3 4,0 0,0",
      ['4'] = "3,6 3,0|4,2 0,2 3,6",
      ['5'] = "4,6 0,6 0,3 3,3 4,2 4,1 3,0 0,0",
      ['6'] = "4,5 3,6 1,6 0,5 0,1 1,0 3,0 4,1 4,2 3,3 0,3",
      ['7'] = "0,6 4,6 1,0",
      ['8'] = "1,0 3,0 4,1 4,2 3,3 1,3 0,2 0,1 1,0"
              "|1,3 0,4 0,5 1,6 3,6 4,5 4,4 3,3 1,3",
      ['9'] = "0,1 1,0 3,0 4,1 4,5 3,6 1,6 0,5 0,4 1,3 4,3",
      ['-'] = "0,3 4,3",
      ['+'] = "0,3 4,3|2,1 2,5",
      ['.'] = "1,0 1,1",
      [','] = "1,1 2,0",
      [':'] = "1,1 2,1|1,4 2,4",
      ['/'] = "0,0 4,6",
      ['('] = "3,6 2,5 2,1 3,0",
      [')'] = "1,6 2,5 2,1 1,0",
      ['!'] = "2,6 2,1|1,0 3,0",
      ['?'] = "0,5 1,6 3,6 4,5 4,4 2,2 2,0",
      ['_'] = "0,0 4,0",
      ['='] = "0,2 4,2|0,4 4,4",
      ['*'] = "2,2 2,6|0,3 4,5|0,5 4,3",
      [' '] = NULL,
  };

  /* ------------------------------------------------------------------ */
  /* Core: draw one glyph at pen position (u0,0), scaled.                */
  /* plane: 0 = XY, 1 = YZ, 2 = ZX. Text runs along the first axis,       */
  /* letter height grows along the second axis.                          */
  /* ------------------------------------------------------------------ */
  
%}

DECLARE %{
  int pl;
%}

INITIALIZE %{
  if (!text || !plane || size <= 0.0f || pen_thickness < 0) {
    exit(-fprintf (stderr, "Error (%s): Text has invalid parameters.\n", NAME_CURRENT_COMP));
    exit (-1);
  }
  if (strlen(plane) != 2)
    exit(-fprintf (stderr, "Error (%s): Invalid plane.\n", NAME_CURRENT_COMP));
    
  if      (plane[0] == 'X' && plane[1] == 'Y') pl = 0;
  else if (plane[0] == 'Y' && plane[1] == 'Z') pl = 1;
  else if (plane[0] == 'X' && plane[1] == 'Z') pl = 2;
  else if (plane[1] == 'X' && plane[0] == 'Z') pl = 2;
  else 
    exit(-fprintf (stderr, "Error (%s): Invalid plane specification. Must be XY, YZ or XZ\n", NAME_CURRENT_COMP));

  if (mcdotrace==1) {
    /* For legacy mcdisplay line-mode, run with 0 thickness */
    pen_thickness=0;
  }
%}

TRACE %{
  /* component Text does nothing */
%}

MCDISPLAY %{
    
  float s   = size / 6.0f;          /* metres per grid unit; glyph height = 6 units */
  float adv = 5.0f;                 /* pen advance in grid units: 4 wide + 1 gap    */
  float u0  = 0.0f;                 /* pen position in grid units                   */

  for (const char *t = text; *t; t++, u0 += adv) {
      int c = toupper((unsigned char)*t);
      if (c < 128 && glyph[c]) {
          float ox=0,oy=0,oz=0;
          const char *p = glyph[c];
          float prevx = 0, prevy = 0, prevz = 0;
          int pen = 0;                      /* pen down? */

          while (*p) {
              if (*p == '|') { pen = 0; p++; continue; }
              while (*p == ' ') p++;
              if (!*p) break;

              /* parse "x,y" (strtol also tolerates future negative coords) */
              char *end;
              long gx = strtol(p, &end, 10); p = end;
              if (*p == ',') p++;
              long gy = strtol(p, &end, 10); p = end;

              float u = (u0 + (float)gx) * s;   /* along text direction   */
              float v = (float)gy * s;          /* along letter height    */

              float x, y, z;
              switch (pl) {
              case 0:  x = ox + u;  y = oy + v;  z = oz;      break;  /* XY */
              case 1:  x = ox;      y = oy + u;  z = oz + v;  break;  /* YZ */
              default: x = ox + v;  y = oy;      z = oz + u;  break;  /* ZX */
              }

              if (pen) {
                  if (pen_thickness > 0) {
                      /* stroke as a cylinder centred on the segment midpoint, axis along it */
                      double dx = x - prevx, dy = y - prevy, dz = z - prevz;
                      double len = sqrt(dx*dx + dy*dy + dz*dz);
                      if (len > 0)
                          cylinder(0.5*(prevx + x), 0.5*(prevy + y), 0.5*(prevz + z),
                                   0.5*size*pen_thickness, len, 0,
                                   dx/len, dy/len, dz/len);
                  } else
                      line(prevx, prevy, prevz, x, y, z);
              }

              /* round joints and stroke ends: sphere at every vertex */
              if (pen_thickness > 0)
                  sphere(x, y, z, 0.5*size*pen_thickness);

              prevx = x; prevy = y; prevz = z;
              pen = 1;
          }
      }
  }
%}

END
