/*************************************************
*    The PMW Music Typesetter - 3rd incarnation  *
*************************************************/

/* Copyright (c) Philip Hazel, 1991 - 2020 */

/* Written by Philip Hazel, starting November 1991 */
/* This file last modified: April 2020 */


/* This file contains code for outputting non-note items in a bar */


#include "pmwhdr.h"
#include "pagehdr.h"
#include "outhdr.h"

static uschar rrepeat_adjust[] = {66, 50, 50, 50, 66,  65, 85, 85, 85, 65};


/*************************************************
*      Deal with non-note item in a bar          *
*************************************************/

/* This function is used for all the various non-note items. When there's a lot
to do, a separate function is called. There's also a separate function for
those the are handled the same way in the pagination scan (see at the end).

Argument:   the item's data
Returns:    nothing
*/

void
out_setother(bstr *p)
{
int magn;

DEBUG(("out_setother() start\n"));

switch (p->type)
  {
  /* Set barline break just for this stave */

  case b_breakbarline:
  out_ybarend = out_ystave;
  break;

  /* Set no barline break just for this stave */

  case b_unbreakbarline:
  out_ybarend = out_deepybarend;
  break;

  /* Remember block containing data for previous barline for omitempty bars. */

  case b_prevbar:
  out_prevbar = (b_prevbarstr *)p;
  break;

  /* Remember x adjustment for dots for next note */

  case b_dotright:
  n_dotxadjust = (((b_dotrightstr *)p)->value * main_stavemagn)/1000;
  break;

  /* Remember start of tie/short slur and/or glissando */

  case b_tie:
  bar_cont->tie = (b_tiestr *)p;
  bar_cont->tiex = out_lastnotex + n_dotxadjust;
  n_dotxadjust = 0;
  break;

  /* Remember start of long slur */

  case b_slur:
    {
    slurstr *s = misc_setstartslur(p);

    /* The x & y position of the start of the slur gets set when the next note
    is processed, as a result of the slurstarted flag. However, put in some
    defaults for the mad case when the slur does not cross any notes. */

    s->moff = out_moff;
    s->x = out_barx + out_findXoffset(out_moff);
    s->y = L_3L;

    /* The slurstarted flag tells the note processing routine to scan the slur
    data for any slurs with s->count set to 0, and to set their left
    x-coordinate according to that [moved] note, and also to set other
    parameters appropriately. */

    out_slurstarted = TRUE;
    }
  break;

  /* Draw a long slur. If there is no id, draw the most recent slur. Otherwise
  search for the correct identity, complaining if not found, though that should
  have been picked up earlier during the setting up of the cont structure. */

  case b_endslur:
    {
    slurstr *s = misc_getendslur(p);
    if (s == NULL)
      error_moan(ERR62, ((b_endslurstr *)p)->id, out_bar, out_stave);
    else
      misc_drawslur(s, n_x, n_pitch, FALSE);
    }
  break;

  /* Deal with gaps in lines and slurs. The coordinate data has to be saved,
  because the vertical position of the line or slur is not yet known. */

  case b_linegap:
  case b_slurgap:
    {
    b_linegapstr *pg = (b_linegapstr *)p;
    slurstr *s = bar_cont->slurs;
    gapstr *g;
    int slurid = pg->id;

    if (slurid != 0)
      {
      while (s != NULL)
        {
        if ((s->slur)->id == slurid) break;
        s = s->next;
        }
      }

    if (s == NULL)
      { error_moan(ERR62, pg->id, out_bar, out_stave); break; }

    if ((s->slur->flags & sflag_l) == 0)
      {
      if (p->type == b_linegap)
        {
        error_moan(ERR93, out_bar, out_stave);
        break;
        }
      }
    else
      {
      if (p->type == b_slurgap)
        {
        error_moan(ERR119, out_bar, out_stave);
        break;
        }
      }

    g = store_Xget(sizeof(gapstr));
    g->next = s->gaps;
    s->gaps = g;
    g->gap = pg;
    g->x = out_barx + out_findXoffset(out_moff) + pg->xadjust;

    /* Except at the end of a bar, move right to the centre of
    the next notehead. */

    if (misc_nextnote((b_notestr *)p, NULL) != NULL)
      g->x += 3*main_stavemagn;
    }
  break;

  /* Deal with hairpins. Take special action for a hairpin that ends before
  the first note of a bar. */

  case b_hairpin:
    {
    b_hairpinstr *h = (b_hairpinstr *)p;
    if (h->opt != 0)
      out_setstarthairpin(h, out_barx + out_findXoffset(out_moff));
    else if (out_moff == 0)
      out_drawhairpin(h, out_barx - 4*main_stavemagn);
    else
      out_drawhairpin(h, n_x + 6*main_stavemagn);
    }
  break;

  /* Deal with position reset */

  case b_reset:
  out_moff = 0;
  out_passedreset = TRUE;
  break;

  /* Deal with tremolo */

  case b_tremolo:
  if (n_pitch != 0)
    {
    out_tremolo = (b_tremolostr *)p;
    out_tremupflag = n_upflag;
    out_tremx = n_x;
    out_tremy =
      misc_ybound(n_notetype < minim || !n_upflag, NULL, FALSE, FALSE);
    }
  break;

  /* Deal with plets */

  case b_plet:
  out_plet = (b_pletstr *)p;
  beam_seq = -1;                  /* Maintain beaming state */
  out_plet_x = -1;                /* Indicates plet starting */
  out_plet_highest = -BIGNUMBER;
  out_plet_lowest  =  BIGNUMBER;
  out_plet_highest_head = 0;
  out_pden = out_plet->pletlen;
  if (out_pden > 4) out_pnum = 2;
  break;

  case b_endplet:
    {
    int flags = out_plet->flags;

    if (((bar_cont->flags & cf_triplets) == 0) != ((flags & plet_x) == 0))
      {
      int fontsize = (curmovt->fontsizes)->fontsize_triplet;
      int *matrix = (curmovt->fontsizes)->fontmatrix_triplet;
      int x0 = out_plet_x - (5*main_stavemagn)/10;
      int x1 = n_x + 7*main_stavemagn;
      int mid = (x0 + x1)/2;
      int yy, yyl, yyr, sx, width;
      BOOL above, omitline;
      uschar s[10];

      if (matrix != NULL) memcpy(font_transform, matrix, 4*sizeof(int));

      /* Determine whether above or below, either by forcing, or by beaming, or
      on the position of the highest notehead. */

      if ((flags & (plet_a | plet_b)) != 0) above = (flags & plet_a) != 0;
        else if (beam_seq == 1) above = beam_upflag;
          else above = out_plet_highest_head < P_3L;

      if ((flags & (plet_by | plet_bn)) != 0)
        omitline = (flags & plet_bn) != 0;
          else omitline = (beam_seq == 1 && above == beam_upflag);

      /* Compute y level for the number according to the highest and lowest
      bits of note in between, or take absolute level if given. */

      if ((flags & plet_abs) != 0) yy = above? 16000 : 0; else
        {
        if (above)
          yy = (out_plet_highest < 16000)? 18000 : out_plet_highest + 2000;
            else yy = ((out_plet_lowest > 0)? 0 : out_plet_lowest) - fontsize;
        }

      yyl = ((yy + out_plet->yleft)*main_stavemagn)/1000;   /* Manual adjustment */
      yyr = ((yy + out_plet->yright)*main_stavemagn)/1000;  /* Manual adjustment */

      yy = (yyl + yyr)/2;   /* mid height */

      /* Scale the font size, create the string, and find out how many digits
      there are in it. We scale the font size according to the stave size and
      also according to the size of the previous note. */

      fontsize = mac_muldiv(fontsize*main_stavemagn, n_fontsize, 10000000);

      sprintf(CS s, "%d", out_plet->pletlen);
      width = string_width(s, curmovt->font_triplet, fontsize)/2;

      /* If beamed and no line, compute text position according to the beam;
      otherwise compute it as central in the line */

      if (beam_seq == 1 && above == beam_upflag && omitline)
        {
        sx = (out_plet_x + n_x)/2 + out_plet->x;
        if (beam_upflag) sx += (51*main_stavemagn)/10;
        }
      else sx = mid + (omitline? out_plet->x : 0);

      /* Now output the text */

      out_string(s, curmovt->font_triplet, fontsize, sx - width,
        out_ystave - yy, 0);
      font_reset();

      /* Draw the line if wanted */

      if (!omitline)
        {
        int slope = mac_muldiv(yyr - yyl, 1000, x1 - x0);
        int x[3], y[3];
        int ly, ry;

        ly = ry = above? -2000 : 2000;

        if ((flags & plet_lx) != 0) ly = -ly;
        if ((flags & plet_rx) != 0) ry = -ry;

        yyl += (35*fontsize)/100;
        yyr += (35*fontsize)/100;

        x[0] = x[1] = x0;
        x[2] = mid - 1500 - width;
        y[0] = yyl + ly;
        y[1] = yyl;
        y[2] = yyl + mac_muldiv(slope, x[2] - x0, 1000);
        ps_lines(x, y, 3, curmovt->tripletlinewidth);

        x[0] = mid + 1500 + width;
        x[1] = x[2] = x1;
        y[0] = yyr - mac_muldiv(slope, x1 - x[0], 1000);
        y[1] = yyr;
        y[2] = yyr + ry;
        ps_lines(x, y, 3, curmovt->tripletlinewidth);
        }
      }
    }

  out_plet = NULL;
  out_pnum = 1;
  out_pden = 2;
  break;

  /* Clefs, keys, and times */

  case b_setclef:
  bar_cont->clef = ((b_setclefstr *)p)->value;
  break;

  case b_clef:
  bar_cont->clef = ((b_clefstr *)p)->trueclef;
  if (out_stave != 0 && !((b_clefstr *)p)->suppress)
    out_writeclef(out_barx + out_findXoffset(out_moff + posx_clef) +
      out_Xadjustment, out_ystave - out_Yadjustment,
        ((b_clefstr *)p)->trueclef,
          (curmovt->fontsizes)->fontsize_clefs, TRUE);

  out_Xadjustment = out_Yadjustment = 0;
  break;

  case b_key:
  if (out_stave != 0 && !((b_keystr *)p)->suppress)
    {
    int x = out_barx +
      out_findXoffset(out_moff + posx_keyfirst + out_keycount++) +
        out_Xadjustment;
    if (!out_startlinebar && out_moff == 0 &&
      curmovt->keydoublebar && !out_repeatonbarline &&
        out_bar != curmovt->startbracketbar + 1)
          ps_barline(out_lastbarlinex, out_ystave, out_ybarend, bar_double);
    out_writekey(x, out_ystave - out_Yadjustment, bar_cont->clef,
      ((b_keystr *)p)->key);
    }

  out_Xadjustment = out_Yadjustment = 0;
  break;

  case b_time:
  if (out_stave != 0 && !((b_timestr *)p)->suppress)
    out_writetime(out_barx +
      out_findXoffset(out_moff + posx_timefirst + out_timecount++) +
        out_Xadjustment, out_ystave - out_Yadjustment,
          ((b_timestr*)p)->time);

  bar_cont->time = ((b_timestr *)p)->time;
  out_Xadjustment = out_Yadjustment = 0;
  break;

  case b_settime:
  bar_cont->time = ((b_settimestr *)p)->value;
  break;

  /* Dotted bar line in mid-bar */

  case b_dotbar:
  ps_barline(out_barx+out_findXoffset(out_moff+posx_dotbar)+out_Xadjustment,
      out_ystave, out_ybarend, bar_dotted);
  out_Xadjustment = out_Yadjustment = 0;
  break;

  /* Repeat marks. For a right repeat we have to check for a following left
  repeat, because some styles are different when the two are simultaneous. */

  case b_rrepeat:
  magn = (curmovt->barlinesize > 0)? curmovt->barlinesize : main_stavemagn;

  if (misc_nextnote((b_notestr *)p, NULL) == NULL)   /* check for end of bar */
    {
    int xadjust;
    int reptype = rep_right;

    if (out_lineendflag)
      xadjust = (out_bar >= curmovt->barcount)?
        (rrepeat_adjust[curmovt->repeatstyle + (curmovt->unfinished? 0:5)]) : 68;
    else
      {
      bstr *pp = ((curmovt->stavetable)[out_stave])->barindex[out_bar+1];
      if (pp != NULL)
        {
       while (pp->type == b_Jump)
          pp = (bstr *)
            ((uschar *)(((b_Jumpstr *)pp)->next) + length_table[b_Jump]);
        if (pp->type == b_lrepeat)
          {
          reptype = rep_dright;
          bar_cont->flags |= cf_rdrepeat;   /* Remember for next start */
          }
        }
      xadjust = 50;
      }
    out_lastbarwide = TRUE;
    out_writerepeat(out_barlinex - (xadjust*magn)/10, reptype, magn);
    }

  /* Not at end of bar; we look at the next item to see if it is a left repeat. */

  else
    {
    bstr *pp = (bstr *)((uschar *)p + length_table[p->type]);
    while (pp->type == b_Jump)
       pp = (bstr *)
         ((uschar *)(((b_Jumpstr *)pp)->next) + length_table[b_Jump]);
    out_writerepeat(out_barx +
      out_findXoffset(out_moff + posx_RR) + out_Xadjustment,
        (pp->type == b_lrepeat)? rep_dright : rep_right, magn);
     }

  out_Xadjustment = out_Yadjustment = 0;
  break;

  /* See if there is also a right repeat here. If not, we put the sign on the
  bar line for non-start of line bars, if there is no position for it. If the
  previous bar ended with a repeat, this repeat will have been found and the
  flag set. Note that the variable out_posxRL contains the relevant posx_RLxxx
  value this this particular bar. */

  case b_lrepeat:
  magn = (curmovt->barlinesize > 0)? curmovt->barlinesize : main_stavemagn;

  if (out_findTentry(out_moff + posx_RR) == NULL)
    {
    if (!out_startlinebar && out_findTentry(out_moff+out_posxRL) == NULL)
      {
      out_writerepeat(out_lastbarlinex, ((bar_cont->flags & cf_rdrepeat) == 0)? rep_left : rep_dleft, magn);
      bar_cont->flags &= ~cf_rdrepeat;
      out_repeatonbarline = TRUE;
      }
    else out_writerepeat(out_barx +
      out_findXoffset(out_moff + out_posxRL) + out_Xadjustment, rep_left, magn);
    }

  /* Right repeat exists */
  else
    {
    if (misc_nextnote((b_notestr *)p, NULL) == NULL)
      out_writerepeat(out_barlinex -
        (out_lineendflag? (18*main_stavemagn)/10 : 0), rep_dleft, magn);
    else
      out_writerepeat(out_barx + out_findXoffset(out_moff + posx_RR) +
        5*main_stavemagn + out_Xadjustment, rep_dleft, magn);
    }

  out_Xadjustment = out_Yadjustment = 0;
  break;

  /* Set masquerade for next note */

  case b_masq:
  n_masq = ((b_masqstr *)p)->value;
  break;

  /* Set ornament for next note */

  case b_ornament:
  if (n_ornament == NULL) n_ornament = ((b_ornamentstr *)p);
  break;

  /* Deal with dynamic move and/or bracketing for next note */

  case b_dynmove:
    {
    b_dynmovestr *d = (b_dynmovestr *)p;
    int z = d->dynamic;
    out_dynmovef[z] = d->bflags;
    out_dynmovex[z] = d->x;
    out_dynmovey[z] = mac_muldiv(d->y, main_stavemagn, 1000);
    }
  break;

  /* Pauses and breaths */

  case b_comma:
    {
    int y;
    uschar *s;
    b_notestr *pp = misc_nextnote((b_notestr *)p, 0);
    if (main_righttoleft)
      {
      y = 19;
      s = US"\xc3\x81";
      }
    else
      {
      y = 22;
      s = US"N";
      }
    if (pp != NULL && pp->spitch > 148) y += pp->spitch - 148;
    out_string(s, font_mf, 10*main_stavemagn,
      out_barx + out_findXoffset(out_moff + posx_comma) + out_Xadjustment,
        out_ystave - y*main_stavemagn - out_Yadjustment, 0);
    }
  out_Xadjustment = out_Yadjustment = 0;
  break;

  case b_tick:
    {
    int y = 18;
    b_notestr *pp = misc_nextnote((b_notestr *)p, 0);
    if (pp != NULL && pp->spitch > 148) y += pp->spitch - 148;
    out_string(US"\302\200", font_mf, 10*main_stavemagn,
      out_barx + out_findXoffset(out_moff + posx_tick) + out_Xadjustment,
        out_ystave - y*main_stavemagn - out_Yadjustment, 0);
    }
  out_Xadjustment = out_Yadjustment = 0;
  break;

  case b_caesura:
  out_string(main_caesurastrings[curmovt->caesurastyle], font_mf,
    10*main_stavemagn,
      out_barx + out_findXoffset(out_moff + posx_caesura) + out_Xadjustment,
        out_ystave - 12*main_stavemagn - out_Yadjustment, 0);
  out_Xadjustment = out_Yadjustment = 0;
  break;

  /* Deal with nth time bar markings; if there is already a current block, we
  must differentiate between a number of different cases:

    (1) This is an additional marking to be added to the current one
    (2) This is the start of the next iteration at the start of a line
    (3) This is the start of the next iteration in the middle of a line

  In case (3) we have to close off the previous marking; in case (2) we
  replace the previous block; in case (1) we add an additional block. */

  case b_nbar:
    {
    int miny = 0;

    if (bar_cont->nbar != NULL)
      {
      nbarstr *bb = bar_cont->nbar;

      /* (1) Deal with additional marking; hang on extra block */

      if (out_lastbarlinex == bb->x)
        {
        nbarstr *bbb = store_Xget(sizeof(nbarstr));
        while (bb->next != NULL) bb = bb->next;
        bb->next = bbb;
        bbb->next = NULL;
        bbb->nbar = (b_nbarstr *)p;     /* only data needed is original block */
        break;                          /* that's all */
        }

      /* (2) If this is a line start, the current block is superflous. Note
      that there will only ever be one block at the start of a line if its
      x-value doesn't match (i.e. it's continued). */

      else if (out_startlinebar) out_freenbar();

      /* (3) This is the middle of a line; close off the previous */

      else
        {
        miny = out_drawnbar(TRUE, out_lastbarlinex - 1500);
        out_freenbar();
        }
      }

    /* Now set up for this one */

    out_setstartnbar((b_nbarstr *)p, out_lastbarlinex, miny);
    }
  break;

  case b_all:
  if (!out_startlinebar && bar_cont->nbar != NULL)
    out_drawnbar(FALSE, out_lastbarlinex);
  out_freenbar();
  break;

  /* Set offset adjustment for next beam */

  case b_offset:
  beam_offsetadjust = ((b_offsetstr *)p)->value;
  break;

  /* Set next beam as accellerando */

  case b_beamacc:
  beam_accrit = ((b_beamaccstr *)p)->value;
  break;

  /* Set next beam as ritardando */

  case b_beamrit:
  beam_accrit = -((b_beamritstr *)p)->value;
  break;

  /* Text at the end of a bar must be handled now; othewise we queue it to be
  done with the next note, when the bounding box is known. Although n_x must be
  set to the barline for the text, we must preserve it in case something that
  follows needs it, for example, an end slur. */

  case b_text:
  if (misc_nextnote((b_notestr *)p, NULL) == NULL)
    {
    int savex = n_x;
    n_x = out_barlinex;
    out_text((b_textstr *)p, TRUE);
    n_x = savex;
    }
  else
    {
    if (out_textqueueptr >= TEXT_QUEUE_SIZE)
      error_moan(ERR131, TEXT_QUEUE_SIZE);
    out_textXqueue[out_textqueueptr] = out_textX;
    out_textqueue[out_textqueueptr++] = (b_textstr *)p;
    }
  out_textX = NULL;
  break;

  /* Extra text data is saved for the next text item which must immediately
  follow it. */

  case b_textX:
  out_textX = (b_textXstr *)p;
  break;

  /* Draw items associated with notes must be saved, as for text items. */

  case b_draw:
  if (misc_nextnote((b_notestr *)p, NULL) == NULL)
    {
    b_drawstr *d = (b_drawstr *)p;
    draw_ox = out_barlinex;
    draw_oy = 0;
    out_dodraw(d->item, d->args, d->overflag);
    }
  else out_drawqueue[out_drawqueueptr++] = (b_drawstr *)p;
  break;

  /* Double bar setting */

  case b_dbar:
  out_barchar = bar_double;
  break;

  /* Internal end bar setting */

  case b_ebar:
  out_barchar = bar_thick;
  break;

  /* Invisible bar setting */

  case b_ibar:
  out_omitbarline = TRUE;
  break;

  /* Positioning adjustments - horizontal distances may or may not be scaled;
  vertical distances are always scaled. */

  case b_move:
    {
    b_movestr *bm = (b_movestr *)p;
    if (bm->relative) out_Xadjustment = (bm->x * main_stavemagn)/1000;
      else out_Xadjustment = bm->x;
    out_Yadjustment = (bm->y * main_stavemagn)/1000;
    }
  break;

  /* Stave zero copy level adjustment - applies of stave 0 being printed at
  this stave's level. */

  case b_zcopy:
    {
    zcopystr *z = curmovt->zcopy;
    while (z != NULL)
      {
      if (z->level == out_ystave - out_yposition)
        {
        z->baradjust = ((b_zcopystr *)p)->value;
        break;
        }
      z = z->next;
      }
    }
  break;

  /* Handle all items that are common between this scan and the pre-scan during
  pagination. */

  default:
  misc_commoncont(p);
  break;
  }

DEBUG(("out_setother() end\n"));
}

/* End of setother.c */
