package de.prob2.ui.groovy;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.input.KeyCode;
import javafx.scene.shape.Path;
import javafx.stage.Popup;

public class GroovyCodeCompletion extends Popup {
	
	private final Logger logger = LoggerFactory.getLogger(GroovyCodeCompletion.class);
	
	@FXML
	private ListView<GroovyClassPropertyItem> lv_suggestions;
	
	private ScriptEngine engine;
	
	public GroovyCodeCompletion(FXMLLoader loader, ScriptEngine engine) {
		loader.setLocation(getClass().getResource("groovy_codecompletion_popup.fxml"));
		loader.setRoot(this);
		loader.setController(this);
		try {
			loader.load();
		} catch (IOException e) {
			logger.error("loading fxml failed", e);
		}
		this.engine = engine;
		lv_suggestions.setOnKeyPressed(e-> {
			if(e.getCode().equals(KeyCode.ENTER)) {
				System.out.println(lv_suggestions.getSelectionModel().getSelectedItem());
				this.deactivate();
			}
		});		
	}
	
	
	public void activate(GroovyConsole console, String currentLine) {
		Bindings engineScope = engine.getBindings(ScriptContext.ENGINE_SCOPE);
		Bindings globalScope = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
		if(!engineScope.keySet().contains(currentLine) && !globalScope.keySet().contains(currentLine)) {
			return;
		}
		Object object = null;
		if(engineScope.keySet().contains(currentLine)) {
			object = engineScope.get(currentLine);
		} else if(globalScope.keySet().contains(currentLine)) {
			object = globalScope.get(currentLine);
		} else {
			return;
		}
		showSuggestions(object);
		lv_suggestions.getSelectionModel().selectFirst();
		Point2D point = findCaretPosition(findCaret(console));
		double x = point.getX() + 10;
		double y = point.getY() + 10;
		this.show(console, x, y);
		
	}
	
	
	public void deactivate() {
		this.hide();
	}
	
	private void showSuggestions(Object object) {
		Class <? extends Object> clazz = object.getClass();
		for(Method m : clazz.getMethods()) {
			lv_suggestions.getItems().add(new GroovyClassPropertyItem(m));
		}
		for(Field f : clazz.getFields()) {
			lv_suggestions.getItems().add(new GroovyClassPropertyItem(f));
		}
		MetaPropertiesHandler.handleMethods(object, lv_suggestions.getItems());
		MetaPropertiesHandler.handleProperties(object, lv_suggestions.getItems());
		
	}
	
	public boolean isVisible() {
		return this.isShowing();
	}
	
	
	private Path findCaret(Parent parent) {
		for (Node node : parent.getChildrenUnmodifiable()) {
			if (node instanceof Path) {
				return (Path) node;
			} else if (node instanceof Parent) {
				Path caret = findCaret((Parent) node);
				if (caret != null) {
					return caret;
				}
			}
		}
		return null;
	}
	

	private Point2D findCaretPosition(Node node) {
		double x = 0;
		double y = 0;
		if(node == null) {
			return null;
		}
		for (Node n = node; n != null; n=n.getParent()) {
			Bounds parentBounds = n.getBoundsInParent();
			x += parentBounds.getMinX();
			y += parentBounds.getMinY();
		}
		if(node.getScene() != null) {
			Scene scene = node.getScene();
			x += scene.getX() + scene.getWindow().getX();
			y += scene.getY() + scene.getWindow().getY();
			x = Math.min(scene.getWindow().getX() + scene.getWindow().getWidth() - 20, x);
			y = Math.min(scene.getWindow().getY() + scene.getWindow().getHeight() - 20, y);
		}
		return new Point2D(x,y);
	}

}

@@ -844,16 +844,47 @@
 gine;%0A%09%0A
+%09private String currentLine;%0A%09%0A
 %09public 
@@ -1207,16 +1207,40 @@
 engine;%0A
+%09%09this.currentLine =%22%22;%0A
 %09%09lv_sug
@@ -1501,16 +1501,50 @@
 Line) %7B%0A
+%09%09this.currentLine = currentLine;%0A
 %09%09Bindin
@@ -1814,15 +1814,8 @@
 ject
- = null
 ;%0A%09%09
@@ -2058,16 +2058,92 @@
 bject);%0A
+%09%09showPopup(console);%0A%09%7D%0A%09%0A%09private void showPopup(GroovyConsole console) %7B%0A
 %09%09lv_sug
@@ -2338,19 +2338,15 @@
  y);
-%0A%09
 %09%0A%09%7D%0A%09%0A
-%09%0A
 %09pub
@@ -2365,24 +2365,49 @@
 ctivate() %7B%0A
+%09%09this.currentLine = %22%22;%0A
 %09%09this.hide(

Update Groovy Code Completion