#include "post_mus.h"

#define MAXBEAMN 20

float   setslope ();

setbeam (bar, beamslope) {	/* if beamslope, take previous and next
				   notes into a/c */
    int     x[MAXBEAMN],
            y[MAXBEAMN],
            i,
            n;			/* max of MAXBEAMN notes */
    float   m,
            c,
            cmin,
            cmax;		/* beam will be y = m * (x-x[0]) + c */
    int     start,
            fin,
            maxy,
            miny,
            l = 0,
            maxl,
            minh,
            up,
            both,
            first = barv[bar].b_first_note,
            beat = first,
            last = barv[bar].b_last_note;
    int     bottom = 0,
            top = 0;
    struct notes   *notep = notev + beat;
    int     from = 1;
    int     slopeform = beamslope / 10,
            slopespread = beamslope % 10;
 /* scan bar looking for beams */
    do {
	start = -1;
    /* look for start of beam marker */
	while (beat < last && notep -> n_beam <= 0) {
	    beat++;
	    notep++;
	}
	if (beat < last) {	/* found one */
	    start = fin = beat;
	    n = 1;
	    bottom = top = 0;
	    maxl = notep -> n_length;
	    minh = notep -> n_hooks;
	    maxy = miny = notep -> n_y_wrt_staff;
	    both = 0;		/* set if both up and down sticks occur */
	    up = notep -> stickup;
	/* scan for end of beam marker */
	    do {
		if (up * notep -> stickup < 0)
		    both = 1;
		if (up + notep -> stickup > 0)
		    up = 1;
		if (up + notep -> stickup < 0)
		    up = -1;
		if (up + notep -> stickup == 0)
		    up = 0;
		x[n] = notep -> n_x_wrt_bar + notep -> n_head_x;
		y[n++] = notep -> n_y_wrt_staff;
		if (n >= MAXBEAMN) {
		    ioerror ("Too many notes under beam %d bar %d",
			    MAXBEAMN, bar);
		    abandon (1);
		}
	    /* max note length */
		l = notep -> n_length;
		if (l > maxl)
		    maxl = l;
	    /* min no of hooks */
		l = notep -> n_hooks;
		if (l > minh)
		    minh = l;
	    /* max and min y-values */
		l = notep -> n_y_wrt_staff;
		if (l > maxy)
		    maxy = l;
		if (l < miny)
		    miny = l;
		l = notep -> n_beam;
		beat++;
		notep++;
	    } while (beat < last && l >= 0);

	    if (beat == last && l >= 0) {
		ioerror ("****** Error no beamend bar %d beat %d\n",
			bar, beat - barv[bar].b_first_note + 1);
		return;
	    }
	    fin = beat;

	/* to include previous and next notes */
	    if (slopespread) {
		if ((start > first ||
			    bar > 0
			    && barv[bar - 1].b_end_type == BLPLAIN)
			&& (slopespread > 1 || notev[start - 1].n_beam == -1)
		    ) {
		    bottom++;
		    x[0] = (start > first ? 0 : -barv[bar - 1].b_width)
			+ notev[start - 1].n_x_wrt_bar
			+ notev[start - 1].n_head_x;
		    y[0] = notev[start - 1].n_y_wrt_staff;
/*
fprintf( stderr, "%d bar %d first %d start %d note %d bottom\n",
		slopespread, bar, first, start-first+1, start-first );
*/
		}
		if ((fin < last ||
			    bar < piecev.p_bars - 1
			    && barv[bar].b_end_type == BLPLAIN)
			&& (slopespread > 1 || notev[fin].n_beam == 1)
		    ) {
		    top++;
		    x[n] = (fin < last ? 0 : barv[bar].b_width)
			+ notev[fin].n_x_wrt_bar + notev[fin].n_head_x;
		    y[n] = notev[fin].n_y_wrt_staff;
/*
fprintf( stderr, "%d bar %d first %d start %d note %d top\n",
		slopespread, bar, first, start-first+1, fin-first+1 );
*/
		}
	    }
	    if (n > MAXBEAMN)
		ioerror ("Too many notes in beam", 0, 0);

	/* determine slope of beam */
	    m = setslope (x, y, from, n, bar, beat,
		    slopeform, slopespread, bottom, top);

	    if (both || up == 0)
		up = (miny + maxy) / 2 <= up_down_border;
	    else
		up = up > 0;

	/* adjust x co-ordinates, minimise stick length */
	    cmax = cmin = y[from];
	    for (i = from; i < n; i++) {
/*		  if ( up ) x[i] += atom[ SOLID ].n_width; */
		c = y[i] - m * (x[i] - x[from]);
		if (c > cmax)
		    cmax = c;
		else
		    if (c < cmin)
			cmin = c;
	    }
	    if (set_monitor == 4) {
		fprintf (stderr, "Beam [%d,%d] (%d), len %d hk %d, miny %d maxy %d,",
			start, fin - 1, n, maxl, minh, miny, maxy);
		fprintf (stderr, " m %5.2f, cmin %5.2f cmax %5.2f\n",
			m, cmin, cmax);
	    }

	/* set "stickup" and "n_y_end_stick"s for this beam */
	    for (beat = start, notep = notev + beat; beat < fin; beat++, notep++) {
		c = m * (x[beat - start + from] - x[from]);
		if (up) {	/* sticks up */
		    notep -> stickup = 1;
		    notep -> n_x_stick = notep -> n_head_x
			+ notewidth (beat);
		    notep -> n_y_end_stick =
			c + cmax + atom[STICK].height - 3;
		    if (notep -> n_y_end_stick > notep -> n_yover)
			notep -> n_yover = notep -> n_y_end_stick;
		    if (notep -> n_y_end_stick > barv[bar].b_maxy)
			barv[bar].b_maxy = notep -> n_y_end_stick;
		}
		else {		/* sticks down */
		    notep -> stickup = -1;
		    notep -> n_x_stick = notep -> n_head_x;
		    notep -> n_y_end_stick =
			c + cmin - atom[STICK].height + 4;
		    if (notep -> n_y_end_stick < notep -> n_yunder)
			notep -> n_yunder = notep -> n_y_end_stick;
		    if (notep -> n_y_end_stick < barv[bar].b_miny)
			barv[bar].b_miny = notep -> n_y_end_stick;
		}

		if (set_monitor == 5) {
		    fprintf (stderr, "beamed note %d(%d) [%d,%d]",
			    beat, part_now, notev[beat].n_x_wrt_bar, notev[beat].n_y_wrt_staff);
		    fprintf (stderr, " wid %d, up %d end %d",
			    notev[beat].n_width, notev[beat].stickup, notev[beat].n_y_end_stick);
		    fprintf (stderr, "\n");
		}
	    }			/* for beat = start to fin */

	}			/* if beam found */

    }
    while (start >= 0);
}

float   setslope (x, y, from, n, bar, beat,
            slopeform, slopespread, bottom, top)
int    *x,
       *y;
/* set slope for beam, co-ords x,y[from-bottom]..x,y[n-1+top]
 * slopeform determines formula
 * slopespread determines number of notes considered
 * bottom is number of notes added at left
 */
{
    int     i,
            j;
    float   num = 0.0,
            denom = 0.0;
    if (bottom)
	from -= bottom;
    if (top)
	n += top;
    switch (slopeform) {
	case 0: 
	    j = n - 1;
	    for (i = from; i < j; i++, j--) {
		num += y[j] - y[i];
		denom += x[j] - x[i];
	    }
	    denom *= 2.0;
	    break;
	default: 
	    ioerror ("Slope formula number %d not recognised", slopeform, 0);
    }

    if (denom == 0) {
	ioerror ("Denominator zero in beam bar %d from note %d",
		bar,
		beat - barv[beat].b_first_note + 1);
	return 0.0;
    }
    return num / denom;
}
