/*
* Copyright (C) 2015-2017 たんらる
*/
package fourthline.mmlTools.optimizer;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalInt;
import fourthline.mmlTools.core.MMLTokenizer;
import fourthline.mmlTools.core.MelodyParser;
import fourthline.mmlTools.core.ParserWarn3ML;
import fourthline.mmlTools.core.UndefinedTickException;
/**
* Nxを使用した最適化.
*/
public final class NxOptimizer implements MMLStringOptimizer.Optimizer {
private static final class NxBuilder implements Cloneable {
private StringBuilder builder = new StringBuilder();
private int nCount = 0;
private int prevOct;
private OptionalInt offset = OptionalInt.empty();
private void addOctToken(int offset, String token) {
if (!this.offset.isPresent()) {
this.offset = OptionalInt.of( offset );
}
}
private NxBuilder(int initOct) {
prevOct = initOct;
}
@Override
public String toString() {
return builder.toString()+" [o"+prevOct+"] ("+offset+") ";
}
@Override
public NxBuilder clone() {
NxBuilder obj = new NxBuilder(prevOct);
obj.builder = new StringBuilder(builder.toString());
if (offset.isPresent()) {
obj.offset = OptionalInt.of( offset.getAsInt() );
}
return obj;
}
}
private int octave = 4;
private final MelodyParser parser = new MelodyParser("");
private final List<NxBuilder> builderList = new ArrayList<>();
public NxOptimizer() {
builderList.add(new NxBuilder(octave));
}
private NxBuilder minStack(List<NxBuilder> stack) {
return stack.stream().min((t1, t2) -> {
int ret = t1.builder.length() - t2.builder.length();
if (ret == 0) {
ret = t1.nCount - t2.nCount;
}
return ret;
}).get();
}
private void addPattern(List<NxBuilder> prevMap) {
if (parser.getNoteNumber() < 0) {
return;
}
prevMap.forEach(t -> {
t.builder.append("n"+parser.getNoteNumber());
t.nCount++;
});
builderList.add( minStack(prevMap) );
clearOctToken();
}
private void addOctToken(String token) {
builderList.forEach(t -> {
t.addOctToken(t.builder.length(), token);
});
}
private void clearOctToken() {
builderList.forEach(t -> {
t.offset = OptionalInt.empty();
});
}
private List<NxBuilder> listClone() {
List<NxBuilder> cloneList = new ArrayList<>();
builderList.forEach(t -> {
cloneList.add(t.clone());
});
return cloneList;
}
private void cleanList() {
NxBuilder min = minStack(builderList);
builderList.clear();
builderList.add(min);
}
private void notePattern(String token, String noteLength) {
List<NxBuilder> prevList = listClone();
addNoteToken(token);
cleanList();
if (noteLength.length() == 0) {
addPattern(prevList);
}
}
private void doToken(String token) {
String s[] = MMLTokenizer.noteNames(token);
char firstC = Character.toLowerCase(s[0].charAt(0));
if ( (firstC >= 'a') && (firstC <= 'g') ) {
notePattern(token, s[1]);
} else if (firstC == '>') {
octave++;
addOctToken(token);
} else if (firstC == '<') {
octave--;
addOctToken(token);
} else if (firstC == 'o') {
octave = Integer.parseInt(s[1]);
addOctToken(token);
} else {
addToken(token);
}
}
private void addNoteToken(String token) {
builderList.forEach(t -> {
t.builder.append( OxLxOptimizer.getOctaveString(t.prevOct, octave) );
t.builder.append(token);
t.prevOct = octave;
});
clearOctToken();
}
private void addToken(String token) {
builderList.forEach(t -> {
t.builder.append(token);
});
}
private void printMap() {
if (MMLStringOptimizer.getDebug()) {
builderList.forEach(t -> {
System.out.println(t.toString());
});
System.out.println(" -- ");
}
}
@Override
public void nextToken(String token) {
try {
parser.noteGT(token);
} catch (UndefinedTickException | ParserWarn3ML e) {}
doToken(token);
printMap();
}
@Override
public String getMinString() {
return minStack(builderList).builder.toString();
}
}