package amidst.map;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import amidst.logging.Log;
import amidst.map.layers.BiomeLayer;
public class Map {
public static Map instance = null;
private static final boolean START = true, END = false;
private FragmentManager fragmentManager;
private Fragment startNode = new Fragment();
private double scale = 0.25;
private Point2D.Double start;
public int tileWidth, tileHeight;
public int width = 1, height = 1;
private final Object resizeLock = new Object(), drawLock = new Object();
private AffineTransform mat;
private boolean firstDraw = true;
// TODO : This must be changed with the removal of ChunkManager
public Map(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
fragmentManager.setMap(this);
mat = new AffineTransform();
start = new Point2D.Double();
addStart(0, 0);
instance = this;
}
public void resetImageLayer(int id) {
Fragment frag = startNode;
while (frag.hasNext) {
frag = frag.nextFragment;
fragmentManager.repaintFragmentLayer(frag, id);
}
}
public void resetFragments() {
Fragment frag = startNode;
while (frag.hasNext) {
frag = frag.nextFragment;
fragmentManager.repaintFragment(frag);
}
}
public void draw(Graphics2D g, float time) {
AffineTransform originalTransform = g.getTransform();
if (firstDraw) {
firstDraw = false;
centerOn(0, 0);
}
synchronized (drawLock) {
int size = (int) (Fragment.SIZE * scale);
int w = width / size + 2;
int h = height / size + 2;
while (tileWidth < w) addColumn(END);
while (tileWidth > w) removeColumn(END);
while (tileHeight < h) addRow(END);
while (tileHeight > h) removeRow(END);
while (start.x > 0) { start.x -= size; addColumn(START); removeColumn(END); }
while (start.x < -size) { start.x += size; addColumn(END); removeColumn(START); }
while (start.y > 0) { start.y -= size; addRow(START); removeRow(END); }
while (start.y < -size) { start.y += size; addRow(END); removeRow(START); }
Fragment frag = startNode;
size = Fragment.SIZE;
if (frag.hasNext) {
mat.setToIdentity();
mat.concatenate(originalTransform);
mat.translate(start.x, start.y);
mat.scale(scale, scale);
while (frag.hasNext) {
frag = frag.nextFragment;
frag.drawImageLayers(time, g, mat);
mat.translate(size, 0);
if (frag.endOfLine)
mat.translate(-size * w, size);
}
}
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
fragmentManager.updateLayers(time);
frag = startNode;
if (frag.hasNext) {
mat.setToIdentity();
mat.concatenate(originalTransform);
mat.translate(start.x, start.y);
mat.scale(scale, scale);
while (frag.hasNext) {
frag = frag.nextFragment;
frag.drawLiveLayers(time, g, mat);
mat.translate(size, 0);
if (frag.endOfLine)
mat.translate(-size * w, size);
}
}
frag = startNode;
if (frag.hasNext) {
mat.setToIdentity();
mat.concatenate(originalTransform);
mat.translate(start.x, start.y);
mat.scale(scale, scale);
while (frag.hasNext) {
frag = frag.nextFragment;
frag.drawObjects(g, mat);
mat.translate(size, 0);
if (frag.endOfLine)
mat.translate(-size * w, size);
}
}
g.setTransform(originalTransform);
}
}
public void addStart(int x, int y) {
synchronized (resizeLock) {
Fragment start = fragmentManager.requestFragment(x, y);
start.endOfLine = true;
startNode.setNext(start);
tileWidth = 1;
tileHeight = 1;
}
}
public void addColumn(boolean start) {
synchronized (resizeLock) {
int x = 0;
Fragment frag = startNode;
if (start) {
x = frag.nextFragment.blockX - Fragment.SIZE;
Fragment newFrag = fragmentManager.requestFragment(x, frag.nextFragment.blockY);
newFrag.setNext(startNode.nextFragment);
startNode.setNext(newFrag);
}
while (frag.hasNext) {
frag = frag.nextFragment;
if (frag.endOfLine) {
if (start) {
if (frag.hasNext) {
Fragment newFrag = fragmentManager.requestFragment(x, frag.blockY + Fragment.SIZE);
newFrag.setNext(frag.nextFragment);
frag.setNext(newFrag);
frag = newFrag;
}
} else {
Fragment newFrag = fragmentManager.requestFragment(frag.blockX + Fragment.SIZE, frag.blockY);
if (frag.hasNext) {
newFrag.setNext(frag.nextFragment);
}
newFrag.endOfLine = true;
frag.endOfLine = false;
frag.setNext(newFrag);
frag = newFrag;
}
}
}
tileWidth++;
}
}
public void removeRow(boolean start) {
synchronized (resizeLock) {
if (start) {
for (int i = 0; i < tileWidth; i++) {
Fragment frag = startNode.nextFragment;
frag.remove();
fragmentManager.returnFragment(frag);
}
} else {
Fragment frag = startNode;
while (frag.hasNext)
frag = frag.nextFragment;
for (int i = 0; i < tileWidth; i++) {
frag.remove();
fragmentManager.returnFragment(frag);
frag = frag.prevFragment;
}
}
tileHeight--;
}
}
public void addRow(boolean start) {
synchronized (resizeLock) {
Fragment frag = startNode;
int y;
if (start) {
frag = startNode.nextFragment;
y = frag.blockY - Fragment.SIZE;
} else {
while (frag.hasNext)
frag = frag.nextFragment;
y = frag.blockY + Fragment.SIZE;
}
tileHeight++;
Fragment newFrag = fragmentManager.requestFragment(startNode.nextFragment.blockX, y);
Fragment chainFrag = newFrag;
for (int i = 1; i < tileWidth; i++) {
Fragment tempFrag = fragmentManager.requestFragment(chainFrag.blockX + Fragment.SIZE, chainFrag.blockY);
chainFrag.setNext(tempFrag);
chainFrag = tempFrag;
if (i == (tileWidth - 1))
chainFrag.endOfLine = true;
}
if (start) {
chainFrag.setNext(frag);
startNode.setNext(newFrag);
} else {
frag.setNext(newFrag);
}
}
}
public void removeColumn(boolean start) {
synchronized (resizeLock) {
Fragment frag = startNode;
if (start) {
fragmentManager.returnFragment(frag.nextFragment);
startNode.nextFragment.remove();
}
while (frag.hasNext) {
frag = frag.nextFragment;
if (frag.endOfLine) {
if (start) {
if (frag.hasNext) {
Fragment tempFrag = frag.nextFragment;
tempFrag.remove();
fragmentManager.returnFragment(tempFrag);
}
} else {
frag.prevFragment.endOfLine = true;
frag.remove();
fragmentManager.returnFragment(frag);
frag = frag.prevFragment;
}
}
}
tileWidth--;
}
}
public void moveBy(Point2D.Double speed) {
moveBy(speed.x, speed.y);
}
public void moveBy(double x, double y) {
start.x += x;
start.y += y;
}
public void centerOn(long x, long y) {
long fragOffsetX = x % Fragment.SIZE;
long fragOffsetY = y % Fragment.SIZE;
long startX = x - fragOffsetX;
long startY = y - fragOffsetY;
synchronized (drawLock) {
while (tileHeight > 1) removeRow(false);
while (tileWidth > 1) removeColumn(false);
Fragment frag = startNode.nextFragment;
frag.remove();
fragmentManager.returnFragment(frag);
// TODO: Support longs?
double offsetX = width >> 1;
double offsetY = height >> 1;
offsetX -= (fragOffsetX)*scale;
offsetY -= (fragOffsetY)*scale;
start.x = offsetX;
start.y = offsetY;
addStart((int)startX, (int)startY);
}
}
public void setZoom(double scale) {
this.scale = scale;
}
public double getZoom() {
return scale;
}
public Point2D.Double getScaled(double oldScale, double newScale, Point p) {
double baseX = p.x - start.x;
double scaledX = baseX - (baseX / oldScale) * newScale;
double baseY = p.y - start.y;
double scaledY = baseY - (baseY / oldScale) * newScale;
return new Point2D.Double(scaledX, scaledY);
}
public void dispose() {
synchronized (drawLock) {
fragmentManager.reset();
}
}
public Fragment getFragmentAt(Point position) {
Fragment frag = startNode;
Point cornerPosition = new Point(position.x >> Fragment.SIZE_SHIFT, position.y >> Fragment.SIZE_SHIFT);
Point fragmentPosition = new Point();
while (frag.hasNext) {
frag = frag.nextFragment;
fragmentPosition.x = frag.getFragmentX();
fragmentPosition.y = frag.getFragmentY();
if (cornerPosition.equals(fragmentPosition))
return frag;
}
return null;
}
public MapObject getObjectAt(Point position, double maxRange) {
double x = start.x;
double y = start.y;
MapObject closestObject = null;
double closestDistance = maxRange;
Fragment frag = startNode;
int size = (int) (Fragment.SIZE * scale);
while (frag.hasNext) {
frag = frag.nextFragment;
for (int i = 0; i < frag.objectsLength; i ++) {
if (frag.objects[i].parentLayer.isVisible()) {
Point objPosition = frag.objects[i].getLocation();
objPosition.x *= scale;
objPosition.y *= scale;
objPosition.x += x;
objPosition.y += y;
double distance = objPosition.distance(position);
if (distance < closestDistance) {
closestDistance = distance;
closestObject = frag.objects[i];
}
}
}
x += size;
if (frag.endOfLine) {
x = start.x;
y += size;
}
}
return closestObject;
}
public Point screenToLocal(Point inPoint) {
Point point = inPoint.getLocation();
point.x -= start.x;
point.y -= start.y;
// TODO: int -> double -> int = bad?
point.x /= scale;
point.y /= scale;
point.x += startNode.nextFragment.blockX;
point.y += startNode.nextFragment.blockY;
return point;
}
public String getBiomeNameAt(Point point) {
Fragment frag = startNode;
while (frag.hasNext) {
frag = frag.nextFragment;
if ((frag.blockX <= point.x) &&
(frag.blockY <= point.y) &&
(frag.blockX + Fragment.SIZE > point.x) &&
(frag.blockY + Fragment.SIZE > point.y)) {
int x = point.x - frag.blockX;
int y = point.y - frag.blockY;
return BiomeLayer.getBiomeNameForFragment(frag, x, y);
}
}
return "Unknown";
}
public String getBiomeAliasAt(Point point) {
Fragment frag = startNode;
while (frag.hasNext) {
frag = frag.nextFragment;
if ((frag.blockX <= point.x) &&
(frag.blockY <= point.y) &&
(frag.blockX + Fragment.SIZE > point.x) &&
(frag.blockY + Fragment.SIZE > point.y)) {
int x = point.x - frag.blockX;
int y = point.y - frag.blockY;
return BiomeLayer.getBiomeAliasForFragment(frag, x, y);
}
}
return "Unknown";
}
}