package com.keenwrite.search;
import com.keenwrite.util.CyclicIterator;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.IndexRange;
import org.ahocorasick.trie.Emit;
import org.ahocorasick.trie.Trie;
import java.util.ArrayList;
import java.util.List;
import static org.ahocorasick.trie.Trie.builder;
public final class SearchModel {
private final ObjectProperty<IndexRange> mMatchOffset =
new SimpleObjectProperty<>();
private final ObjectProperty<Integer> mMatchCount =
new SimpleObjectProperty<>();
private final ObjectProperty<Integer> mMatchIndex =
new SimpleObjectProperty<>();
private CyclicIterator<Emit> mMatches = new CyclicIterator<>( List.of() );
private String mNeedle = "";
public SearchModel() {
}
public ObjectProperty<Integer> matchCountProperty() {
return mMatchCount;
}
public ObjectProperty<Integer> matchIndexProperty() {
return mMatchIndex;
}
public ObservableValue<IndexRange> matchOffsetProperty() {
return mMatchOffset;
}
public void search( final String needle, final String haystack ) {
assert needle != null;
assert haystack != null;
final var trie = builder()
.ignoreCase()
.ignoreOverlaps()
.addKeyword( needle )
.build();
final var emits = trie.parseText( haystack );
mMatches = new CyclicIterator<>( emits );
mMatchCount.set( emits.size() );
mNeedle = needle;
advance();
}
public void search( final String haystack ) {
search( mNeedle, haystack );
}
public void advance() {
if( mMatches.hasNext() ) {
setCurrent( mMatches.next() );
}
}
public void retreat() {
if( mMatches.hasPrevious() ) {
setCurrent( mMatches.previous() );
}
}
private void setCurrent( final Emit emit ) {
mMatchOffset.set( new IndexRange( emit.getStart(), emit.getEnd() ) );
mMatchIndex.set( mMatches.getIndex() + 1 );
}
}