package com.scrivenvar.editors;
import com.scrivenvar.FileEditorTab;
import com.scrivenvar.decorators.VariableDecorator;
import com.scrivenvar.definition.DefinitionPane;
import com.scrivenvar.definition.FindMode;
import com.scrivenvar.definition.VariableTreeItem;
import javafx.event.Event;
import javafx.scene.control.TreeItem;
import javafx.scene.input.KeyEvent;
import org.fxmisc.richtext.StyledTextArea;
import org.fxmisc.wellbehaved.event.EventPattern;
import java.nio.file.Path;
import java.text.BreakIterator;
import java.util.function.Consumer;
import static com.scrivenvar.definition.FindMode.*;
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 VariableNameInjector {
private FileEditorTab mTab;
private DefinitionPane mDefinitionPane;
public VariableNameInjector(
final FileEditorTab tab, final DefinitionPane pane ) {
setFileEditorTab( tab );
setDefinitionPane( pane );
initKeyboardEventListeners();
}
public void initKeyboardEventListeners( final FileEditorTab tab ) {
setFileEditorTab( tab );
initKeyboardEventListeners();
}
public void injectSelectedItem() {
final TreeItem<String> item = getDefinitionPane().getSelectedItem();
if( item.isLeaf() ) {
final VariableTreeItem<String> leaf = getDefinitionPane().findLeaf(
item.getValue(), FindMode.EXACT );
final StyledTextArea<?, ?> editor = getEditor();
editor.insertText( editor.getCaretPosition(), decorate( leaf ) );
}
}
private void initKeyboardEventListeners() {
addKeyboardListener(
keyPressed( SPACE, CONTROL_DOWN ),
this::autoinsert
);
}
private void autoinsert( final KeyEvent e ) {
final String paragraph = getCaretParagraph();
final int[] boundaries = getWordBoundariesAtCaret();
final String word = paragraph.substring( boundaries[ 0 ], boundaries[ 1 ] );
final VariableTreeItem<String> leaf = findLeaf( word );
if( leaf != null ) {
replaceText( boundaries[ 0 ], boundaries[ 1 ], decorate( leaf ) );
expand( leaf );
}
}
private int[] getWordBoundariesAtCaret() {
final String paragraph = getCaretParagraph();
int offset = getCurrentCaretColumn();
final BreakIterator wordBreaks = BreakIterator.getWordInstance();
wordBreaks.setText( paragraph );
while( offset > 0 && wordBreaks.isBoundary( offset ) ) {
offset--;
}
final int[] boundaries = new int[ 2 ];
boundaries[ 1 ] = wordBreaks.following( offset );
boundaries[ 0 ] = wordBreaks.previous();
return boundaries;
}
private String decorate( final VariableTreeItem<String> leaf ) {
return decorate( leaf.toPath() );
}
private String decorate( final String variable ) {
return getVariableDecorator().decorate( 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();
}
private VariableTreeItem<String> findLeaf( final String word ) {
assert word != null;
VariableTreeItem<String> leaf = findLeafExact( word );
leaf = leaf == null ? findLeafStartsWith( word ) : leaf;
leaf = leaf == null ? findLeafContains( word ) : leaf;
leaf = leaf == null ? findLeafLevenshtein( word ) : leaf;
return leaf;
}
private VariableTreeItem<String> findLeafExact( final String text ) {
return findLeaf( text, EXACT );
}
private VariableTreeItem<String> findLeafContains( final String text ) {
return findLeaf( text, CONTAINS );
}
private VariableTreeItem<String> findLeafStartsWith( final String text ) {
return findLeaf( text, STARTS_WITH );
}
private VariableTreeItem<String> findLeafLevenshtein( final String text ) {
return findLeaf( text, LEVENSHTEIN );
}
private VariableTreeItem<String> findLeaf(
final String text, final FindMode findMode ) {
return getDefinitionPane().findLeaf( text, findMode );
}
private void expand( final TreeItem<String> node ) {
final DefinitionPane pane = getDefinitionPane();
pane.collapse();
pane.expand( node );
pane.select( node );
}
private VariableDecorator getVariableDecorator() {
return VariableNameDecoratorFactory.newInstance( getFilename() );
}
private Path getFilename() {
return getFileEditorTab().getPath();
}
private EditorPane getEditorPane() {
return getFileEditorTab().getEditorPane();
}
private <T extends Event, U extends T> void addKeyboardListener(
final EventPattern<? super T, ? extends U> event,
final Consumer<? super U> consumer ) {
getEditorPane().addKeyboardListener( event, consumer );
}
private StyledTextArea<?, ?> getEditor() {
return getEditorPane().getEditor();
}
public FileEditorTab getFileEditorTab() {
return mTab;
}
public void setFileEditorTab( final FileEditorTab tab ) {
mTab = tab;
}
private DefinitionPane getDefinitionPane() {
return mDefinitionPane;
}
private void setDefinitionPane( final DefinitionPane definitionPane ) {
mDefinitionPane = definitionPane;
}
}