package net.suncrescent.clicker.robot;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import net.suncrescent.clicker.robot.RobotInteractionProvider.RobotControlProvider;
import net.suncrescent.clicker.robot.RobotInteractionProvider.RobotEventProvider;
import net.suncrescent.clicker.robot.RobotInteractionProvider.RobotStatus;
import net.suncrescent.clicker.robot.RobotInteractionProvider.RobotStatusProvider;
import net.suncrescent.clicker.robot.action.Action;
import net.suncrescent.clicker.robot.action.Condition;
import org.apache.log4j.Logger;
public class Robot {
private final static Logger log = Logger.getLogger(Robot.class);
private final java.awt.Robot robot;
private final ArrayList<Action> actionSequence = new ArrayList<Action>();
private final RobotInteractionProvider interactionProvider;
private Boolean previousConditionResult = null;
private int repeatCount = 0;
public Robot() throws AWTException {
Robot.log.debug("Robot constructor");
this.robot = new java.awt.Robot();
this.robot.setAutoWaitForIdle(true);
this.interactionProvider = new RobotInteractionProvider(this);
}
public RobotStatusProvider getStatusProvider() {
return this.interactionProvider.getStatusProvider();
}
public RobotControlProvider getControlProvider() {
return this.interactionProvider.getControlProvider();
}
public RobotEventProvider getEventProvider() {
return this.interactionProvider.getEventProvider();
}
public void setActionSequence(final List<Action> actions) {
this.statusCheck();
this.actionSequence.clear();
this.actionSequence.addAll(actions);
}
public void setRepeatCount(final int count) {
this.statusCheck();
this.repeatCount = count;
}
protected void executeWork() throws InterruptedException {
Robot.log.info("Execute work started. Number of actions: " + this.actionSequence.size() + ". Repeat count: " + this.repeatCount);
// loop
while (this.hasWorkingStatus() && this.repeatCount > 0) {
Robot.log.debug("Executing repeat count: " + this.repeatCount);
for (final Action a : this.actionSequence) {
Robot.log.debug("Executing action: " + a.toString());
// check condition
final Condition cond = a.getCondition();
if (cond == null || this.evaluateCondition(cond)) {
// position the pointer
this.smoothMove(a.getX(), a.getY());
this.robot.setAutoDelay(100 + (int) (Math.random() * 50));
// do button action
switch (a.getAction()) {
case LEFT_DOWN:
this.robot.mousePress(InputEvent.BUTTON1_MASK);
break;
case LEFT_UP:
this.robot.mouseRelease(InputEvent.BUTTON1_MASK);
break;
case MIDDLE_DOWN:
this.robot.mousePress(InputEvent.BUTTON2_MASK);
break;
case MIDDLE_UP:
this.robot.mouseRelease(InputEvent.BUTTON2_MASK);
break;
case RIGHT_DOWN:
this.robot.mousePress(InputEvent.BUTTON3_MASK);
break;
case RIGHT_UP:
this.robot.mouseRelease(InputEvent.BUTTON3_MASK);
break;
case LEFT_CLICK:
this.robot.mousePress(InputEvent.BUTTON1_MASK);
this.robot.mouseRelease(InputEvent.BUTTON1_MASK);
break;
case MIDDLE_CLICK:
this.robot.mousePress(InputEvent.BUTTON2_MASK);
this.robot.mouseRelease(InputEvent.BUTTON2_MASK);
break;
case RIGHT_CLICK:
this.robot.mousePress(InputEvent.BUTTON3_MASK);
this.robot.mouseRelease(InputEvent.BUTTON3_MASK);
break;
case KEY_B:
this.robot.keyPress(KeyEvent.VK_B);
this.robot.keyRelease(KeyEvent.VK_B);
break;
case KEY_W:
this.robot.keyPress(KeyEvent.VK_W);
this.robot.keyRelease(KeyEvent.VK_W);
break;
case KEY_S:
this.robot.keyPress(KeyEvent.VK_S);
this.robot.keyRelease(KeyEvent.VK_S);
break;
case KEY_X:
this.robot.keyPress(KeyEvent.VK_X);
this.robot.keyRelease(KeyEvent.VK_X);
break;
case NONE:
// nothing happens!
break;
}
// pause?
if (a.getPause() > 0) {
int pause = a.getPause();
Robot.log.debug("Pause of " + pause + "ms");
while (pause > 0 && this.hasWorkingStatus()) {
Thread.sleep(Math.min(pause, 100));
pause -= 100;
}
}
}
if (!this.hasWorkingStatus()) {
return;
}
// lets be polite
Thread.yield();
}
this.interactionProvider.getEventProvider().notifyRepeatCountChange(--this.repeatCount);
// paused by controller?
while (this.hasWorkingStatus() && this.interactionProvider.getStatusProvider().getStatus() == RobotStatus.PAUSED) {
Thread.sleep(100);
}
}
Robot.log.info("Execute work complete");
}
private boolean evaluateCondition(final Condition condition) {
Robot.log.debug("Condition evaluate. Type: " + condition.getConditionType());
boolean result = false;
final Object[] params = condition.getConditionParameters();
// evaluate
switch (condition.getConditionType()) {
case PIXEL_COLOR_MATCHES:
if (params != null && params.length == 2) {
final Point position = (Point) params[0];
final Color color = (Color) params[1];
final Color pixelColor = this.robot.getPixelColor(position.x, position.y);
Robot.log.debug("Position (" + position.x + ", " + position.y + "). Expected color: " + color.toString() + ". Pixel color: "
+ pixelColor.toString());
result = (color.equals(pixelColor));
}
break;
case PIXEL_COLOR_NOT_MATCHES:
if (params != null && params.length == 2) {
final Point position = (Point) params[0];
final Color color = (Color) params[1];
final Color pixelColor = this.robot.getPixelColor(position.x, position.y);
Robot.log.debug("Position (" + position.x + ", " + position.y + "). Not expected color: " + color.toString() + ". Pixel color: "
+ pixelColor.toString());
result = (!color.equals(pixelColor));
}
break;
case PREVIOUS_CONDITION_TRUE:
result = (this.previousConditionResult != null && this.previousConditionResult == true);
break;
case PREVIOUS_CONDITION_FALSE:
result = (this.previousConditionResult == null || this.previousConditionResult == false);
break;
}
// check for chained operation
if (condition.getChainedCondition() != null) {
switch (condition.getChainedOperator()) {
default:
case AND:
result = (result && this.evaluateCondition(condition.getChainedCondition()));
break;
case EQUAL:
result = (result == this.evaluateCondition(condition.getChainedCondition()));
break;
case NOT_EQUAL:
result = (result != this.evaluateCondition(condition.getChainedCondition()));
break;
case OR:
result = (result || this.evaluateCondition(condition.getChainedCondition()));
break;
case XOR:
result = ((result == true && this.evaluateCondition(condition.getChainedCondition()) == false) || (result == false && this
.evaluateCondition(condition.getChainedCondition()) == true));
break;
}
}
this.previousConditionResult = result;
Robot.log.debug("Condition evaluates as: " + (result ? "true" : "false"));
return result;
}
private void smoothMove(final int x, final int y) {
Robot.log.info("Mouse move to (" + x + ", " + y + ")");
final PointerInfo pInfo = MouseInfo.getPointerInfo();
final Point origin = pInfo.getLocation();
Robot.log.debug("Current pointer position (" + origin.getX() + ", " + origin.getY() + ")");
int differenceX = (int) (x - origin.getX());
int differenceY = (int) (y - origin.getY());
if (x == -1) {
differenceX = 0;
}
if (y == -1) {
differenceY = 0;
}
if (differenceX != 0 || differenceY != 0) {
final double step = (((int) Math.random() * 2 + 2) / Math.sqrt(Math.pow(differenceX, 2) + Math.pow(differenceY, 2)));
this.robot.setAutoDelay(2);
for (double current = 0.0; current < 1.0; current += step) {
final int stepX = (int) (origin.getX() + (differenceX * current));
final int stepY = (int) (origin.getY() + (differenceY * current));
Robot.log.debug("Step to (" + stepX + ", " + stepY + ")");
this.robot.mouseMove(stepX, stepY);
}
// just make sure we actually are at the right spot
this.robot.setAutoDelay(450 + (int) (Math.random() * 100));
this.robot.mouseMove(x, y);
this.robot.setAutoDelay(0);
}
}
private boolean hasWorkingStatus() {
final RobotStatus status = this.interactionProvider.getStatusProvider().getStatus();
return status == RobotStatus.PAUSED || status == RobotStatus.RUNNING;
}
private void statusCheck() throws IllegalStateException {
if (this.hasWorkingStatus()) {
throw new IllegalStateException("This method can not be called while the robot is in a running or paused state.");
}
}
}