package com.scrivenvar.editors;
import com.scrivenvar.FileEditorTab;
import com.scrivenvar.definition.DefinitionPane;
import com.scrivenvar.definition.DefinitionTreeItem;
import com.scrivenvar.sigils.SigilOperator;
import javafx.scene.control.TreeItem;
import javafx.scene.input.KeyEvent;
import org.fxmisc.richtext.StyledTextArea;
import java.nio.file.Path;
import java.text.BreakIterator;
import static com.scrivenvar.Constants.*;
import static com.scrivenvar.StatusBarNotifier.alert;
import static java.lang.Character.isWhitespace;
import static javafx.scene.input.KeyCode.SPACE;
import static javafx.scene.input.KeyCombination.CONTROL_DOWN;
import static org.fxmisc.wellbehaved.event.EventPattern.keyPressed;
public final class DefinitionNameInjector {
private FileEditorTab mTab;
private final DefinitionPane mDefinitionPane;
public DefinitionNameInjector( final DefinitionPane pane ) {
mDefinitionPane = pane;
}
public void addListener( final FileEditorTab tab ) {
assert tab != null;
mTab = tab;
tab.getEditorPane().addKeyboardListener(
keyPressed( SPACE, CONTROL_DOWN ),
this::autoinsert
);
}
public void injectSelectedItem() {
final var pane = getDefinitionPane();
final TreeItem<String> item = pane.getSelectedItem();
if( item.isLeaf() ) {
final var leaf = pane.findLeafExact( item.getValue() );
final var editor = getEditor();
editor.insertText( editor.getCaretPosition(), decorate( leaf ) );
}
}
public void autoinsert() {
final String paragraph = getCaretParagraph();
final int[] bounds = getWordBoundariesAtCaret();
try {
if( isEmptyDefinitionPane() ) {
alert( STATUS_DEFINITION_EMPTY );
}
else {
final String word = paragraph.substring( bounds[ 0 ], bounds[ 1 ] );
if( word.isBlank() ) {
alert( STATUS_DEFINITION_BLANK );
}
else {
final var leaf = findLeaf( word );
if( leaf == null ) {
alert( STATUS_DEFINITION_MISSING, word );
}
else {
replaceText( bounds[ 0 ], bounds[ 1 ], decorate( leaf ) );
expand( leaf );
}
}
}
} catch( final Exception ignored ) {
alert( STATUS_DEFINITION_BLANK );
}
}
private void autoinsert( final KeyEvent e ) {
autoinsert();
}
private int[] getWordBoundariesAtCaret() {
final var paragraph = getCaretParagraph();
final var length = paragraph.length();
int offset = getCurrentCaretColumn();
int began = offset;
int ended = offset;
while( began > 0 && !isWhitespace( paragraph.charAt( began - 1 ) ) ) {
began--;
}
while( ended < length && !isWhitespace( paragraph.charAt( ended ) ) ) {
ended++;
}
final var iterator = BreakIterator.getWordInstance();
iterator.setText( paragraph );
while( began < length && iterator.isBoundary( began + 1 ) ) {
began++;
}
while( ended > 0 && iterator.isBoundary( ended - 1 ) ) {
ended--;
}
return new int[]{began, ended};
}
private String decorate( final DefinitionTreeItem<String> leaf ) {
return decorate( leaf.toPath() );
}
private String decorate( final String variable ) {
return getVariableDecorator().apply( variable );
}
private void replaceText(
final int posBegan, final int posEnded, final String text ) {
final int p = getCurrentParagraph();
getEditor().replaceText( p, posBegan, p, posEnded, text );
}
private int getCurrentParagraph() {
return getEditor().getCurrentParagraph();
}
private String getCaretParagraph() {
return getEditor().getText( getCurrentParagraph() );
}
private int getCurrentCaretColumn() {
return getEditor().getCaretColumn();
}
@SuppressWarnings("ConstantConditions")
private DefinitionTreeItem<String> findLeaf( final String word ) {
assert word != null;
final var pane = getDefinitionPane();
DefinitionTreeItem<String> leaf = null;
leaf = leaf == null ? pane.findLeafExact( word ) : leaf;
leaf = leaf == null ? pane.findLeafStartsWith( word ) : leaf;
leaf = leaf == null ? pane.findLeafContains( word ) : leaf;
leaf = leaf == null ? pane.findLeafContainsNoCase( word ) : leaf;
return leaf;
}
private boolean isEmptyDefinitionPane() {
return getDefinitionPane().isEmpty();
}
private void expand( final TreeItem<String> node ) {
final DefinitionPane pane = getDefinitionPane();
pane.collapse();
pane.expand( node );
pane.select( node );
}
private SigilOperator getVariableDecorator() {
return DefinitionDecoratorFactory.newInstance( getFilename() );
}
private Path getFilename() {
return getFileEditorTab().getPath();
}
private EditorPane getEditorPane() {
return getFileEditorTab().getEditorPane();
}
private StyledTextArea<?, ?> getEditor() {
return getEditorPane().getEditor();
}
public FileEditorTab getFileEditorTab() {
return mTab;
}
private DefinitionPane getDefinitionPane() {
return mDefinitionPane;
}
}