package it.paspiz85.nanobot.game;
import it.paspiz85.nanobot.platform.Platform;
import it.paspiz85.nanobot.util.Area;
import it.paspiz85.nanobot.util.Config;
import it.paspiz85.nanobot.util.Point;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import org.sikuli.core.search.RegionMatch;
import org.sikuli.core.search.algorithm.TemplateMatcher;
/**
* Abstract screen.
*
* @author paspiz85
*
*/
public abstract class Screen {
private static final Map<String, Screen> INSTANCES = new HashMap<>();
private static final int THRESHOLD_DEFAULT = 5;
protected static final Area getArea(final String name) {
Area area = null;
final String value = Config.instance().getProperty(name);
if (value != null) {
final String[] split = value.split(",");
area = Area.byEdges(Integer.parseInt(split[0].trim()), Integer.parseInt(split[1].trim()),
Integer.parseInt(split[2].trim()), Integer.parseInt(split[3].trim()));
}
return area;
}
protected static final Color getColor(final String name) {
Color color = null;
final Integer value = getRGB(name);
if (value != null) {
color = new Color(value);
}
return color;
}
@SuppressWarnings("unchecked")
public static <P extends Screen> P getInstance(final Class<P> c) {
P parser = null;
if (c != null) {
parser = (P) INSTANCES.get(c.getName());
if (parser == null) {
try {
parser = c.newInstance();
INSTANCES.put(c.getName(), parser);
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
return parser;
}
protected static final int[][] getOffset(final String name) {
int[][] result = null;
final String value = Config.instance().getProperty(name);
if (value != null) {
final String[] points = value.split(";");
result = new int[points.length][];
for (int i = 0; i < points.length; i++) {
final String[] point = points[i].split(",");
result[i] = new int[point.length];
for (int j = 0; j < point.length; j++) {
result[i][j] = Integer.parseInt(point[j].trim());
}
}
}
return result;
}
private static int[][] getOffset(final String name, final String fallback) {
int[][] result = getOffset(name);
if (result == null) {
result = getOffset(fallback);
}
return result;
}
private static int[][][] getOffsets(final String name) {
return new int[][][] { getOffset(name + ".a", name), getOffset(name + ".b", name),
getOffset(name + ".c", name), getOffset(name + ".d", name) };
}
protected static final Point getPoint(final String name) {
Point point = null;
final String value = Config.instance().getProperty(name);
if (value != null) {
final String[] split = value.split(",");
point = new Point(Integer.parseInt(split[0].trim()), Integer.parseInt(split[1].trim()));
}
return point;
}
private static Integer getRGB(final String name) {
Integer color = null;
final String value = Config.instance().getProperty(name);
if (value != null) {
color = Integer.decode(value.replace("#", "0x").trim());
}
return color;
}
protected static final int[] getRGBs(final String name) {
int[] colors = null;
final String value = Config.instance().getProperty(name);
if (value != null) {
final String[] split = value.split(",");
colors = new int[split.length];
for (int i = 0; i < split.length; i++) {
colors[i] = Integer.decode(split[i].replace("#", "0x").trim());
}
}
return colors;
}
private static int[][] getRGBseries(final String name) {
return new int[][] { getRGBs(name + ".a"), getRGBs(name + ".b"), getRGBs(name + ".c"), getRGBs(name + ".d") };
}
private static int getThreshold(final String name) {
int result = THRESHOLD_DEFAULT;
final String value = Config.instance().getProperty(name + ".threshold");
if (value != null) {
result = Integer.parseInt(value.trim());
}
return result;
}
private static int[] getWidth(final String name) {
int[] result = null;
final String value = Config.instance().getProperty(name);
if (value != null) {
final String[] split = value.split(",");
result = new int[split.length];
for (int i = 0; i < split.length; i++) {
result[i] = Integer.parseInt(split[i].trim());
}
}
return result;
}
private static int[] getWidth(final String name, final String fallback) {
int[] result = getWidth(name);
if (result == null) {
result = getWidth(fallback);
}
return result;
}
private static int[][] getWidths(final String name) {
return new int[][] { getWidth(name + ".a", name), getWidth(name + ".b", name), getWidth(name + ".c", name),
getWidth(name + ".d", name) };
}
private final int[][][] colors;
/*
* Set this var for debugging image screen parsing
*/
private final Integer learnMode = null;
protected final Logger logger = Logger.getLogger(getClass().getName());
private final int[][][][] offsets;
protected final Platform platform = Platform.instance();
private final int[] thresholds;
private final int[][] widths;
Screen() {
offsets = new int[10][][][];
colors = new int[10][][];
thresholds = new int[10];
for (int i = 0; i < 10; i++) {
offsets[i] = getOffsets("digit." + i + ".offset");
colors[i] = getRGBseries("digit." + i + ".color");
thresholds[i] = getThreshold("digit." + i);
}
widths = getWidths("digit.widths");
}
protected final Rectangle findArea(final BufferedImage input, final InputStream in) {
Rectangle result = null;
try {
final BufferedImage tar = ImageIO.read(in);
final List<RegionMatch> doFindAll = TemplateMatcher.findMatchesByGrayscaleAtOriginalResolution(input, tar,
1, 0.9);
result = doFindAll.isEmpty() ? null : doFindAll.get(0).getBounds();
} catch (final IOException e) {
logger.log(Level.SEVERE, "Unable to read input", e);
}
return result;
}
protected final Rectangle findArea(final BufferedImage input, final URL url) {
Rectangle result = null;
try {
result = findArea(input, url.openStream());
} catch (final IOException e) {
logger.log(Level.SEVERE, "Unable to read url " + url, e);
}
return result;
}
public abstract boolean isDisplayed();
protected final boolean isDisplayedByImageSearch(final Consumer<Boolean> imageSearch) {
boolean result = false;
try {
imageSearch.accept(false);
result = true;
} catch (final Exception ex) {
logger.log(Level.FINE, getClass().getSimpleName() + " not found");
}
return result;
}
private Integer parseDigit(final BufferedImage image, final Point start, final int type) {
Integer result = null;
for (int i = 0; i < 10; i++) {
final int[] expected = colors[i][type];
final int[] actual = new int[offsets[i][type].length];
for (int j = 0; j < actual.length; j++) {
actual[j] = image.getRGB(start.x() + offsets[i][type][j][0], start.y() + offsets[i][type][j][1]);
}
boolean found = true;
int count = 0;
for (int j = 0; j < actual.length; j++) {
final boolean compare = platform.compareColor(new Color(actual[j]), new Color(expected[j]),
thresholds[i]);
if (learnMode != null) {
image.setRGB(start.x() + offsets[i][type][j][0], start.y() + offsets[i][type][j][1], compare ? 0xFF
: 0xFF0000);
}
if (!compare) {
found = false;
if (learnMode == null) {
break;
}
} else {
count++;
}
}
if (learnMode != null && learnMode == i) {
String s = "";
for (final int element : actual) {
s += "#" + Integer.toHexString(element & 0xFFFFFF).toUpperCase() + ",";
}
System.out.println(s);
if (found) {
final File f = platform.saveImage(image, "test_" + System.currentTimeMillis() + "_found");
System.out.println(f.getAbsolutePath());
System.out.println();
} else {
final File f = platform.saveImage(image, "test_" + System.currentTimeMillis() + "_notfound_"
+ count);
System.out.println(f.getAbsolutePath());
System.out.println();
}
}
if (learnMode != null) {
for (int j = 0; j < actual.length; j++) {
image.setRGB(start.x() + offsets[i][type][j][0], start.y() + offsets[i][type][j][1], actual[j]);
}
}
if (found) {
result = i;
break;
}
}
return result;
}
protected Integer parseNumber(final BufferedImage image, final int type, final Point start, final int maxSearchWidth) {
String no = "";
int curr = start.x();
while (curr < start.x() + maxSearchWidth) {
final Integer i = parseDigit(image, new Point(curr, start.y()), type);
if (i != null) {
no += i;
curr += widths[type][i] - 1;
} else {
curr++;
}
}
return no.isEmpty() ? null : Integer.parseInt(no);
}
protected final Point relativePoint(final Point point, final Point root) {
Point result = null;
if (point != null) {
result = new Point(point.x() + root.x(), point.y() + root.y());
}
return result;
}
protected final Point searchImage(final URL resource, final Area area, final boolean debug) {
final BufferedImage image = platform.screenshot(area);
Point point = searchImageCenter(image, resource);
if (point == null) {
String message = "unable to find " + resource.getFile();
if (debug) {
final File f = platform.saveImage(image, "error_" + System.currentTimeMillis());
message += " in " + f.getAbsolutePath();
}
throw new IllegalArgumentException(message);
}
if (area != null) {
point = relativePoint(point, area.getEdge1());
}
return point;
}
protected final Point searchImageCenter(final BufferedImage image, final URL resource) {
Point result = null;
final Rectangle rectangle = findArea(image, resource);
if (rectangle != null) {
final int x = rectangle.getLocation().x + (int) (rectangle.getWidth() / 2);
final int y = rectangle.getLocation().y + (int) (rectangle.getHeight() / 2);
result = new Point(x, y);
}
return result;
}
}