const express = require('express');
const auth = require('../middleware/authenticator.js');
const verify = require("../middleware/verifier.js");
const mysql = require("../db/mysql.js");
const alphaProcesser = require('../AlphaTab/ProcessAlphaTab.js');
const Manipulator = require("../Parser/ParsedOutputManipulator.js");
const ParserPData = require("../Parser/ParserPerformanceData.js");
const ParserMeasures = require("../Parser/ParserMeasures.js");
const updateExerciseTable = require("../util/updateExercise.js");
const router = express.Router();
/**
* @typedef {object} SheetMusicPartGetPackage
* @property {string} sheet_music The created AlphaTex isolating the user's part
* @property {string[]} part_list A list of part (i.e. track) names in the generated AlphaTex
* @property {string[]} clefs Clefs per staff
* @property {number[]} performance_expectation A 1D array of even size with the ith index as the midi value and the i+1 index as the duration of that note for i%2==0
* @property {number[]} lower_upper A 1D two valued array with the lower and upper midi values
* @property {number[]} measure_lengths A collection of the lengths of each Measure. The ith index contains the length in seconds of the i+1 Measure
* @property {string} exerciseId The ID of the exercise created
*/
/**
* Route for getting only the user's part from the given sheet music. Creates AlphaTex with their part isolated
* @name sheet-music-part/get
* @function
* @inner
* @param {*} req
* @param {string} req.query.sheetMusicId The sheet music ID to be accessed
* @param {*} res
* @returns {SheetMusicPartGetPackage|string} The created isolated sheet music and information about it. Otherwise, an error message if an error occured
* @async
*/
router.get('/', auth.token, async (req,res) => {
verify.memberOfSheetMusic(req, res).then((choirId) => {
if (choirId === null) {
res.status(403).send('Unauthorized');
} else {
let sheetMusicId = req.query.sheetMusicId;
const errorMessage = "Unable to get single part from sheet music";
if (sheetMusicId) {
mysql.getClient().then((client) => {
client.query("SELECT hex(member_id) AS member_id FROM tma.member WHERE person_id=? AND choir_id=unhex(?)", [res.locals.uid, choirId], function(error, results, fields) {
if (error) {
res.status(503).send(errorMessage);
} else if (results.length === 0) {
res.status(204).send(errorMessage);
} else {
let memberId = results[0]["member_id"];
client.query("SELECT part FROM tma.member_sheet_music_part WHERE member_id=unhex(?) AND sheet_music_id=unhex(?)", [memberId, sheetMusicId], function(error, results, fields) {
if (error) {
res.status(503).send(errorMessage);
} else if (results.length === 0) {
res.status(204).send(errorMessage);
} else {
let partName = results[0]["part"];
client.query("SELECT performance_data FROM tma.sheet_music WHERE sheet_music_id = unhex(?)", [sheetMusicId], function(error, results, fields) {
if (error) {
res.status(503).send(errorMessage);
} else {
let performanceData = JSON.parse(results[0]['performance_data']);
const measureParser = new ParserMeasures(performanceData);
let measureEnd = measureParser.manipulator.getMeasureEnd(partName, 1);
let trackIndex = measureParser.manipulator.getTrackIndexByName(partName);
if (measureEnd === null) {
res.status(400).send(errorMessage);
} else {
let {lyricText, alphaTex} = measureParser.measuresToAlphaTex([1, measureEnd, "all"], trackIndex + 1, 1, false);
let exercisePerformanceData = alphaProcesser.process(alphaTex, {0:lyricText});
let partNames = ParserPData.getPartNames(exercisePerformanceData.mainObj);
let clefs = ParserPData.getClefs(exercisePerformanceData.mainObj);
let noteStream = ParserPData.getNoteStream(exercisePerformanceData.mainObj, partName);
let lowerAndUpper = ParserPData.getLowerAndUpper(noteStream);
let manipulator = new Manipulator(exercisePerformanceData.mainObj);
let measureLengths = manipulator.getMeasureTimeLength(partName);
let retObj = {
"sheet_music": alphaTex,
"part_list": partNames,
clefs,
"performance_expectation": noteStream,
"lower_upper": lowerAndUpper,
"measure_lengths": measureLengths,
};
updateExerciseTable(sheetMusicId, trackIndex + 1, 0, 1, measureEnd, false).then((exerciseId) => {
retObj.exerciseId = exerciseId;
res.status(200).json(retObj);
});
}
}
});
}
});
}
});
});
} else {
res.status(400).send(errorMessage);
}
}
}).catch((error) => {
res.status(403).send('Unauthorized');
});
});
/**
* Route for assigning a part to a user for a piece of sheet music
* @name sheet-music-part/post
* @function
* @inner
* @param {*} req
* @param {string} req.body.memberId The member ID to be accessed
* @param {string} req.body.sheetMusicId The sheet music ID for the part
* @param {string} req.body.part The part to be assigned to the member
* @param {*} res
* @returns {string} An error message if an error occured
* @async
*/
router.post('/', auth.token, async (req,res) => {
verify.userIsMember(req, res).then((isMember) => {
if (!isMember) {
res.status(403).send('Unauthorized');
} else {
verify.memberOfSheetMusic(req, res).then((choirId) => {
if (choirId === null) {
res.status(403).send('Unauthorized');
} else {
let sheetMusicId = req.body.sheetMusicId;
let part = req.body.part;
let memberId = req.body.memberId;
const errorMessage = "Unable to add part for member in sheet music";
if (sheetMusicId && part && memberId) {
mysql.getClient().then((client) => {
client.beginTransaction(function(error) {
client.query("INSERT INTO tma.member_sheet_music_part (member_id, sheet_music_id, part) values(unhex(?), unhex(?), ?)", [memberId, sheetMusicId, part], function(error, results, fields) {
if (error) {
res.status(503).send(errorMessage);
} else {
client.query("SELECT performance_data FROM tma.sheet_music WHERE sheet_music_id = unhex(?)", [sheetMusicId], function(error, results, fields) {
if (error) {
client.rollback(function() {
res.status(503).send(errorMessage);
});
} else {
let performanceData = JSON.parse(results[0]['performance_data']);
let {progressData, trackIndex} = ParserPData.createEmptyProgressData(performanceData, part);
let progress = {
index: 0,
data: [progressData]
};
client.query("INSERT INTO tma.performance_progress (performance_progress_id, member_id, sheet_music_id, staff_number, progress_data) values(unhex(replace(uuid(),\"-\",\"\")), unhex(?), unhex(?), ?, ?)", [memberId, sheetMusicId, trackIndex+1, JSON.stringify(progress)], function(error, results, fields) {
if (error) {
client.rollback(function() {
res.status(503).send(errorMessage);
});
} else {
client.commit(function(err) {
if (err) {
client.rollback(function() {
res.status(503).send(errorMessage);
});
} else {
res.status(200).send();
}
});
}
});
}
});
}
});
});
});
} else {
res.status(400).send(errorMessage);
}
}
}).catch((error) => {
res.status(403).send('Unauthorized');
});
}
}).catch((error) => {
res.status(403).send('Unauthorized');
});
});
module.exports = router;