package com.rs.tools;

import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.swing.JFrame;

import com.rs.cache.Cache;
import com.rs.cache.loaders.Floor2Definitions;
import com.rs.cache.loaders.FloorDefinitions;
import com.rs.cache.loaders.IndexedColorImageFile;
import com.rs.cache.loaders.MapSpriteDefinitions;
import com.rs.cache.loaders.ObjectDefinitions;
import com.rs.io.InputStream;
import com.rs.utils.Utils;

public class MapGenerator extends JFrame implements MouseListener, MouseMotionListener, MouseWheelListener {

	private static final long serialVersionUID = -6054276817868497393L;

	public static void main(String[] args) throws IOException {
		Cache.init();
		Frame f = new MapGenerator();

	}

	public MapGenerator() {
		xPos = 1500;
		yPos = 1500;
		scale = 1;
		ratio = 20;
		setTitle("Dragonkk map viewer");
		setSize(640, 640);
		setVisible(true);
		addMouseListener(this);
		addMouseMotionListener(this);
		addMouseWheelListener(this);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		checkLoad();

	}

	private int xPos, yPos, planePos, maxRX, maxRY, rxPos, ryPos, ratio;
	private double scale;

	private BufferedImage map;
	private BufferedImage screen;
	private boolean loading;
	private boolean forceNoRefresh;

	public void checkLoad() {
		if (!loading) {
			final int newRX = xPos / 64;
			final int newRY = yPos / 64;
			final int newMaxRX = (int) ((getWidth() / 64) / scale) + (int) (1 * scale) + ratio * 2;
			final int newMaxRY = (int) ((getHeight() / 64) / scale) + (int) (1 * scale) + ratio * 2;
			if (Math.abs(newRX - rxPos) >= ratio / 2 || Math.abs(newRY - ryPos) >= ratio / 2 || newMaxRX != maxRX || newMaxRY != maxRY || forceNoRefresh) {
				loading = true;
				repaint();
				Thread t = new Thread() {

					@Override
					public void run() {
						map = getMap(newRX, newRY, newMaxRX, newMaxRY);
						rxPos = newRX;
						ryPos = newRY;
						maxRX = newMaxRX;
						maxRY = newMaxRY;
						loading = false;
						forceNoRefresh = false;
						repaint();
					}

				};
				t.setPriority(Thread.MIN_PRIORITY);
				t.setDaemon(true);
				t.start();
			}
		}
	}

	@Override
	public void paint(Graphics g) {
		/*
		 * for(int x = 0; x < 10; x++) { for(int y = 0; y < 10; y++) { int regionId = ((x+xPos) << 8) | (y + yPos);
		 * BufferedImage[] data = getMap(regionId); if(data == null) continue; g.drawImage(data[planePos], x*64,
		 * 640-y*64, null);
		 * 
		 * } }
		 */
		screen = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
		Graphics g2 = screen.getGraphics();
		int localX = xPos & 63;
		int localY = yPos & 63;
		// System.out.println(localX+", "+localY);

		if (map != null && !forceNoRefresh)
			g2.drawImage(map, (int) (((int) (-localX) + (rxPos - xPos / 64) * 64 - ratio * 64) * scale), (int) ((-((64 + (ryPos - yPos / 64) * 64 + ratio * 64) - localY)) * scale), this);
		g2.setColor(Color.PINK);
		if (loading)
			g2.drawString("Loading", 50, 50);
		if (mouseLocation2 != null) {
			int[] pos = getPos();
			g2.drawString("Position: " + pos[0] + ", " + pos[1], 50, 60);
		}
		g2.dispose();
		g.drawImage(screen, 0, 0, this);
	}

	public int[] getPos() {
		return new int[] { (int) (xPos + ratio * 64 + mouseLocation2.getX() / scale), (int) (yPos + ratio * 64 + (getHeight() - mouseLocation2.getY() / scale)) };
	}

