package yuku.alkitabconverter.util;
import yuku.alkitab.util.Ari;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
public class TextDb {
public static final String TAG = TextDb.class.getSimpleName();
TreeMap<Integer, VerseState> map = new TreeMap<>();
public TextDb() {
}
public TextDb(final List<Rec> recs) {
for (final Rec rec : recs) {
final int key = Ari.encode(rec.book_1 - 1, rec.chapter_1, rec.verse_1);
final VerseState value = new VerseState();
value.text = rec.text;
map.put(key, value);
}
}
public interface TextProcessor {
void process(int ari, VerseState verseState);
}
public static class VerseState {
// was: "public int menjorok;" but no longer used
public String text;
}
public String append(int bookId, int chapter_1, int verse_1, String s, int currentIndent) {
return append(Ari.encode(bookId, chapter_1, verse_1), s, currentIndent);
}
public String append(int bookId, int chapter_1, int verse_1, String s, int currentIndent, String separatorWhenExisting) {
return append(Ari.encode(bookId, chapter_1, verse_1), s, currentIndent, separatorWhenExisting);
}
/**
* @param currentIndent if -1, don't write anything.
*/
public String append(int ari, String s, int currentIndent) {
return append(ari, s, currentIndent, null);
}
/**
* @param separatorWhenExisting if the text is appended to an ari that has already some text, append this first, then the text.
* @param currentIndent if -1, don't write anything.
*/
public String append(int ari, String text, int currentIndent, String separatorWhenExisting) {
VerseState as = map.get(ari);
boolean isNew = false;
if (as == null) {
as = new VerseState();
as.text = "";
map.put(ari, as);
isNew = true;
}
boolean writtenParaMarker = false;
if (currentIndent != -1) {
if (currentIndent == -2) {
as.text += "@^";
} else if (currentIndent < 0 || currentIndent > 4) {
throw new RuntimeException("menjorok ngaco: " + currentIndent);
} else {
as.text += "@" + String.valueOf(currentIndent);
}
writtenParaMarker = true;
// was: "update menjoroknya ayatstate" but no longer used
// for (int i = 0; i < as.isi.length(); i++) {
// if (as.isi.charAt(i) == '@' && as.isi.charAt(i+1) >= '0' && as.isi.charAt(i+1) <= '4') {
// as.menjorok = as.isi.charAt(i+1) - '0';
// }
// }
}
if (!isNew && separatorWhenExisting != null) {
as.text += separatorWhenExisting;
}
if (writtenParaMarker) {
as.text += leftSpaceTrim(text);
} else {
as.text += text;
}
// buang spasi di depan kalo ada
while (as.text.startsWith(" ")) {
as.text = as.text.substring(1);
}
// kasih @@ kalo depannya blum ada
if (as.text.contains("@") && !as.text.startsWith("@@")) {
as.text = "@@" + as.text;
}
return as.text;
}
private static String leftSpaceTrim(String s) {
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != ' ') {
return s.substring(i);
}
}
return s;
}
public void normalize() {
Set<Integer> keys = new TreeSet<>(map.keySet());
int last_bookId = -1;
int last_chapter_1 = 0;
int last_verse_1 = 0;
for (int ari: keys) {
int bookId = Ari.toBook(ari);
int chapter_1 = Ari.toChapter(ari);
int verse_1 = Ari.toVerse(ari);
if (bookId != last_bookId) {
// must start with chapter_1 1 and verse_1 1
if (chapter_1 != 1 || verse_1 != 1) {
throw new RuntimeException("at " + bookId + " " + chapter_1 + " " + verse_1 + ": " + " new book does not start from 1:1");
}
// different book, ignore and restart
last_bookId = bookId;
last_chapter_1 = chapter_1;
last_verse_1 = verse_1;
continue;
}
if (chapter_1 == last_chapter_1) {
if (verse_1 != last_verse_1 + 1) {
System.out.println("at " + bookId + " " + chapter_1 + " " + verse_1 + ": " + " skipped after " + last_bookId + " " + last_chapter_1 + " " + last_verse_1);
System.out.println("Adding empty verses:");
for (int a = last_verse_1 + 1; a < verse_1; a++) {
System.out.println(" at " + bookId + " " + chapter_1 + " " + a + ": " + " (blank)");
append(bookId, chapter_1, a, "", 0);
}
}
} else if (chapter_1 == last_chapter_1 + 1) {
if (verse_1 != 1) {
throw new RuntimeException("at " + bookId + " " + chapter_1 + " " + verse_1 + ": " + " verse_1 is not 1");
}
} else {
throw new RuntimeException("at " + bookId + " " + chapter_1 + " " + verse_1 + ": " + " so wrong! it's after " + last_bookId + " " + last_chapter_1 + " " + last_verse_1);
}
last_bookId = bookId;
last_chapter_1 = chapter_1;
last_verse_1 = verse_1;
}
System.out.println("normalize done");
}
public void removeEmptyVerses() {
for (final Entry<Integer, VerseState> entry : map.entrySet()) {
final VerseState vs = entry.getValue();
if ("@@@0".equals(vs.text)) {
vs.text = "";
}
}
}
public void dump(PrintStream ps) {
ps.println("TOTAL text: " + map.size());
for (Entry<Integer, VerseState> e: map.entrySet()) {
ps.printf("%d\t%d\t%d\t%s%n", Ari.toBook(e.getKey()) + 1, Ari.toChapter(e.getKey()), Ari.toVerse(e.getKey()), e.getValue().text);
}
}
public void dump() {
dump(System.out);
}
public int size() {
return map.size();
}
public List<Rec> toRecList() {
List<Rec> res = new ArrayList<>();
for (Entry<Integer, VerseState> e: map.entrySet()) {
Rec rec = new Rec();
int ari = e.getKey();
rec.book_1 = Ari.toBook(ari) + 1;
rec.chapter_1 = Ari.toChapter(ari);
rec.verse_1 = Ari.toVerse(ari);
rec.text = e.getValue().text;
res.add(rec);
}
return res;
}
public void processEach(TextProcessor textProcessor) {
for (Map.Entry<Integer, VerseState> e: map.entrySet()) {
textProcessor.process(e.getKey(), e.getValue());
}
}
public int getBookCount() {
Set<Integer> bookIds = new LinkedHashSet<>();
for (Map.Entry<Integer, VerseState> e: map.entrySet()) {
int bookId = Ari.toBook(e.getKey());
bookIds.add(bookId);
}
return bookIds.size();
}
public int[] getBookIds() {
Set<Integer> bookIds = new TreeSet<>();
for (Map.Entry<Integer, VerseState> e: map.entrySet()) {
int bookId = Ari.toBook(e.getKey());
bookIds.add(bookId);
}
int[] res = new int[bookIds.size()];
int c = 0;
for (Integer bookId: bookIds) {
res[c++] = bookId;
}
return res;
}
/**
* No skipped chapters recognized. So if a book has chapters [1, 5, 6], this returns 6, not 3.
*/
public int getChapterCountForBook(int bookId) {
int maxChapter = 0;
for (Map.Entry<Integer, VerseState> e: map.entrySet()) {
int ari = e.getKey();
if (Ari.toBook(ari) == bookId) {
int chapter_1 = Ari.toChapter(ari);
if (chapter_1 > maxChapter) maxChapter = chapter_1;
}
}
return maxChapter;
}
/**
* No skipped verses recognized. So if a chapter has verses [1, 5, 6], this returns 6, not 3.
*/
public int getVerseCountForBookChapter(int bookId, int chapter_1) {
int maxVerse = 0;
for (Map.Entry<Integer, VerseState> e: map.entrySet()) {
int ari = e.getKey();
if (Ari.toBook(ari) == bookId && Ari.toChapter(ari) == chapter_1) {
int verse_1 = Ari.toVerse(ari);
if (verse_1 > maxVerse) maxVerse = verse_1;
}
}
return maxVerse;
}
public String getVerseText(int bookId, int chapter_1, int verse_1) {
return map.get(Ari.encode(bookId, chapter_1, verse_1)).text;
}
}