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

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

/* Written by Philip Hazel, starting November 1991 */
/* This file last modified: August 2019 */


/* This file contains code for drawing ties and glissandos. */

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


/* Different actions are required for single notes and for chords. It is
simplest to separate these out into entirely separate routines. */



/************************************************
*        Output a tie (or short slur)           *
************************************************/

/* This function is called to draw a tie between two single notes. They need
not have the same pitch, in which case it is really a slur. The variable
n_prevtie points to the tie block for the first note, while n_nexttie points to
the ongoing tie block, if any.

Arguments:
  x1          the x coordinate of the end of the tie/slur
  endline     TRUE if at end of line
  flags       tie flags specifying type of tie (editorial, dashed, dotted)

Returns:      nothing
*/

void
out_setnotetie(int x1, BOOL endline, int flags)
{
BOOL startline = FALSE;
BOOL above = n_prevtie->abovecount > 0;
BOOL leftup = out_laststemup[out_stave];

b_notestr *left = n_prevtie->note;

int slurflags;
int tietype;
int dstart = 0;
int dstop = 1000;
int adjustL = 0;
int adjustR = 0;
int co = 0;
int x0 = bar_cont->tiex;
int y0 = left->spitch;
int y1 = n_pitch;
int yy0, yy1;

/* If this note is further tied, arrange to leave a bit of a gap in the tie
marks. */

int joinedties =
  (n_nexttie != NULL && n_prevtie->abovecount == n_nexttie->abovecount)?
    main_stavemagn : 0;

/* Check for coupling of first note */

mac_couplepitch(y0, left->flags);

/* If the final pitch is zero, make the tie horizontal. This can arise at the
end of a line if a tie continues over a totally empty bar. */

if (y1 == 0) y1 = y0;

/* Set slur flags and initialize tie type; raise the pitch when the tie is
above. */

if (above)
  {
  slurflags = tietype = 0;
  y0 += 8;
  y1 += 8;
  }
else
  {
  slurflags = sflag_b;
  tietype = 1;
  }

if ((flags & tief_editorial) != 0) slurflags |= sflag_e;
if ((flags & tief_dashed) != 0) slurflags |= sflag_i;
if ((flags & tief_dotted) != 0) slurflags |= sflag_i | sflag_idot;

/* Save basic levels for accidental checking */

yy0 = y0;
yy1 = y1;

/* Compute the rest of the "tie type" value. There are eight possibilities:

     0  stems up, tie above           1  stems up, tie below
     2  stems down, tie above         3  stems down, tie below
     4  first stem up, tie above      5  first stem up, tie below
     6  first stem down, tie above    7  first stem down, tie below

This value is used for checking on stem crossings, etc. Continuation ties can
only be types 0-3. Also, certain checks on the left end are skipped for
continued ties. Remember the fact for style handling. */

if (x0 == 0)
  {
  startline = TRUE;
  if (!n_upflag) tietype += 2;
  x0 = out_barx - 10*main_stavemagn;
  y0 = yy0 = y1;
  }

/* A non-continued tie */

else
  {
  if (leftup != n_upflag) tietype += 4;
  if (!leftup) tietype += 2;

  /* Check for stem crossing at the left-hand end */

  if (left->notetype >= minim &&
    (tietype == 0 || tietype == 4)) adjustL += 5*main_stavemagn;

  /* Check for dotted note at the left-hand end */

  if (above && leftup && (left->flags & nf_dot) != 0)
    {
    adjustL += 3*main_stavemagn;
    if ((left->flags & nf_dot2) != 0) adjustL += (35*main_stavemagn)/10;
    }

  /* Check for staccato etc. on the left-hand note */

  if ((tietype == 1 || tietype == 2 || tietype == 5 || tietype == 6) &&
    (left->acflags & af_dyninside) != 0 && (left->acflags & af_opposite) == 0)
      {
      int z = 6 - (y0 & 2);
      int zz = ((left->acflags & af_ring) == 0)? 2 : 3;

      if (leftup)
        y0 -= (y0 == 132)? 4 : (y0 <= 130)? zz : z;
      else
        y0 += (y0 == 148)? 4 : (y0 >= 150)? zz : z;
      }
  }

/* Check on stem crossing at the right-hand end, unless this is an invisible
note. */

if (n_notetype >= minim && (tietype == 3 || tietype == 5) &&
  (bar_cont->flags & cf_notes) != 0)
    adjustR -= 4*main_stavemagn - joinedties;

/* Check for staccato etc. at the right-hand end (of a slur, not a tie, of
course). */

if ((tietype == 1 || tietype == 2 || tietype == 4 || tietype == 7) &&
  (n_acflags & af_dyninside) != 0 && (n_acflags & af_opposite) == 0)
    {
    int z = 6 - (y1 & 2);
    int zz = ((n_acflags & af_ring) == 0)? 2 : 3;

    if (n_upflag)
      y1 -= (y1 == 132)? 4 : (y1 <= 130)? zz : z;
    else
      y1 += (y1 == 148)? 4 : (y1 >= 150)? zz : z;
    }

/* Check for enough space if the final note has an accidental. Then ensure that
ties do not start or end on stave lines */

if (above)
  {
  if (!endline && n_acc >= ac_flat && y1 <= yy1)
    {
    y1++;
    if (x1 - x0 <= 16000 && yy1 > yy0) { y1++; y0++; }
    }

  if (y0 <= 148 && (y0 & 2) == 0) y0++;
  if (y1 <= 148 && (y1 & 2) == 0) y1++;
  }

else
  {
  if (!endline && n_acc >= ac_natural && y1 >= yy1)
    {
    y1 -= 3;
    if (x1 - x0 < 16000 && yy1 < yy0) co += 1000;
    }

  if (y0 >= 132 && (y0 & 2) == 0) y0--;
  if (y1 >= 132 && (y1 & 2) == 0) y1--;
  }

/* If the slur is very steep, make it a bit more curvy, and adjust the right
hand end in some cases. Otherwise, for very short slurs, make flatter. */

if (abs(y0 - y1) > 10)
  {
  co += 2000;
  }
else if (x1 - x0 > 10000) co -= 1000;

/* If this is really a tie (the two notes have the same pitch) then it should
be horizontal. One end may have been moved to avoid accents, etc. If this is
the case, we adjust the other end to keep the tie horizontal. */

if (n_pitch == left->spitch)
  {
  if ((above && y0 > y1) || (!above && y0 < y1)) y1 = y0; else y0 = y1;
  }

/* If this is really a slur, make sure that we haven't negated its sense by
moving one end to account for accents. If we have, make it at least horizontal.
Which end to move depends on whether the tie is above or below. */

else if (n_pitch > left->spitch)
  {
  if (y1 < y0)
    {
    if ((tietype & 1) == 0) y1 = y0; else y0 = y1;
    }
  }
else
  {
  if (y1 > y0)
    {
    if ((tietype & 1) == 0) y0 = y1; else y1 = y0;
    }
  }

/* When printing right-to-left, certain ties need some horizontal adjustment to
cope with stem positions. */

if (main_righttoleft) switch (tietype)
  {
  case 0:
  adjustL -= 4*main_stavemagn;
  adjustR -= 4*main_stavemagn;
  break;

  case 3:
  adjustL += 4*main_stavemagn;
  adjustR += 4*main_stavemagn;
  break;

  case 5:
  adjustR += 4*main_stavemagn;
  break;

  case 6:
  adjustR -= 4*main_stavemagn;
  break;

  case 7:
  adjustL += 4*main_stavemagn;
  break;

  default:
  break;
  }

/* Set up the final coordinates, taking into account the style of continued
ties, and then output it. */

x0 += adjustL;
y0 = (y0 - 132)*main_stavemagn;
x1 += adjustR - joinedties;
y1 = (y1 - 132)*main_stavemagn;

if (endline && curmovt->endlinetiestyle != 0)
  {
  x1 += x1 - x0;
  y1 += y1 - y0;
  dstop = 500;
  }
else if (startline && curmovt->endlinetiestyle != 0)
  {
  x0 -= x1 - x0;
  y0 -= y1 - y0;
  dstart = 500;
  }

out_slur(x0, y0, x1, y1, slurflags, co, dstart, dstop);
}


