package tk.captainsplexx.JavaFX.CellFactories;
import java.nio.ByteOrder;
import java.util.HashMap;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.effect.InnerShadow;
import javafx.scene.image.ImageView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.paint.Color;
import tk.captainsplexx.Game.Core;
import tk.captainsplexx.JavaFX.JavaFXHandler;
import tk.captainsplexx.JavaFX.TreeViewEntry;
import tk.captainsplexx.JavaFX.Windows.MainWindow.EntryType;
import tk.captainsplexx.JavaFX.Windows.MainWindow.WorkDropType;
import tk.captainsplexx.Resource.FileHandler;
import tk.captainsplexx.Resource.ResourceHandler;
import tk.captainsplexx.Resource.EBX.EBXFieldDescriptor;
import tk.captainsplexx.Resource.EBX.EBXFile;
import tk.captainsplexx.Resource.EBX.EBXHandler;
import tk.captainsplexx.Resource.TOC.ResourceLink;
public class JavaFXebxTCF extends TreeCell<TreeViewEntry> {
private enum Operation {Name, Value};
private Operation modifyOp;
private TextField textField;
private WorkDropType dropType;
private ContextMenu contextMenu = new ContextMenu();
public TreeItem<TreeViewEntry> draggedTreeItem;
private MenuItem addText, addFloat, addDouble, addArray, addInteger, addBool, addList, addLong, addByte, addShort, remove, rename, follow;
private EBXFile ebxFile;
private boolean isOriginal;
public JavaFXebxTCF(EBXFile ebxFile, boolean isOriginal) {
this.ebxFile = ebxFile;
this.isOriginal = isOriginal;
addText = new MenuItem("Add Text");
addText.setGraphic(new ImageView(JavaFXHandler.textIcon));
addText.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
TreeItem<TreeViewEntry> newItem = new TreeItem<TreeViewEntry>(new TreeViewEntry("NEW STRING ENTRY", new ImageView(JavaFXHandler.textIcon), "", EntryType.STRING));
getTreeItem().getChildren().add(newItem);
}
});
addFloat = new MenuItem("Add Float");
addFloat.setGraphic(new ImageView(JavaFXHandler.floatIcon));
addFloat.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
TreeItem<TreeViewEntry> newItem = new TreeItem<TreeViewEntry>(new TreeViewEntry("NEW FLOAT ENTRY", new ImageView(JavaFXHandler.floatIcon), 0.0f, EntryType.FLOAT));
getTreeItem().getChildren().add(newItem);
}
});
addDouble = new MenuItem("Add Double");
addDouble.setGraphic(new ImageView(JavaFXHandler.doubleIcon));
addDouble.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
TreeItem<TreeViewEntry> newItem = new TreeItem<TreeViewEntry>(new TreeViewEntry("NEW DOUBLE ENTRY", new ImageView(JavaFXHandler.doubleIcon), 0.0d, EntryType.DOUBLE));
getTreeItem().getChildren().add(newItem);
}
});
addArray = new MenuItem("Add Array");
addArray.setGraphic(new ImageView(JavaFXHandler.arrayIcon));
addArray.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
TreeItem<TreeViewEntry> newItem = new TreeItem<TreeViewEntry>(new TreeViewEntry("NEW ARRAY ENTRY", new ImageView(JavaFXHandler.arrayIcon), null, EntryType.ARRAY));
getTreeItem().getChildren().add(newItem);
}
});
addInteger = new MenuItem("Add Integer");
addInteger.setGraphic(new ImageView(JavaFXHandler.integerIcon));
addInteger.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
TreeItem<TreeViewEntry> newItem = new TreeItem<TreeViewEntry>(new TreeViewEntry("NEW INTEGER ENTRY", new ImageView(JavaFXHandler.integerIcon), 1337, EntryType.INTEGER));
getTreeItem().getChildren().add(newItem);
}
});
addBool = new MenuItem("Add Bool");
addBool.setGraphic(new ImageView(JavaFXHandler.boolIcon));
addBool.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
TreeItem<TreeViewEntry> newItem = new TreeItem<TreeViewEntry>(new TreeViewEntry("NEW BOOL ENTRY", new ImageView(JavaFXHandler.boolIcon), false, EntryType.BOOL));
getTreeItem().getChildren().add(newItem);
}
});
addList = new MenuItem("Add List");
addList.setGraphic(new ImageView(JavaFXHandler.listIcon));
addList.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
TreeItem<TreeViewEntry> newItem = new TreeItem<TreeViewEntry>(new TreeViewEntry("NEW LIST ENTRY", new ImageView(JavaFXHandler.listIcon), null, EntryType.LIST));
getTreeItem().getChildren().add(newItem);
}
});
addLong = new MenuItem("Add Long");
addLong.setGraphic(new ImageView(JavaFXHandler.longIcon));
addLong.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
TreeItem<TreeViewEntry> newItem = new TreeItem<TreeViewEntry>(new TreeViewEntry("NEW LONG ENTRY", new ImageView(JavaFXHandler.longIcon), (long) 9001, EntryType.LONG));
getTreeItem().getChildren().add(newItem);
}
});
addByte = new MenuItem("Add Byte");
addByte.setGraphic(new ImageView(JavaFXHandler.byteIcon));
addByte.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
TreeItem<TreeViewEntry> newItem = new TreeItem<TreeViewEntry>(new TreeViewEntry("NEW BYTE ENTRY", new ImageView(JavaFXHandler.shortIcon), (byte) 0, EntryType.BYTE));
getTreeItem().getChildren().add(newItem);
}
});
addShort= new MenuItem("Add Short");
addShort.setGraphic(new ImageView(JavaFXHandler.shortIcon));
addShort.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
TreeItem<TreeViewEntry> newItem = new TreeItem<TreeViewEntry>(new TreeViewEntry("NEW SHORT ENTRY", new ImageView(JavaFXHandler.byteIcon), (short) 0, EntryType.SHORT));
getTreeItem().getChildren().add(newItem);
}
});
rename = new MenuItem("Rename");
rename.setGraphic(new ImageView(JavaFXHandler.pencilIcon));
rename.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
modifyOp = Operation.Name;
startEdit();
}
});
remove = new MenuItem("Remove");
remove.setGraphic(new ImageView(JavaFXHandler.removeIcon));
remove.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
if (getTreeItem().getParent()!=null){
getTreeItem().getParent().getChildren().remove(getTreeItem());
}
}
});
follow = new MenuItem("Follow");
follow.setGraphic(new ImageView(JavaFXHandler.rightArrowIcon));
follow.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
try{
String target = ((String)getTreeItem().getValue().getValue());
if (target!=null){
String[] targetArr = target.split(" ");
if (targetArr.length==2){//guid has a file guid and instance guid
ResourceHandler rs = Core.getGame().getResourceHandler();
EBXHandler eh = rs.getEBXHandler();
boolean readOriginal = false;
EBXFile file = eh.getEBXFileByGUID(targetArr[0], true/*tryLoad*/, readOriginal);
ResourceLink resLink = rs.getResourceLinkByEBXGUID(targetArr[0]);
if (file!=null&&resLink!=null){
Core.getJavaFXHandler().getMainWindow().createEBXWindow(file, resLink.getName(), readOriginal);
}else{
System.err.println("Link can't be followed, cuz off missing data or link.");
}
}else{
System.err.println("Internal GUID's can't be followed!");
}
}
}catch (Exception e){
System.out.println("Invaild link to follow.");
}
}
});
setOnDragDetected(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
draggedTreeItem = getTreeItem();
if (draggedTreeItem!=null){
ClipboardContent content;
content = new ClipboardContent();
content.putString(draggedTreeItem.getValue().toString());
Dragboard dragboard;
dragboard = getTreeView().startDragAndDrop(TransferMode.MOVE);
dragboard.setContent(content);
System.out.println("dragging: "+draggedTreeItem.getValue().getName());
event.consume();
}
}});
setOnDragDropped(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent event) {
if (getTreeItem() != draggedTreeItem && getTreeItem() != null && dropType == WorkDropType.DROP_INTO){
System.out.println("in to: "+ getTreeItem().getValue().getName());
TreeItem<TreeViewEntry> draggedItemParent = draggedTreeItem.getParent();
TreeViewEntry draggedWork = draggedTreeItem.getValue();
draggedItemParent.getChildren().remove(draggedWork);
draggedTreeItem = null;
event.setDropCompleted(true);
}else if(getTreeItem() != draggedTreeItem && getTreeItem() != null && dropType == WorkDropType.REORDER){
System.out.println("reorder!");
}
event.consume();
}});
setOnDragOver(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent event) {
if (getTreeItem()!=null){
double height = JavaFXebxTCF.this.getHeight();
Point2D sceneCoordinates = JavaFXebxTCF.this.localToScene(0d, 0d);
double y = event.getSceneY() - (sceneCoordinates.getY());
if (y > (height * .75d)) {
InnerShadow shadow;
shadow = new InnerShadow();
shadow.setOffsetY(-1.0);
shadow.setWidth(1.0f);
setEffect(shadow);
dropType = WorkDropType.REORDER;
}
else {
InnerShadow shadow;
shadow = new InnerShadow();
shadow.setOffsetX(1.0);
shadow.setColor(Color.web("#666666"));
shadow.setOffsetY(1.0);
setEffect(shadow);
dropType = WorkDropType.DROP_INTO;
}
event.acceptTransferModes(TransferMode.MOVE);
}
}
});
setOnDragExited(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent event) {
setEffect(null);
}});
}
@Override
public void startEdit() {
super.startEdit();
if (modifyOp == Operation.Name){
createTextField(getTreeItem());
setText(null);
setGraphic(textField);
textField.selectAll();
}else if (modifyOp == null){
TreeViewEntry entry = getTreeItem().getValue();
if (entry.getType()==EntryType.BOOL){
Object bv = null;
if ((boolean) entry.getValue()){
bv = convertToObject("FALSE", entry);
}else{
bv = convertToObject("TRUE", entry);
}
entry.setValue(bv);
commitEdit(getTreeItem().getValue());
}else if(entry.getType()==EntryType.ENUM&&entry.getValue() instanceof HashMap<?,?>){
HashMap<EBXFieldDescriptor, Boolean> enums = (HashMap<EBXFieldDescriptor, Boolean>) entry.getValue();
Boolean done = false;
Boolean selectNext = false;
for (EBXFieldDescriptor desc : enums.keySet()){
boolean selected = enums.get(desc);
if (selected){
enums.put(desc, false);//TODO workaround to keep index
System.err.println("TODO: workaround to keep index");
selectNext = true;
}else if (selectNext){
enums.put(desc, true);//TODO workaround to keep index
selectNext = false;
done = true;
}
}
if (!done){
for (EBXFieldDescriptor desc : enums.keySet()){
enums.put(desc, true);
break;
}
}
}else{
//if (textField == null) { //USELESS ?
createTextField(getTreeItem());
//}
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
updateItem(getTreeItem().getValue(), getTreeItem().getValue()==null);
}
@Override
public void updateItem(TreeViewEntry item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(convertToString(item));
}
setText(null);
setGraphic(textField);
} else {
if (item != null){
if (item.getType() == EntryType.ARRAY || item.getType() == EntryType.LIST){
setText(item.getName()+":"+item.getType().toString());
}else{
setText(item.getName()+": "+convertToString(item));
}
if (item.getEBXType()!=0){
setTooltip(new Tooltip("Type: "+FileHandler.bytesToHex(FileHandler.toBytes(item.getEBXType(), ByteOrder.BIG_ENDIAN))+
" | Offset: "+FileHandler.bytesToHex(FileHandler.toBytes(item.getOffset(), ByteOrder.BIG_ENDIAN))));
}
setGraphic(getTreeItem().getValue().getGraphic());
contextMenu.getItems().clear();
if (getTreeItem().getValue().getType()==EntryType.ARRAY||getTreeItem().getValue().getType()==EntryType.LIST){
// contextMenu.getItems().addAll(addText, addFloat, addDouble, addInteger, addLong, addByte, addBool, addArray, addList, remove);
}else if (getTreeItem().getValue().getType()==EntryType.GUID){
contextMenu.getItems().addAll(follow ,rename, remove);
}else if (getTreeItem()!= null){
contextMenu.getItems().addAll(rename, remove);
}
//setUnderline(value);
setContextMenu(contextMenu);
}
}
}
}
private void createTextField(TreeItem<TreeViewEntry> treeItem) {
if (modifyOp == Operation.Name){
textField = new TextField(treeItem.getValue().getName());
}else{
textField = new TextField(convertToString(treeItem.getValue()));
}
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
if (modifyOp == Operation.Name){
treeItem.getValue().setName(textField.getText());
commitEdit(treeItem.getValue());
modifyOp = null;
}else if(modifyOp == null){
Object obj = convertToObject(textField.getText(), treeItem.getValue());
if (obj != null && (treeItem.getValue().getType() == EntryType.ARRAY) || treeItem.getValue().getType() == EntryType.LIST){
treeItem.getValue().setName((String) obj);
commitEdit(treeItem.getValue());
modifyOp = null;
}else if (obj != null){
treeItem.getValue().setValue(obj);
commitEdit(treeItem.getValue());
modifyOp = null;
}else{
modifyOp = null;
cancelEdit();
}
}
} else if (t.getCode() == KeyCode.ESCAPE) {
modifyOp = null;
cancelEdit();
}
}
});
}
public String convertToString(TreeViewEntry item){
if (item.getValue()!=null){
ResourceHandler rs = Core.getGame().getResourceHandler();
EBXHandler ebxHandler = rs.getEBXHandler();
switch(item.getType()){
case STRING:
return (String)item.getValue();
case SHA1:
return (String)item.getValue();
case FLOAT:
return ((Float)item.getValue()).toString();
case DOUBLE:
return ((Double)item.getValue()).toString();
case SHORT:
return ((Short)item.getValue()).toString();
case INTEGER:
return ((Integer)item.getValue()).toString();
case UINTEGER:
return ((Long)item.getValue()).toString();
case LONG:
return ((Long)item.getValue()).toString();
case ARRAY:
return item.getName();
case LIST:
return item.getName();
case BOOL:
if (((Boolean)item.getValue())==true){
return "TRUE";
}else{
return "FALSE";
}
case HEX8:
return (String)item.getValue();
case BYTE:
return byteToHex(((Byte)item.getValue()));
case ENUM:
HashMap<EBXFieldDescriptor, Boolean> enums = (HashMap<EBXFieldDescriptor, Boolean>) item.getValue();
String value = "null";
for (EBXFieldDescriptor desc : enums.keySet()){
boolean selected = enums.get(desc);
if (selected){
value = desc.getName();
break;
}
}
return value;
case RAW:
return FileHandler.bytesToHex((byte[]) item.getValue());
case NULL:
return ("NULL"); //DEFINED NULL
case GUID:
String fileGUIDName = null;
String[] split = ((String)item.getValue()).split(" ");
if (ebxHandler.getEBXFiles()!=null&&split.length==2){//DEBUG-
EBXFile file = ebxHandler.getEBXFileByGUID(split[0], false/*aka. don't try to load*/, false);
if (file!=null){//Table with EBXFile
return file.getTruePath()+" "+split[1];
}else{//Table with ResourceLink's Name
ResourceLink resLink = rs.getResourceLinkByEBXGUID(split[0]);
if (resLink!=null){
return resLink.getName()+" "+split[1];
}
}
}
return (String)item.getValue();
case CHUNKGUID:
return (String)item.getValue();
default:
return "null"; //UNDEFINED NULL
}
}else{
return "null";
}
}
public Object convertToObject(String value, TreeViewEntry item){
try{
EBXHandler ebxHandler = Core.getGame().getResourceHandler().getEBXHandler();
if (value.equals("null")){//hasNoPayloadData! aka. undefined null
return null;
}else{
switch(item.getType()){
case STRING:
return(value);
case ENUM:
return(item.getValue()/*RETURNS STRING(if null) OR HASHMAP*/);
case HEX8:
return(value);
case LIST:
return(value);
case ARRAY:
return(value);
case FLOAT:
float f = Float.valueOf(value);
ebxHandler.getModifyHandler().addChange(ebxFile.getGuid(), ebxFile.getByteOrder(), isOriginal, item.getOffset(), f, EntryType.FLOAT);
return(f);
case DOUBLE:
return(Double.valueOf(value));
case SHORT:
short sh = Short.valueOf(value);
ebxHandler.getModifyHandler().addChange(ebxFile.getGuid(), ebxFile.getByteOrder(), isOriginal, item.getOffset(), sh, EntryType.SHORT);
return(sh);
case INTEGER:
Integer i = Integer.valueOf(value);
ebxHandler.getModifyHandler().addChange(ebxFile.getGuid(), ebxFile.getByteOrder(), isOriginal, item.getOffset(), i, EntryType.INTEGER);
return(i);
case LONG:
Long lon = Long.valueOf(value);
ebxHandler.getModifyHandler().addChange(ebxFile.getGuid(), ebxFile.getByteOrder(), isOriginal, item.getOffset(), lon, EntryType.LONG);
return(lon);
case UINTEGER:
long l = (Long.valueOf(value))& 0xffffffffL;
Integer ui = Integer.valueOf((int) (l&0xFFFFFFFF));
ebxHandler.getModifyHandler().addChange(ebxFile.getGuid(), ebxFile.getByteOrder(), isOriginal, item.getOffset(), ui, EntryType.UINTEGER);
return(l);
case BYTE:
byte b = hexToByte(value);
ebxHandler.getModifyHandler().addChange(ebxFile.getGuid(), ebxFile.getByteOrder(), isOriginal, item.getOffset(), b, EntryType.BYTE);
return(b);
case RAW:
return(FileHandler.hexStringToByteArray(value));
case BOOL:
if (value.equals("TRUE")){
ebxHandler.getModifyHandler().addChange(ebxFile.getGuid(), ebxFile.getByteOrder(), isOriginal, item.getOffset(), (byte) 0x1, EntryType.BOOL);
return true;
}else{
ebxHandler.getModifyHandler().addChange(ebxFile.getGuid(), ebxFile.getByteOrder(), isOriginal, item.getOffset(), (byte) 0x0, EntryType.BOOL);
return false;
}
case NULL:
return("NULL"); //DEFINED NULL ("NULL")
case GUID:
if (value.contains("/")){
String[] split = value.split(" ");
if (split.length==2){
EBXFile file = ebxHandler.getEBXFileByTrueFileName(split[0]);//Table with EBXFile
if (file!=null){
return (file.getGuid()+" "+split[1]);
}else{
String guid = ebxHandler.getEBXGUIDByResourceName(split[0]);
if (guid!=null){
return (guid+" "+split[1]);
}
}
}
System.err.println("EXTERNAL GUID PATH COULD NOT BE FOUND IN DATABASE. NO CONVERTION TO FILEGUID POSSIBLE!");
return("ERROR");
}else{
return(value);
}
case CHUNKGUID:
return(value);
case SHA1:
return(value);
default:
return null; //UNDEFINED NULL ("null")
}
}
}catch(Exception e){
e.printStackTrace();
System.err.println("Couldn't not parse entry with name "+item.getName()+" in JavaFXTreeCellFactory!");
return null;
}
}
byte hexToByte(String s) {
return (byte)((Character.digit(s.charAt(0), 16) << 4) + Character.digit(s.charAt(1), 16));
}
String byteToHex(byte in) {
return String.format("%02x", in).toUpperCase();
}
}