	public BufferedImage getMap(int rxPos, int ryPos, int maxRX, int maxRY) {
		int[][] rgb = new int[(int) (maxRX * 64 * scale)][(int) (maxRY * 64 * scale)];
		int[] a1 = new int[(int) ((64 * maxRX) * (64 * maxRY) * scale)];
		int[] a2 = new int[(int) ((64 * maxRX) * (64 * maxRY) * scale)];
		int[] a3 = new int[(int) ((64 * maxRX) * (64 * maxRY) * scale)];
		int[] a4 = new int[(int) ((64 * maxRX) * (64 * maxRY) * scale)];
		int[] a5 = new int[(int) ((64 * maxRX) * (64 * maxRY) * scale)];
		BufferedImage img = new BufferedImage(rgb.length, rgb[0].length, BufferedImage.TYPE_INT_RGB);
		int[][] dataA = new int[64 * maxRX][64 * maxRY];
		int[][] dataB = new int[64 * maxRX][64 * maxRY];

		// part1 grab data

		for (int rx = 0; rx < maxRX; rx++) {
			for (int ry = 0; ry < maxRY; ry++) {
				int regionId = ((rx + rxPos) << 8) | (ry + ryPos);
				int regionX = (regionId >> 8);
				int regionY = (regionId & 0xff);
				int archiveId = Utils.getMapArchiveId(regionX, regionY);
				byte[] data = Cache.STORE.getIndexes()[5].getFile(archiveId, 3);
				if (data == null)
					continue;

				InputStream stream = new InputStream(data);
				for (int plane = 0; plane < 4; plane++) {
					for (int x = 0; x < 64; x++) {
						for (int y = 0; y < 64; y++) { // edges / street
							int value = stream.readUnsignedByte();
							if ((value & 0x1) != 0) {
								int v = stream.readUnsignedByte();
								int v2 = stream.readUnsignedSmart();
								if (plane == planePos)
									dataA[rx * 64 + x][ry * 64 + y] = v2;
							}
							if ((value & 0x2) != 0) {
								byte v = (byte) stream.readByte();
							}
							if ((value & 0x4) != 0) { // floor tiles
								int v = stream.readUnsignedSmart(); // setted to
								// 30
								if (plane == planePos)
									dataB[rx * 64 + x][ry * 64 + y] = v;
							}
							if ((value & 0x8) != 0) {
								stream.readUnsignedByte();
							}
						}
					}
				}
			}
		}

		// part2 work data

		for (int x = -5; x < rgb.length; x++) {
			for (int y = 0; y < rgb[0].length; y++) {
				int x2 = 5 + x;
				if (x2 < rgb.length) {
					int i_98_ = ((dataB[(int) (x2 / scale)][(int) (y / scale)]) & 0x7fff);
					if (i_98_ > 0) {
						FloorDefinitions defs = FloorDefinitions.getFloorDefinitions(i_98_ - 1);
						a1[y] += defs.anInt6001;
						a2[y] += defs.anInt6007;
						a3[y] += defs.anInt6002;
						a4[y] += defs.anInt6008;
						a5[y]++;
					}
				}
				int x3 = x - 5;
				if (x3 >= 0) {
					int i_100_ = ((dataB[(int) (x3 / scale)][(int) (y / scale)]) & 0x7fff);
					if (i_100_ > 0) {
						FloorDefinitions defs = FloorDefinitions.getFloorDefinitions(i_100_ - 1);
						a1[y] -= defs.anInt6001;
						a2[y] -= defs.anInt6007;
						a3[y] -= defs.anInt6002;
						a4[y] -= defs.anInt6008;
						a5[y]--;
					}
				}
			}

			if (x >= 0) {
				int c1 = 0;
				int c2 = 0;
				int c3 = 0;
				int r2 = 0;
				int r3 = 0;
				for (int y = -5; y < rgb[0].length; y++) {
					int i_107_ = y + 5;
					if (i_107_ < rgb[0].length) {
						c1 += a1[i_107_];
						c2 += a2[i_107_];
						c3 += a3[i_107_];
						r2 += a4[i_107_];
						r3 += a5[i_107_];
					}
					int i_108_ = y - 5;
					if (i_108_ >= 0) {
						c1 -= a1[i_108_];
						c2 -= a2[i_108_];
						c3 -= a3[i_108_];
						r2 -= a4[i_108_];
						r3 -= a5[i_108_];
					}
					// sets map
					if (y >= 0 && r3 > 0) {

						rgb[x][y] = new Color(c1 / r3, c2 / r3, c3 / r3).getRGB();
					}
				}
			}
		}
		for (int x = 0; x < rgb.length; x++) {
			for (int y = 0; y < rgb[0].length; y++) {
				if (((dataA[(int) (x / scale)][(int) (y / scale)]) & 0x7fff) > 0) {
					Floor2Definitions defs = Floor2Definitions.getFloorDefinitions(((dataA[(int) (x / scale)][(int) (y / scale)]) & 0x7fff) - 1);
					int r = defs.anInt6323 != -1 ? defs.anInt6323 : defs.anInt6320;
					if (r == 16711935) // rs turns pink into black
						r = 0;
					int red = (r >> 16 & 0xff);
					int green = (r >> 8 & 0xff);
					int blue = (r & 0xff);

					img.setRGB(x, (rgb[0].length - 1) - y, new Color(red, green, blue).getRGB());
				} else {
					img.setRGB(x, (rgb[0].length - 1) - y, rgb[x][y]);
				}
			}
		}

		// set map sprites
		for (int rx = 0; rx < maxRX; rx++) {
			for (int ry = 0; ry < maxRY; ry++) {
				int regionId = ((rx + rxPos) << 8) | (ry + ryPos);
				int regionX = (regionId >> 8);
				int regionY = (regionId & 0xff);
				int archiveId = Utils.getMapArchiveId(regionX, regionY);
				byte[] data = Cache.STORE.getIndexes()[5].getFile(archiveId, 0);
				if (data == null)
					continue;
				InputStream landStream = new InputStream(data);
				int objectId = -1;
				int incr;
				while ((incr = landStream.readSmart2()) != 0) {
					objectId += incr;
					int location = 0;
					int incr2;
					while ((incr2 = landStream.readUnsignedSmart()) != 0) {
						location += incr2 - 1;
						int localX = (location >> 6 & 0x3f);
						int localY = (location & 0x3f);
						int plane = location >> 12;
						int objectData = landStream.readUnsignedByte();
						int type = objectData >> 2;
						int rotation = objectData & 0x3;
						if (localX < 0 || localX >= 64 || localY < 0 || localY >= 64)
							continue;
						/*
						 * int objectPlane = plane; if (mapSettings != null && (mapSettings[1][localX][localY] & 2)
						 * == 2) objectPlane--; if (objectPlane < 0 || objectPlane >= 4 || plane < 0 || plane >= 4)
						 * continue;
						 */

						// System.out.println(spriteId);
						if (plane == planePos) {
							int mapSpriteId = ObjectDefinitions.getObjectDefinitions(objectId).mapSpriteId;
							if (mapSpriteId == -1)
								continue;
							int spriteId = MapSpriteDefinitions.getMapSpriteDefinitions(mapSpriteId).spriteId;
							if (spriteId == -1)
								continue;
							IndexedColorImageFile sprite = new IndexedColorImageFile(Cache.STORE, spriteId, 0);
							BufferedImage s = sprite.getImages()[0];
							int width = (int) (s.getWidth() / 2 * scale);
							int height = (int) (s.getHeight() / 2 * scale);
							if (width == 0 || height == 0)
								continue;
							/*
							 * if(width > s.getWidth() || height > s.getHeight()) { width = s.getWidth(); height =
							 * s.getHeight();
							 * 
							 * }
							 */

							img.getGraphics().drawImage(s.getScaledInstance(width, height, 0), (int) ((rx * 64 + localX) * scale), (int) (((maxRY * 64 - 1) - (ry * 64 + localY)) * scale), null);
						}

						// spawnObject(new WorldObject(objectId, type, rotation,
						// localX + regionX*64, localY + regionY*64,
						// objectPlane), objectPlane, localX, localY, true);
					}
				}
			}
		}

		return img;

	}

