package org.herac.tuxguitar.io.ptb;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.herac.tuxguitar.io.ptb.base.PTBar;
import org.herac.tuxguitar.io.ptb.base.PTComponent;
import org.herac.tuxguitar.io.ptb.base.PTDirection;
import org.herac.tuxguitar.io.ptb.base.PTPosition;
import org.herac.tuxguitar.io.ptb.base.PTSection;
import org.herac.tuxguitar.io.ptb.base.PTSong;
import org.herac.tuxguitar.io.ptb.base.PTSongInfo;
import org.herac.tuxguitar.io.ptb.base.PTSymbol;
import org.herac.tuxguitar.io.ptb.base.PTTrack;
import org.herac.tuxguitar.io.ptb.base.PTTrackInfo;
public class PTSongSynchronizerUtil {
private static class PTIndex {
/** Index Of Component **/
protected int c;
/** Index Of Position **/
protected int p;
/** Index Of Section **/
protected int s;
protected PTIndex(int s, int p, int c) {
this.s = s;
this.p = p;
this.c = c;
}
}
private static class PTSongSynchronizerData {
/** Active Symbol To Search Directions ( D.C, D.S, D.SS ) **/
protected int findActiveSymbol;
/** Define if there is an Alternative Ending Running **/
protected boolean repeatAlternative;
/** Define If Repeat Is In Progress **/
protected boolean repeatInProgress;
/** Current Repetition Number **/
protected int repeatNumber;
/** Current Repeat Start Index **/
protected PTIndex repeatStart;
/** Define If Next Components Should Be Skipped **/
protected boolean skip;
protected PTSongSynchronizerData() {
this.repeatStart = null;
this.repeatNumber = 0;
this.repeatAlternative = false;
this.skip = false;
}
}
private static void applyInfo(PTSongInfo src, PTSongInfo dst) {
src.copy(dst);
}
private static void applyInfos(PTTrack src, PTTrack dst) {
for (final PTTrackInfo srcInfo : src.getInfos()) {
dst.getInfos().add(srcInfo.getClone());
}
}
private static void applyRepeats(PTTrack src, PTTrack dst) {
applyRepeats(src, dst, new PTIndex(0, 0, 0), new PTSongSynchronizerData(),
new ArrayList<PTDirection>());
}
private static void applyRepeats(PTTrack src, PTTrack dst, PTIndex index,
PTSongSynchronizerData rd, List<PTDirection> useds) {
for (int s = index.s; s < src.getSections().size(); s++) {
PTSection srcSection = (PTSection) src.getSections().get(s);
srcSection.sort();
PTSection dstSection = new PTSection(srcSection.getNumber());
dstSection.setStaffs(srcSection.getStaffs());
dst.getSections().add(dstSection);
for (int p = (s == index.s ? index.p : 0); p < srcSection.getPositions()
.size(); p++) {
PTPosition srcPosition = (PTPosition) srcSection.getPositions().get(p);
srcPosition.sort();
PTPosition dstPosition = new PTPosition(srcPosition.getPosition());
dstSection.getPositions().add(dstPosition);
for (int c = (s == index.s && p == index.p ? index.c : 0); c < srcPosition
.getComponents().size(); c++) {
PTComponent component = (PTComponent) srcPosition.getComponents()
.get(c);
if (!rd.skip) {
dstPosition.addComponent(component.getClone());
}
// ------------------------------ PTBar
// ------------------------------//
if (component instanceof PTBar) {
PTBar bar = (PTBar) component;
if (bar.getRepeatClose() > 0 && rd.repeatStart != null) {
rd.repeatNumber++;
rd.repeatInProgress = true;
rd.repeatAlternative = false;
rd.skip = false;
if (rd.repeatNumber < bar.getRepeatClose()) {
applyRepeats(src, dst, rd.repeatStart, rd, useds);
return;
}
rd.repeatStart = null;
rd.repeatNumber = 0;
}
if (bar.isRepeatStart()) {
rd.repeatStart = new PTIndex(s, p, c);
if (!rd.repeatInProgress) {
rd.repeatNumber = 0;
}
rd.repeatInProgress = false;
}
}
// ------------------------------ PTSymbol
// ------------------------------//
else if (component instanceof PTSymbol) {
PTSymbol symbol = (PTSymbol) component;
rd.skip = false;
if (!rd.repeatAlternative && ((symbol.getEndNumber() & 1) != 0)) {
boolean validEnding = ((symbol.getEndNumber() & (1 << (rd.repeatNumber))) != 0);
if (rd.repeatNumber > 0 && !validEnding) {
rd.skip = true;
}
rd.repeatAlternative = true;
}
}
// ------------------------------ PTDirection
// ------------------------------//
else if (component instanceof PTDirection) {
PTDirection direction = (PTDirection) component;
boolean validRepeat = (direction.getRepeat() == 0 || (rd.repeatStart != null && (rd.repeatNumber + 1) == direction
.getRepeat()));
boolean validDirection = (direction.getActiveSymbol() == rd.findActiveSymbol);
if (validDirection && validRepeat) {
rd.findActiveSymbol = 0;
if (direction.getDirection() == PTDirection.DIRECTION_FINE) {
// Used to mark when to stop playing (usually the end of the
// score)
if (canUseDirection(direction, useds)) {
return;
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DA_CAPO) {
// Go back to the beginning of the score and play from there
if (canUseDirection(direction, useds)) {
applyRepeats(src, dst, new PTIndex(0, 0, 0), rd, useds);
return;
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO) {
// Go back to the Segno and play from there
if (canUseDirection(direction, useds)) {
PTIndex segno = findUnusedDirection(src, useds,
PTDirection.DIRECTION_SEGNO, s, p);
if (segno != null) {
applyRepeats(src, dst, segno, rd, useds);
return;
}
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_SEGNO) {
// Go back to the Segno Segno and play from there
if (canUseDirection(direction, useds)) {
PTIndex segno = findUnusedDirection(src, useds,
PTDirection.DIRECTION_SEGNO_SEGNO, s, p);
if (segno != null) {
applyRepeats(src, dst, segno, rd, useds);
return;
}
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_TO_CODA) {
// Go to the Coda sign and play from there. Used in conjunction
// with D.C./D.S. al Coda signs.
if (canUseDirection(direction, useds)) {
PTIndex coda = findUnusedDirection(src, useds,
PTDirection.DIRECTION_CODA, -1, -1);
if (coda != null) {
applyRepeats(src, dst, coda, rd, useds);
return;
}
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_TO_DOUBLE_CODA) {
// Go to the Double Coda and play from there. Used in
// conjunction with D.C./D.S. al Double Coda signs.
if (canUseDirection(direction, useds)) {
PTIndex coda = findUnusedDirection(src, useds,
PTDirection.DIRECTION_DOUBLE_CODA, -1, -1);
if (coda != null) {
applyRepeats(src, dst, coda, rd, useds);
return;
}
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DA_CAPO_AL_CODA) {
// Go back to the beginning of the score and play from there
// until the To Coda sign is reached,
// then jump to the Coda sign.
if (canUseDirection(direction, useds)) {
rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DC;
applyRepeats(src, dst, new PTIndex(0, 0, 0), rd, useds);
return;
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DA_CAPO_AL_DOUBLE_CODA) {
// Go back to the beginning of the score and play from there
// until the To Double Coda sign is reached,
// then jump to the Double Coda sign.
if (canUseDirection(direction, useds)) {
rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DC;
applyRepeats(src, dst, new PTIndex(0, 0, 0), rd, useds);
return;
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_AL_CODA) {
// Go back to the Segno sign and play from there until the To
// Coda sign is reached,
// then jump to the Coda sign.
if (canUseDirection(direction, useds)) {
PTIndex segno = findUnusedDirection(src, useds,
PTDirection.DIRECTION_SEGNO, s, p);
if (segno != null) {
rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DS;
applyRepeats(src, dst, segno, rd, useds);
return;
}
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_AL_DOUBLE_CODA) {
// Go back to the Segno sign and play from there until the To
// Double Coda sign is reached,
// then jump to the Double Coda sign.
if (canUseDirection(direction, useds)) {
PTIndex segno = findUnusedDirection(src, useds,
PTDirection.DIRECTION_SEGNO, s, p);
if (segno != null) {
rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DS;
applyRepeats(src, dst, segno, rd, useds);
return;
}
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_SEGNO_AL_CODA) {
// Go back to the Segno Segno sign and play from there until the
// To Coda sign is reached,
// then jump to the Coda sign.
if (canUseDirection(direction, useds)) {
PTIndex segno = findUnusedDirection(src, useds,
PTDirection.DIRECTION_SEGNO_SEGNO, s, p);
if (segno != null) {
rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DSS;
applyRepeats(src, dst, segno, rd, useds);
return;
}
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_SEGNO_AL_DOUBLE_CODA) {
// Go back to the Segno Segno sign and play from there until the
// To Double Coda sign is reached,
// then jump to the Double Coda sign.
if (canUseDirection(direction, useds)) {
PTIndex segno = findUnusedDirection(src, useds,
PTDirection.DIRECTION_SEGNO_SEGNO, s, p);
if (segno != null) {
rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DSS;
applyRepeats(src, dst, segno, rd, useds);
return;
}
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DA_CAPO_AL_FINE) {
// Go back to the beginning and play until the Fine sign is
// reached.
if (canUseDirection(direction, useds)) {
rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DC;
applyRepeats(src, dst, new PTIndex(0, 0, 0), rd, useds);
return;
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_AL_FINE) {
// Go back to the Segno sign and play until the Fine sign is
// reached.
PTIndex segno = findUnusedDirection(src, useds,
PTDirection.DIRECTION_SEGNO, s, p);
if (segno != null) {
rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DS;
applyRepeats(src, dst, segno, rd, useds);
return;
}
}
else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_SEGNO_AL_FINE) {
// Go back to the Segno Segno sign and play until the Fine sign
// is reached.
PTIndex segno = findUnusedDirection(src, useds,
PTDirection.DIRECTION_SEGNO_SEGNO, s, p);
if (segno != null) {
rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DSS;
applyRepeats(src, dst, segno, rd, useds);
return;
}
}
}
}
}
}
}
}
private static boolean canUseDirection(PTDirection direction, List useds) {
boolean inUse = false;
for (int i = 0; i < useds.size() && !inUse; i++) {
PTDirection used = (PTDirection) useds.get(i);
if (used.equals(direction)) {
return false;
}
}
useds.add(direction);
return true;
}
private static PTIndex findUnusedDirection(PTTrack src, List useds,
int value, int sEndIndex, int pEndIndex) {
return findUnusedDirection(src, useds, value, sEndIndex, pEndIndex, 0);
}
private static PTIndex findUnusedDirection(PTTrack src, List useds,
int value, int sEndIndex, int pEndIndex, int activeSymbol) {
for (int s = 0; s < (sEndIndex >= 0 ? sEndIndex + 1 : src.getSections()
.size()); s++) {
PTSection section = (PTSection) src.getSections().get(s);
for (int p = 0; p < (s == sEndIndex ? pEndIndex + 1 : section
.getPositions().size()); p++) {
PTPosition position = (PTPosition) section.getPositions().get(p);
for (int c = 0; c < position.getComponents().size(); c++) {
PTComponent component = (PTComponent) position.getComponents().get(c);
if (component instanceof PTDirection) {
PTDirection direction = (PTDirection) component;
if (direction.getDirection() == value
&& (activeSymbol == 0 || direction.getActiveSymbol() == activeSymbol)) {
if (canUseDirection(direction, useds)) {
return new PTIndex(s, p, 0);
}
}
}
}
}
}
return null;
}
private static void synchronizeTrack(PTTrack src, PTTrack dst) {
applyRepeats(src, dst);
applyInfos(src, dst);
}
public static void synchronizeTracks(PTSong src, PTSong dst) {
applyInfo(src.getInfo(), dst.getInfo());
synchronizeTrack(src.getTrack1(), dst.getTrack1());
synchronizeTrack(src.getTrack2(), dst.getTrack2());
}
}