// javascript to convert .bww to .abc
// Copyright (C) 2018-2023 Jean-Francois Moine
// License GPL3+
//
// documentation about BWW:
//	BNF http://forums.bobdunsire.com/forums/showthread.php?t=123219
//	Bagpipe_Reader.pdf

// grace notes adapted from
// https://raw.githubusercontent.com/ChurchOrganist/MuseScore/master/bww2mxml/lexer.cpp

// This script used to work with Mozilla javascript interpreters (js24...)
// Missing functions may be added at the end of this script for other interpreters
// (done for QuickJS - qjs)

// Usage:
//	JSinterpreter this_file BWW_file > ABC_FILE
// Example:
//	qjs --std ./bww2abc great_tune.bww > great_tune.abc

var grace = {
    // Single Grace notes
	ag: "A",
	bg: "B",
	cg: "c",
	dg: "d",
	eg: "e",
	fg: "f",
	gg: "g",
	tg: "a",

    // Regular Doublings
	dblg: "gGd",
	dbla: "gAd",
	dbb: "gBd",
	dbc: "gcd",
	dbd: "gde",
	dbe: "gef",
	dbf: "gfg",
	dbhg: "gf",
	dbha: "ag",

    // Thumb Doublings
	tdblg: "aGd",
	tdbla: "aAd",
	tdbb: "aBd",
	tdbc: "acd",
	tdbd: "ade",
	tdbe: "aef",
	tdbf: "afg",

    // Half Doublings
	hdblg: "Gd",
	hdbla: "Ad",
	hdbb: "Bd",
	hdbc: "cd",
	hdbd: "de",
	hdbe: "ef",
	hdbf: "fg",
/*
	hdbhg: "gf",
	hdbha: "ag",
*/

    // Single Strikes (same as single grace notes)
	strlg: "G",
	strla: "A",
	strb: "B",
	strc: "c",
	strd: "d",
	stre: "e",
	strf: "f",
	strhg: "g",

    // G Grace note, Thumb and Half Strikes
	gstla: "gAG",
	gstb: "gBG",
	gstc: "gcG",
	gstd: "gdG",
	lgstd: "gec",
	gste: "geA",
	gstf: "gfe",

	tstla: "aAG",
	tstb: "aBG",
	tstc: "acG",
	tstd: "adG",
	ltstd: "adc",
	tste: "aeA",
	tstf: "afe",
	tsthg: "agf",

	hstla: "AG",
	hstb: "BG",	// "BgG",
	hstc: "cG",	// "cgG",
	hstd: "dG",	// "dgG",
	lhstd: "ed",
	hste: "eA",
	hstf: "fe",
	hsthg: "gf",

    // Regular Grips
	grp: "GdG",
	hgrp: "dG",
	grpb: "GBG",

    // G Grace note, Thumb and Half Grips
	ggrpla: "gAGdG",
	ggrpb: "gBGdG",
	ggrpc: "gcGdG",
	ggrpd: "gdGdG",
	ggrpdb: "gdGBG",
	ggrpe: "geGdG",
	ggrpf: "gfGfG",

	tgrpla: "aAGdG",
	tgrpb: "aBGdG",
	tgrpc: "acGdG",
	tgrpd: "adGdG",
	tgrpdb: "adGBG",
	tgrpe: "aeGdG",
	tgrpf: "afGfG",
	tgrphg: "agGfG",

	hgrpla: "AGdG",
	hgrpb: "BGdG",
	hgrpc: "cGdG",
	hgrpd: "dGdG",
	hgrpdb: "dGBG",
	hgrpe: "eGdG",
	hgrpf: "fGfG",
	hgrphg: "gGdG",
	hgrpha: "aGdG",

    // Taorluaths and Bublys
	tar: "GdGe",
	tarb: "GBGe",
	htar: "dGe",
	bubly: "GeGcG",
	hbubly: "dGcG",

    //  Birls
	brl: "GAG",
	abr: "AGAG",
	gbr: "gAGAG",
	tbr: "aAGAG",

    // Light, Heavy and Half D Throws
	thrd: "Gdc",
	hvthrd: "GdGc",
	hthrd: "dc",
	hhvthrd: "dGc",

    // Regular, Thumb Grace Note and Half Peles
	pella: "gAeAG",
	pelb: "gBeBG",
	pelc: "gcecG",
	peld: "gdedG",
	lpeld: "gdedc",
	pele: "gefea",
	pelf: "gfgfe",

	tpella: "aAeAG",
	tpelb: "aBeBG",
	tpelc: "acecG",
	tpeld: "adedG",
	ltpeld: "adedc",
	tpele: "aefeA",
	tpelf: "afgfe",
	tpelhg: "agagf",

	hpella: "AeAG",
	hpelb: "BeBG",
	hpelc: "cecG",
	hpeld: "dedG",
	lhpeld: "dedc",
	hpele: "efeA",
	hpelf: "fgfe",
	hpelhg: "gagf",

    // Regular Double Strikes
	st2la: "GAG",
	st2b: "GBG",
	st2c: "GcG",
	st2d: "GdG",
	lst2d: "cdc",
	st2e: "AeA",
	st2f: "efe",
	st2hg: "fgf",
	st2ha: "gag",

    // G Grace note, Thumb and Half Double Strikes
	gst2la: "gAGAG",
	gst2b: "gBGBG",
	gst2c: "gcGcG",
	gst2d: "gdGdG",
	lgst2d: "gdcdc",
	gst2e: "geAeA",
	gst2f: "gfefe",

	tst2la: "aAGAG",
	tst2b: "aBGBG",
	tst2c: "acGcG",
	tst2d: "adGdG",
	ltst2d: "adcdc",
	tst2e: "aeAeA",
	tst2f: "afefe",
	tst2hg: "agfgf",

	hst2la: "AGAG",
	hst2b: "BGBG",
	hst2c: "cGcG",
	hst2d: "dGdG",
	lhst2d: "dcdc",
	hst2e: "eAeA",
	hst2f: "fdfd",
	hst2hg: "gfgf",
	hst2ha: "agag",

    // Regular Triple Strikes
	st3la: "GAGAG",
	st3b: "GBGBG",
	st3c: "GcGcG",
	st3d: "GdGdG",
	lst3d: "cdcdc",
	st3e: "AeAeA",
	st3f: "efefe",
	st3hg: "fgfgf",
	st3ha: "gagag",

    // G Grace note, Thumb and Half Triple Strikes
	gst3la: "gAGAGAG",
	gst3b: "gBGBGBG",
	gst3c: "gcGcGcG",
	gst3d: "gdGdGdG",
	lgst3d: "gdcdcdc",
	gst3e: "geAeAeA",
	gst3f: "gfefefe",

	tst3la: "aAGAGAG",
	tst3b: "aBGBGBG",
	tst3c: "acGcGcG",
	tst3d: "adGdGdG",
	ltst3d: "adcdcdc",
	tst3e: "aeA2AeA",
	tst3f: "afefefe",
	tst3hg: "agfgfgf",

	hst3la: "AGAGAG",
	hst3b: "BGBGBG",
	hst3c: "cGcGcG",
	hst3d: "dGdGdG",
	lhst3d: "dcdcdc",
	hst3e: "eAeAEA",
	hst3f: "fefefe",
	hst3hg: "gfgfgf",
	hst3ha: "agagag",

    // Double Grace notes
	dlg: "dG",
	dla: "dA",
	db: "dB",
	dc: "dc",
	elg: "eG",
	ela: "eA",
	eb: "eB",
	ec: "ec",
	ed: "ed",

	flg: "fG",
	fla: "fA",
	fb: "fB",
	fc: "fc",
	fd: "fd",
	fe: "fe",

	glg: "gG",
	gla: "gA",
	gb: "gB",
	gc: "gc",
	gd: "gd",
	ge: "ge",
	gf: "gf",

	tlg: "aG",
	tla: "aA",
	tb: "aB",
	tc: "ac",
	td: "ad",
	te: "ae",
	tf: "af",
	thg: "ag",

	// cadences
	cadged: "ge4d",
	cadge: "ge4",
	caded: "e4d",
	cade: "e4",
	cadaed: "ae4d",
	cadae: "ae4",

	fcadged: "gHe4d",
	fcadge: "gHe4",
	fcaded: "He4d",
	fcade: "He4",
	fcadaed: "aHe4d",
	fcadae: "aHe4",

	cadgf: "gf4",
	cadaf: "af4",
	fcadgf: "gHf4",
	fcadaf: "aHf4",

	// E, F and High G Throws
	embari: "eAfA",
	endari: "fege",
	chedari: "geae",	// ??
//	hedari: "???",

	// High A and D Throws
	dili: "ag",
	tra: "G2dc",
	htra: "dc",
	tra8: "G2dc",

	// G Grace note, Thumb and Half Throws
	gedre: "geAfA",
	gdare: "gfege",
	tedre: "aeAfA",
	tdare: "afege",
	tchechere: "ageae",
	dre: "AfA",
	hedale: "ege",
	hchechere: "eae",

	// Grips
//	grp: "GdG",
	deda: "GeG",

	// Echo Beat Grace notes
	echolg: "G2",
	echola: "A2",
	echob: "B2",
	echoc: "c2",
	echod: "d2",
	echoe: "e2",
	echof: "f2",
	echohg: "g2",
	echoha: "a2",

	darodo: "GdGcG",
	darodo16: "G2dGcG2",
	hdarodo: "dGcG",

	hiharin: "dAGAG",
	rodin: "GBG",
	chelalho: "f4de",
	din: "G2"
}