	public static final int getV(int i, int i_1_, int i_2_) {
		if (i_2_ > 243)
			i_1_ >>= 4;
		else if (i_2_ > 217)
			i_1_ >>= 3;
		else if (i_2_ > 192)
			i_1_ >>= 2;
		else if (i_2_ > 179)
			i_1_ >>= 1;
		return (i_2_ >> 1) + (((i & 0xff) >> 2 << 10) + (i_1_ >> 5 << 7));
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		mouseLocation = e.getPoint();
		int[] pos = getPos();
		StringSelection selection = new StringSelection(pos[0] + " " + pos[1] + " " + planePos);

		Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
		clipboard.setContents(selection, selection);
		e.consume();
	}

	private Point mouseLocation;

	private Point mouseLocation2;

	@Override
	public void mousePressed(MouseEvent e) {
		mouseLocation = e.getPoint();
		e.consume();
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		drag(e.getPoint());
		e.consume();
	}

	public void drag(Point newLoc) {
		int diffX = (int) ((mouseLocation.x - newLoc.x) / scale);
		int diffY = (int) ((mouseLocation.y - newLoc.y) / scale);
		if (diffX != 0 || diffY != 0) {
			xPos += diffX;
			yPos -= diffY;
			mouseLocation = newLoc;
			checkLoad();
			repaint();
		}
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseDragged(MouseEvent e) {
		drag(e.getPoint());
		e.consume();

	}

	@Override
	public void mouseMoved(MouseEvent e) {
		mouseLocation2 = e.getPoint();
		this.repaint();
	}

	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		if (!loading) {
			if (e.getUnitsToScroll() > 0) {
				if (e.isControlDown())
					planePos = (planePos - 1) & 0x3;
				else
					scale /= 1.5;

			} else {
				if (e.isControlDown())
					planePos = (planePos + 1) & 0x3;
				else {
					scale *= 1.5;
				}
			}
			forceNoRefresh = true;
			checkLoad();
		}

	}

}
