// break.js - module to handle the %%break command
//
// Copyright (C) 2018-2024 Jean-Francois Moine
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded when "%%break" appears in a ABC source.
//
// Parameters
//	%%break measure_nb [":" num "/" den] [" " measure ...]*

if (typeof abc2svg == "undefined")
    var	abc2svg = {}

abc2svg.break = {

	// get the %%break parameters
	get_break: function(parm) {
	    var	C = abc2svg.C,
		b, c, d, n, sq,
		a = parm.split(/[ ,]/),
		cfmt = this.cfmt()

		if (!cfmt.break)
			cfmt.break = []
		for (n = 1; n < a.length; n++) {
			b = a[n];
			c = b.match(/(\d+)([a-z]?)(:\d+\/\d+)?/)
			if (!c) {
				this.syntax(1, this.errs.bad_val, "%%break")
				continue
			}
			if (c[2])
				sq = c[2].charCodeAt(0) - 0x61
			if (!c[3]) {
				cfmt.break.push({	// on measure bar
						m: c[1],
						t: 0,
						sq: sq})
				continue
			}
			d = c[3].match(/:(\d+)\/(\d+)/)
			if (!d || d[2] < 1) {
				this.syntax(1, "Bad denominator in %%break")
				continue
			}
			cfmt.break.push({
					m: c[1],
					t: d[1] * C.BLEN / d[2],
					sq: sq})
		}
	}, // get_break()

	// insert the EOLs of %%break
	do_break: function() {
	    var	i, m, t, brk, s, seq,
		voice_tb = this.get_voice_tb(),
		v = this.get_cur_sy().top_voice,
		s1 = voice_tb[v].sym,
		cfmt = this.cfmt()

		for (i = 0; i < cfmt.break.length; i++) {
			brk = cfmt.break[i];
			m = brk.m
			for (s = s1; s; s = s.next) {
				if (s.bar_num == m)
					break
			}
			if (!s)
				continue

			if (brk.sq) {
				seq = brk.sq
				for (s = s.ts_next; s; s = s.ts_next) {
					if (s.bar_num == m) {
						if (--seq == 0)
							break
					}
				}
				if (!s)
					continue
			}

			t = brk.t
			if (t) {
				t = s.time + t
				for ( ; s; s = s.next) {
					if (s.time >= t)
						break
				}
			} else {
				s = s.next
			}
			if (s)
				s.soln = true
		}
	}, // do_break()

    do_pscom: function (of, text) {
	if (text.slice(0, 6) == "break ")
		abc2svg.break.get_break.call(this, text)
	else
		of(text)
    },

    set_bar_num: function(of) {
	of()
	if (this.cfmt().break)
		abc2svg.break.do_break.call(this)
    },

    set_hooks: function(abc) {
	abc.do_pscom = abc2svg.break.do_pscom.bind(abc, abc.do_pscom);
	abc.set_bar_num = abc2svg.break.set_bar_num.bind(abc, abc.set_bar_num)
    }
} // break

if (!abc2svg.mhooks)
	abc2svg.mhooks = {}
abc2svg.mhooks.break = abc2svg.break.set_hooks