/************************************************
*          Output ties on a chord               *
************************************************/

/* The variable tiecount is set to the number of ties which are in the
"abnormal" direction for the stem direction. A zero value for x0 means we are
continuing at the start of a line.

Arguments:
  notelist    pointer to the first note of the chort
  notecount   number of notes in the chord
  x1          the x coordinate of the end of the ties
  endflag     TRUE if drawing to end of line
  tieflags    type of tie

Returns:      nothing
*/

void
out_setchordtie(b_notestr **notelist, int notecount, int x1, BOOL endflag,
   int tieflags)
{
b_notestr *left = n_prevtie->note;
usint acflags = 0;
int tiecount = n_upflag? n_prevtie->abovecount : n_prevtie->belowcount;
int x0 = bar_cont->tiex;
int leftup = out_laststemup[out_stave];
int flags = 0;
int slurflags = 0;
int count, continued;

/* If this note is further tied, arrange to leave a bit of a gap in the tie
marks. */

int joinedties =
  (n_nexttie != NULL && n_prevtie->abovecount == n_nexttie->abovecount)?
    1000 : 0;

if ((tieflags & tief_editorial) != 0) slurflags |= sflag_e;
if ((tieflags & tief_dashed) != 0) slurflags |= sflag_i;
if ((tieflags & tief_dotted) != 0) slurflags |= sflag_i | sflag_idot;

/* Collect all the flags from the chord, for staccato etc. We have to do this
because the notes may be in either order. */

do
  {
  flags |= left->flags;
  acflags |= left->acflags;
  mac_advancechord(left);
  }
while (left->type == b_chord);

/* Set start position for continuations at start of line */

if (x0 == 0)
  {
  x0 = out_barx - 10*main_stavemagn;
  continued = TRUE;
  }
else continued = FALSE;

/* Process each note in the chord. We scan the right-hand side chord (supplied
in notelist), and tie only those notes which correspond in pitch with a note in
the left-hand side. */

for (count = 0; count < notecount; count++)
  {
  int found = TRUE;
  b_notestr *right = notelist[count];

  left = n_prevtie->note;
  while (right->truepitch != left->truepitch)
    {
    mac_advancechord(left);
    if (left->type != b_chord) { found = FALSE; break; }
    }

  if (found)
    {
    int below = (tiecount > 0 && !n_upflag) || (tiecount <= 0 && n_upflag);
    int y0 = right->spitch;
    int xx0, xx1;
    int adjustL = 0;
    int adjustR = 0;
    int dstart = 0;
    int dstop = 1000;
    int type;

    mac_couplepitch(y0, right->flags);
    if (!below) y0 += 8;

    /* For each tie, we must determine whether it is inside or outside the
    chord. A tie is outside if it is EITHER

      (a) the last one, and in the "normal" direction, unless the stems
          are in opposite directions and the left one is up, in which
          case it is "half outside"

      (b) the first one, and in the abnormal direction, AND
          EITHER the first note has no stem
            OR the first note's stem is down or we are at a line start
          AND
          EITHER the second note has no stem
            OR the stem is up
            OR we are at a line end (for which "no stem" is in fact set)

    There are two cases where notes are "half outside", which occur when case
    (b) is true except that a note required not to have a stem does in fact
    have one. We encode the cases in the variable "type" as follows:

      0 => outside
      1 => inside
      2 => right inside
      3 => left inside

    because otherwise the code is tortuous and repetitious.

    Another "half outside" case has come to light because an end note of
    the right-hand chord may match a middle note of the left-hand chord. */

    /* Handle tie in normal direction for the last note */

    if ((count == notecount - 1 && tiecount <= 0))
      {
      type = (leftup == n_upflag || !leftup || (flags & nf_stem) == 0)? 0 : 3;

      /* Deal with non-end left-hand note */
      if (type == 0 && leftup)
        {
        b_notestr *nleft = left;
        mac_advancechord(nleft);
        if (nleft->type == b_chord) type = 3;
        }
      }

    /* Handle tie in abnormal direction for the first note */

    else if (count == 0 && tiecount > 0)
      {
      if ((flags & nf_stem) == 0 || !leftup || continued)
        {
        type = ((n_flags & nf_stem) == 0 || n_upflag)? 0 : 2;

        if (type == 0 && !endflag &&
          right->spitch - (notelist[count+1])->spitch == -2)
            type = 2;
        }
      else type = !n_upflag? 2 : 3;
      }

    /* All other cases are inside */

    else type = 1;

    /* When printing right-to-left, things change! */

    if (main_righttoleft) switch (type)
      {
      case 0:
      if (n_upflag && !leftup) type = below? 3: 2;
      break;

      case 2:
      if (!n_upflag) type = leftup? 0 : 3;
      break;

      case 3:
      if (leftup) type = n_upflag? 2 : 0;
      break;

      default:
      break;
      }

    /* Now make adjustments according to the type */

    switch (type)
      {
      case 0:                   /* outside */
      case 2:                   /* right inside */
      adjustR = (type == 0)? -joinedties : -3750;

      /* Check for dynamics on the first chord and adjust the position if
      necessary. (We assume no dynamics on second chord). */

      if ((count == 0 && leftup != n_upflag) ||
          (count == notecount - 1 && leftup == n_upflag))
        {
        if ((acflags & af_dyninside) != 0 && (acflags & af_opposite) == 0)
          {
          int z = 6 - (y0 & 2);
          if (leftup)
            y0 -= (y0 <= 128)? 4 : z;
          else
            y0 += (y0 >= 152)? 4 : z;
          }
        }
      break;

      case 1:                   /* inside */
      case 3:                   /* left inside */
      if (!continued)
        {
        adjustL = 4500;
        if ((flags & nf_plus) != 0) adjustL += 8000;
          else if ((flags & nf_dot) != 0)
            {
            adjustL += 4000;
            if ((flags & nf_dot2) != 0) adjustL += 3500;
            }
        }

      if (type == 3)
        {
        adjustR = -joinedties;
        break;       /* that's all for left inside */
        }

      if (!endflag) adjustR = -3750;

      /* Deal with dots moved right (will apply only to stems up) */

      if (!continued &&
        (flags & (nf_dot | nf_dotright)) == (nf_dot | nf_dotright))
          adjustL += 5500;

      /* Else deal with intervals of a second */

      else if (n_upflag)
        {
        if (!continued && count != notecount - 1 &&
          right->spitch - (notelist[count+1])->spitch == 2)
            adjustL += 5500;
        }
      else
        {
        if (!endflag &&
             ((count != notecount - 1 && right->spitch - (notelist[count+1])->spitch == -2) ||
              (count != 0 && tiecount > 0 && right->spitch - (notelist[count-1])->spitch == 2)))
          adjustR -= 5500;
        }

      /* Inside ties are adjusted for pitch */

      y0 += below? 3 : -3;
      break;
      }

    /* If notes are currently switched off, we have just printed an invisible
    note. Cancel any horizontal adjustment. This fudge is a cunning way of
    printing hanging ties. */

    if ((bar_cont->flags & cf_notes) == 0) adjustR = 0;

    /* Correct adjustments for stave magnification */

    adjustL = (adjustL*main_stavemagn)/1000;
    adjustR = (adjustR*main_stavemagn)/1000;

    /* Now make sure the tie does not end on a staff line, and convert from a
    pitch value to a points offset. */

    if ((y0 & 3) == 0)
      {
      if (below)
        {
        if (y0 >= 128) y0--;
        }
      else if (y0 <= 144) y0++;
      }

    /* Set up the final coordinates, taking into account the style of continued
    ties, and then output it. */

    xx0 = x0 + adjustL;
    xx1 = x1 + adjustR;
    y0 = (y0 - 132)*main_stavemagn;

    if (endflag && curmovt->endlinetiestyle != 0)
      {
      xx1 += xx1 - xx0;
      dstop = 500;
      }
    else if (continued && curmovt->endlinetiestyle != 0)
      {
      xx0 -= xx1 - xx0;
      dstart = 500;
      }

    out_slur(xx0, y0, xx1, y0,
      (below? sflag_b : 0) | slurflags,
        (xx1 - xx0 > 10000)? (-main_stavemagn) : 0, dstart, dstop);
    }

  /* Adjust the count of those on the "abnormal" side, whether or not we
  actually printed a tie. */

  tiecount--;
  }
}