var deco = {
	pembari: "P",
	pendari: "P",
	pechedari: "P",
	pehedari: "P",

	pdili: "!trill!",
	ptra: "!trill!",
	phtra: "!trill!",
	ptra8: "!trill!",

	pgrp: "!trill!",

	pdarodo: "!turn!",
	pdarodo16: "!turn!",
	phdarodo: "!turn!",

	phiharin: "P",

	fine: "!fine!y",
	dacapoalfine: "!D.C.alfine!y",
	coda: "O",
	dacapoalcoda: "!D.C.alcoda!y",
	codasection: "O"
}

function main(args) {

	if (!args[0]) {
		printErr('Convert BWW to ABC\n\
Usage:\n\
    ./bww2abc file.bww > file.abc')
		quit()
	}

    var	i, j, l, low, t, n, key, tie, acc, beam, fermata,
	o = '',
	p = read(args[1]).split(/[\r\n]/)

	// header
	print('%abc-2.2\n\
% Converted from ' + args[1] + ' to ABC by bww2abc\n\
\n\
X:1')
	for (i = 0; i < p.length; i++) {
		l = p[i].trim()
		if (!l)
			continue
		switch (l[0]) {
		default: continue
		case '"':
			j = l.indexOf('"', 1)
			if (j <= 0)
				continue
			t = l.slice(1, j)
			if (!t)
				continue
			if (l[j + 1] != ',') {
				print('% ' + t)
				continue
			}
			switch (l[j + 3]) {
			case 'T':
				print('T:' + t)
				continue 
			case 'M':
				print('C:' + t)
				continue 
			case 'Y':
				print('R:' + t)
				continue 
			case 'F':
				print('%%footer \t' + t)
				continue 
			}
			continue
		case 'T':
			t = l.split(',')
			if (t[0] == 'TuneTempo')
				print('Q:1/4=' + t[1])
			continue
		case '&':			// clef = start of music

			//get the accidentals
			t = l.split(/\s+/);
			key = ''
			for (j = 0; j < t.length; j++) {
				l = t[j].match(/(sharp|flat|natural)(.)/)
				if (!l)
					continue
				if (key)
					key += ' '
				switch (l[1]) {
				case 'sharp':
					key += '^'
					break
				case 'flat':
					key += '_'
					break
				default:
					key += '='
					break
				}
				switch (l[2]) {
				case 'hg':
					key += 'g'
					break
				case 'ha':
					key += 'a'
					break
				case 'lg':
					key += 'G'
					break
				case 'la':
					key += 'A'
					break
				default:
					key += l[2]
					break
				}
			}

			// get the measure
			t = t[t.length - 1]	// the measure is the last word
			if (!/(C|C_|\d+_\d+)/.test(t)) { // or in the next line
				l = p[++i].trim();
				t = l.split(/\s+/)[0]
				if (!/(C|C_|\d+_\d+)/.test(t)) {
					print('M:2/4')
					break
				}
			}
			if (t == 'C_')
				print('M:C|')
			else
				print('M:' + t.replace('_', '/'));
			i++
			break
		}
		break
	}
	print('L:1/8\n\
K:Hp exp ' + key)

	// music
	for ( ; i < p.length; i++) {
		l = p[i]
		if (!l || l[0] == '&')
			continue
		l = l.trim().split(/\s+/)
		for (j = 0; j < l.length; j++) {
			t = l[j];
			low = false;
			switch (t[0]) {
			case "'":
				switch (t) {
				case "''!I":
					print(o +  ' :|');
					o = ''
					continue
				case "'intro":
					o += '["Introduction"'
					continue
				}

				// "'[0-9]+" = repeat
				o += '[';
				t = t.slice(1)
				if (t.length == 1) {
					o += t
				} else if (t.length == 2) {
					o += '"' + t[0] + ' of ' + t[1] + '"'
				} else {
					o += '"' + t[0] + ' of ' + t[1] +
						' & ' + t[2] + '"'
				}
				continue
			case "_":
				o += ']'
				continue
			case '!':
				switch (t) {
				case '!t':
					print(o + ' |');
					o = ''
					continue
				case '!!t':
					print(o + ' ||');
					o = ''
					continue
				case '!I':
					print(o + ' |]');
					o = ''
					continue
				}
				o += '| '
				continue
			case 'I':
				switch (t) {
				case "I!''":
					o += '|: '
					continue
				case "I!":
					o += '|| '
					continue
				}
				continue
			case 'H':
				low = false;
				t = t.slice(1)
				break
			case 'L':
				low = true;
				t = t.slice(1)
				break
			case 'R':
				o += 'z';
				u = t.replace('REST_', '')
				switch (Number(u)) {
				case 1: o += '8'; break
				case 2: o += '4'; break
				case 4: o += '2'; break
//				case 8: o += ''; break
				case 16: o += '/'; break
				case 32: o += '//'; break
				}
				continue
			case '^':
				switch (t[1]) {
				case 't':		// tie
					if (t[2] == 's')	// new format
						tie = true
					if (t[2] != 'e')	// old format
						o += '-'
//					else
//						conflict
					break
				case '2':		// duplet
				case '3':		// triplet
					if (t[2] == 'e')
						break
					o += '(' + t[1]
					break
				case '4':
				case '5':
				case '6':
				case '7':
					if (t[2] == 'e')
						break
					o += '(' + t[1][0] + ':' + t[1][1]
					break
				}
				continue
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '9':
				if (/\d+_\d+/.test(t)) {
					o += '[M:' + t.replace('_', '/') + ']'
					continue
				}
				break
			case 'C':
				switch (t) {
				case 'C':
					o += '[M:C]'
					continue
				case 'C_':
					o += '[M:C|]'
					continue
				}
				break
			case 'T':
				u = t.split(',')
				if (u[0] == 'TuneTempo') {
					o += '[Q:1/4=' + u[1] + ']'
					continue
				}
				break
			}
			u = t.match(/([ABCDEFG])([rl]?)_(\d+)/)
			if (!u) {
				if (grace[t]) {
					o += '{'+ grace[t] + '}'
					continue
				}
				if (deco[t]) {
					o += deco[t]
					continue
				}

//fixme
if(!t[j])printErr('t:'+t)

				u = t[j].match(/(sharp|flat|natural)(.)/)
				if (u) {
					switch(u[1]) {
					case 'sharp':
						acc = '^'
						break
					case 'flat':
						acc = '_'
						break
					default:
						acc = '='
						break
					}
					continue
				}

printErr('*** ' + t + ' not found ***')
				continue
			}

			if (acc) {			// accidental
				o += acc;
				acc = ''
			}
			if (j < l.length - 1
			 && /fermat/.test(l[j + 1])) {	// fermata
				fermata = true;
				o += 'H'
			}

			if (u[1] == 'B')
				low = true;

			o += low ? u[1] : u[1].toLowerCase()
			if (j < l.length - 1
			 && /''?[abcdefghl]/.test(l[j + 1])) {	// dot(s)
				if (l[++j][1] == "'") {
					switch (Number(u[3])) {
					case 1: o += '14'; break
					case 2: o += '7'; break
					case 4: o += '7/'; break
					case 8: o += '7//'; break
					case 16: o += '7///'; break
					case 32: o += '7////'; break
					}
				} else {
					switch (Number(u[3])) {
					case 1: o += '12'; break
					case 2: o += '6'; break
					case 4: o += '3'; break
					case 8: o += '3/'; break
					case 16: o += '3//'; break
					case 32: o += '3///'; break
					}
				}
			} else {
				switch (Number(u[3])) {
				case 1: o += '8'; break
				case 2: o += '4'; break
				case 4: o += '2'; break
//				case 8: o += ''; break
				case 16: o += '/'; break
				case 32: o += '//'; break
				}
			}
			if (tie) {
				o += '-';
				tie = false
			}
			if (Number(u[3]) >= 8) {	// if a stem
				switch (u[2]) {
				case 'l':
					beam = false
					break
				case 'r':
					beam = true
					break
				}
				if (!beam)
					o += ' '
			}
			if (fermata) {
				j++;
				fermata = false
			}
		}
	}
	if (o)
		print(o)
} // main

// -- define here the functions missing in your javascript interpreter --
if (typeof read != "function") {
// --- qjs functions ---
	read = std.loadFile
	printErr = function(s) {
		std.err.printf("%s\n", s)
	}
}

main(scriptArgs)