package io.github.jgkamat.ViPaint.VimBar;
import io.github.jgkamat.ViPaint.Canvas.CursorCanvas;
import io.github.jgkamat.ViPaint.Canvas.PaintCanvas;
import io.github.jgkamat.ViPaint.Handlers.SetVariable;
import io.github.jgkamat.ViPaint.Handlers.SettingManager;
import io.github.jgkamat.ViPaint.Tools.KeyTool;
import io.github.jgkamat.ViPaint.ViPaint;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.VPos;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.scene.text.TextAlignment;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.nio.file.Files;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
/**
* A class to store, and parse vim bar commands.
*
* The function takes in a CommandManager Object,
* which will provide needed things that could possibly be changed.
*/
public class CommandManager {
private Map<String, Function<PropertyHolder, String>> commandList;
private CursorCanvas textOut;
private VimBar vimBar;
private ArrayList<String> history;
private int historyPointer;
private ViPaint layout;
private PaintCanvas drawCanvas;
public static final String introText =
"ViP - ViPaint\n\nVersion " + ViPaint.version + "\nby Jay Kamat"
+ "\nViPaint is open source and freely distributable"
+ "\n\nHelp poor children in CS1100!\n"
+ "Type :help cs1100<Enter> to help!"
+ "\n\ntype :q<Enter> to quit"
+ "\ntype :help<Enter> for help";
/**
* A class to handle all commands
* @param textOut The canvas where any help will be printed to
* @param vimBar The vimbar, in case we need to update its status
*/
public CommandManager(CursorCanvas textOut, PaintCanvas drawCanvas, ViPaint
layout, VimBar vimBar) {
commandList = new HashMap<>();
this.layout = layout;
this.textOut = textOut;
this.vimBar = vimBar;
this.drawCanvas = drawCanvas;
addDefaultCommands();
history = new ArrayList<>();
}
/**
* Adds normal commands! Dont forget to run this!
*/
private void addDefaultCommands() {
//command not found function <most important>
commandList.put("cnf", (PropertyHolder holder) -> {
return "Command \'" + holder.args[0] + "\' not found!";
});
// quit functions
commandList.put("quit", (PropertyHolder holder) -> {
System.exit(0);
// shouldn't happen but lets go for it.
return "--NORMAL--";
});
commandList.put("q", commandList.get("quit"));
//help function
commandList.put("help", (PropertyHolder holder) -> {
if (holder.args.length == 1) {
holder.printToGraphics("Welcome to ViPaint!\n\n"
+ "Vi/ViM knowlege is helpful but not required for ViPaint "
+ "usage!\n\n"
+ "ViPaint has a MODAL interface. This means that to "
+ "switch to\n"
+ "another command, you need to press ESC to getModeTool back to "
+ "normal mode!\n\n"
+ "Check which mode you are in in the lower left\n"
+ "Here is a short list of currently supported modes:\n\n"
+ "ESC - Normal Mode (Movement with H,J,K,L)\n"
+ "I - Insert Mode. A pen tool.\n"
+ "D - Delete Mode. An eraser.\n"
+ "R - Rectangle Tool. Move around, then press ESC to "
+ "confirm rectangle.\n"
+ "-/+ - Change size of cursor. Useful!\n"
+ "n - Line tool. Move around, press ESC to confirm!\n"
+ ":q to quit. Like I said earlier!"
+ "\n\nHelp is needed to add more tools! "
+ "(and ex commands)\n "
+ "Please submit a pull request!");
} else if (holder.args[1].equalsIgnoreCase("cs1100")) {
holder.printToGraphics("The people in CS1100 are in distress\n\n"
+ "If you are a TA or anyone else in authority you should\n"
+ "probably go and do something about it!\n\n"
+ "Go and take action at www.zombo.com");
} else {
Optional<? extends KeyTool> toHelp =
holder.layout.getKeyToolClasses().stream()
.filter((KeyTool tool) -> tool.getName().equalsIgnoreCase(holder.args[1])) .findFirst();
if (toHelp.isPresent()) {
holder.printToGraphics(toHelp.get().getHelp());
return "";
}
return "Help page \'" + holder.args[1] + "\' not found!";
}
return "";
});
// ???
commandList.put("moo", (PropertyHolder holder) -> {
StringBuilder b = new StringBuilder();
for(int i = 0; i < 1000; i++) {
for(int j = 0; j < 1000; j++) {
b.append("Uh Oh. ");
}
b.append('\n');
}
holder.printToGraphics(b.toString());
return "Moo.";
});
//intro command
commandList.put("intro", (PropertyHolder holder) -> {
holder.printToGraphics(introText);
return "";
});
PropertyHolder dummyHolder = new PropertyHolder(null, layout, textOut, drawCanvas, vimBar);
// set command
SetVariable graphics = new SetVariable(false, dummyHolder,
(holder) -> {holder.layout.toggleButtons(true); return true;},
(holder) -> {holder.layout.toggleButtons(false); return true;});
SettingManager.put("graphical", graphics);
SettingManager.put("g", graphics);
commandList.put("set", (PropertyHolder holder) -> {
if (holder.args.length <= 1) {
return "No argument specified!";
}
String str = holder.args[1];
if (str.indexOf("no") == 0) {
str = str.substring(2);
}
Object arg1 = SettingManager.get(str);
if (arg1 == null || !(arg1 instanceof SetVariable)) {
return "Bool value \'" + holder.args[1] + "\' not found!";
}
SetVariable arg = (SetVariable) arg1;
if (holder.args[1].indexOf("no") != 0) {
arg.set();
} else {
arg.unSet();
}
return "";
});
commandList.put("write", (PropertyHolder holder) -> {
if (holder.args.length < 2 || holder.args[1].equals("")) {
return "Incorrect Argument Specified!";
}
File file = new File(holder.args[1]);
try {
WritableImage writableImage =
new WritableImage((int) Math.round(holder.drawCanvas.getWidth()),
(int) Math.round(holder.drawCanvas.getHeight()));
holder.drawCanvas.snapshot(null, writableImage);
RenderedImage renderedImage = SwingFXUtils.fromFXImage(writableImage, null);
ImageIO.write(renderedImage, "png", file);
} catch (IOException ex) {
// System.out.println("FILE WRITE ERROR!");
return "File writing failed!";
}
return "";
});
commandList.put("w", (holder) ->
commandList.get("write").apply(holder));
commandList.put("clear", (holder) -> {
holder.drawCanvas.getGraphicsContext2D().clearRect(0, 0,
Short.MAX_VALUE, Short.MAX_VALUE);
return "";
});
commandList.put("cls", (holder) ->
commandList.get("clear").apply(holder));
commandList.put("open", (PropertyHolder holder) -> {
if (holder.args.length < 2 || holder.args[1].equals("")) {
return "Incorrect Argument Specified!";
}
File file = new File(holder.args[1]);
try {
// other text files can go in here
if (file.getName().matches(".*\\.(raw|txt|java|c|cpp|py)$")) {
holder.drawCanvas.clear();
holder.textOut.clear();
double width = RawConverter.getWidth(file);
width *= (int) SettingManager.get("rawthickness");
// Assume raw images are square
holder.layout.setSize(width, width);
RawConverter.writeToCanvas(holder.drawCanvas, file);
return "Opened \'" + holder.args[1] + "\' as a raw image";
}
BufferedImage img = ImageIO.read(file);
if (img == null) {
return "File '" + holder.args[1] + "' not supported!";
}
Image toCanvas = SwingFXUtils.toFXImage(img, null);
if (toCanvas.isError()) {
return "File '" + holder.args[1] + "' not found!";
}
holder.drawCanvas.clear();
holder.textOut.clear();
holder.layout.setSize(toCanvas.getRequestedWidth(), toCanvas.getRequestedHeight());
holder.drawCanvas.getGraphicsContext2D()
.drawImage(toCanvas, 0, 0, toCanvas.getRequestedWidth(),
toCanvas.getRequestedHeight());
} catch (IOException ex) {
ex.printStackTrace();
return "File \'" + holder.args[1] + "\' not found!";
}
return "";
});
commandList.put("o", (holder) ->
commandList.get("open").apply(holder));
commandList.put("let", (PropertyHolder holder) -> {
if (holder.args.length < 2 || holder.args[1].equals("")) {
return "Incorrect Argument Specified!";
}
Object value = SettingManager.mappings.get(holder.args[1]);
if (value == null) {
return "Setting '" + holder.args[1] + "' not found!";
}
if (value instanceof Integer) {
// matches an int
if (holder.args[2].matches("-?\\d+")) {
SettingManager.mappings.put(holder.args[1], Integer.parseInt(holder.args[2]));
return "Setting changed!";
} else {
return "Setting '" + holder.args[1] + "' is incorrect format!";
}
} else if (value instanceof Color) {
// matches a color
try {
SettingManager.mappings.put(holder.args[1], Color.valueOf(holder.args[2].toUpperCase()));
// nasty way of changing color.
// TODO change to a more elegant option
if (textOut != null) {
textOut.getGraphicsContext2D().setFill((Color) SettingManager.mappings.get("color"));
textOut.getLowerWindow().getGraphicsContext2D()
.setFill((Color) SettingManager.mappings.get("color"));
textOut.getLowerWindow().getLowerWindow().getGraphicsContext2D()
.setFill((Color) SettingManager.mappings.get("color"));
textOut.getGraphicsContext2D().setStroke((Color) SettingManager.mappings.get("color"));
textOut.getLowerWindow().getGraphicsContext2D()
.setStroke((Color) SettingManager.mappings.get("color"));
textOut.getLowerWindow().getLowerWindow().getGraphicsContext2D()
.setStroke((Color) SettingManager.mappings.get("color"));
}
return "Color changed to '" + holder.args[2] + "'";
} catch (IllegalArgumentException e) {
return "Color '" + holder.args[2] + "' not found!";
}
}
return "Unsupported Operation";
// plz implement me
});
commandList.put("source", (PropertyHolder holder) -> {
if (holder.args.length < 1 || holder.args[1].equals("")) {
return "Incorrect Argument Specified!";
}
String rawpath = holder.args[1];
// Make it easy to source ~/.viprc
if (rawpath.indexOf("~") == 0) {
rawpath = System.getProperty("user.home") + rawpath.substring(1);
}
Path path = FileSystems.getDefault().getPath(rawpath);
try {
Files.lines(path).forEach((toEval) -> {
if (!toEval.equals("")) {
parseCommand(toEval);
}
});
} catch (IOException e) {
return "File '" + rawpath + "' was not found!";
}
// The file was sourced correctly. Clear things.
return "";
});
}
/**
* Parses a command!
* @param commandName The name of the command to run
*/
public void parseCommand(String commandName) {
history.add(commandName);
restartHistory();
// Deal with comments
int commentLoc = commandName.indexOf('"');
if (commentLoc >= 0) {
commandName = commandName.substring(0, commentLoc);
}
if (commandName.equals("")) {
return;
}
commandName = commandName.trim();
String[] args = commandName.split("[\\s=]+");
Function<PropertyHolder, String> toRun = commandList.get(args[0]);
if(toRun == null) {
toRun = commandList.get("cnf");
}
if (vimBar != null) {
vimBar.setText(toRun.apply(new PropertyHolder(args, layout, textOut, drawCanvas, vimBar)));
} else {
toRun.apply(new PropertyHolder(args, layout, textOut, drawCanvas, vimBar));
}
}
/**
* Gets the previous element of history
* @return previous element
*/
public String getPrevious() {
if(historyPointer > 0) {
return history.get(--historyPointer);
} else if(history.size() > 0) {
return history.get(historyPointer);
} else {
return "";
}
}
/**
* Gets the next element of history
* @return next element
*/
public String getNext() {
if(historyPointer < history.size() - 1) {
return history.get(++historyPointer);
} else {
return "";
}
}
/**
* This method restarts the history to the latest point!
*/
private void restartHistory() {
historyPointer = history.size();
}
/**
* A class that is passed around to command functions so they can use it!
*/
public class PropertyHolder {
public CursorCanvas textOut;
public GraphicsContext gTextOut;
public String[] args;
public VimBar vimBar;
public ViPaint layout;
public PaintCanvas drawCanvas;
public PropertyHolder(String[] args, ViPaint layout,
CursorCanvas textOut, PaintCanvas drawCanvas, VimBar vimBar) {
this.args = args;
this.textOut = textOut;
if (this.textOut != null)
this.gTextOut = this.textOut.getGraphicsContext2D();
this.vimBar = vimBar;
this.layout = layout;
this.drawCanvas = drawCanvas;
}
public void printToGraphics(String toPrint) {
if (this.textOut == null) {
System.err.println("Could not display to graphics, as null was supplied");
return;
}
gTextOut.clearRect(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
gTextOut.setTextAlign(TextAlignment.CENTER);
gTextOut.setTextBaseline(VPos.CENTER);
gTextOut.fillText((toPrint),
Math.round(textOut.getWidth() / 2),
Math.round(textOut.getHeight() / 2));
textOut.setHelpOn();
}
}
}