/*************************************************
*               Output a glissando mark          *
*************************************************/

/* The positions of the ends of the line are adjusted according to whether the
notes are on lines or spaces, and whether or not the right hand one has
accidentals.

Arguments:
  x1          the x coordinate of the end of the mark
  flags       type of line required (editorial, dashed, dotted)

Returns:      nothing
*/

void
out_glissando(int x1, int flags)
{
b_notestr *left = n_prevtie->note;

int x0 = bar_cont->tiex;
int y0 = left->spitch;
int y1 = n_pitch;

/* Check for coupling of first note */

mac_couplepitch(y0, left->flags);

if (x0 == 0)    /* at start of line */
  {
  x0 = out_barx - 6*main_stavemagn;
  y0 = (y0+y1)/2;
  }
else x0 += 8*main_stavemagn;

if ((left->flags & nf_dot) != 0)
  {
  x0 += 4*main_stavemagn;
  if ((left->flags & nf_dot2) != 0) x0 += (35*main_stavemagn)/10;
  }

x1 -= (15*main_stavemagn)/10 + n_accleft;

if ((y0 & 2) == 0) y0 += (y1 > y0)? (+2) : (-2);
if ((y1 & 2) == 0) y1 += (y1 > y0)? (-2) : (+2);

ps_line(x0, (y0 - 128)*main_stavemagn,
  x1, (y1 - 128)*main_stavemagn, (3*main_stavemagn)/10, flags);
}

/* End of settie.c */
