/**
    KJF 03/20/2012 Initial coding.

    @author Kip Fiebig (KJF)
*/

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.print.*;
import java.io.*;
import java.net.*;
import javax.imageio.*;
import javax.swing.*;

public class MapPanel extends JPanel {

    private int[] data;
    private static final int WALL_OFFSET = 2048 + 4;
    private static final int MONSTER_OFFSET = 3072 + 4;
    private static final String[] MONSTERS = new String[] {
        "I",  // Imp
        "S",  // Skeleton
        "G",  // Goblin
        "Z",  // Zombie
        "V",  // Vampire
        "#",  // Giant Spider
        "M",  // Mummy
        "T",  // Troll
        "O",  // Ogre
        "W",  // Wraith
        "A",  // Afreet
        "D",  // Dragon
        "R",  // Rogue Dragon
    };
    private static final int TREASURE_OFFSET = 4096 + 4;
    private static final String[] TREASURES = new String[] {
        " ",  // no chest
        "0-16",  // random small treasure: (EMPTY, WORTHLESS JUNK, STRANDS OF COLORED BEADS, A BAG OF OLD COPPER COINS, A POUCH FILLED WITH SUNDRY COINS, A TINY SILVER STATUETTE, A PURSE FULL OF SILVER COINS)
        "2",  // STRANDS OF COLORED BEADS
        "4",  // A BAG OF OLD COPPER COINS
        "8",  // A POUCH FILLED WITH SUNDRY COINS
        "12",  // A TINY SILVER STATUETTE
        "16",  // A PURSE FULL OF SILVER COINS
        "28",  // A POUCH FILLED WITH OPALS
        "40",  // A LARGE GOLD EARRING
        "60",  // A SMALL BAG OF GOLD COINS
        "100",  // A LARGE RUBY
        "160",  // A $HUGE$ BLACK PEARL
        "200",  // >> A PLATINUM ARMBAND <<
        "260",  // ** A GIANT BLUE SAPPHIRE **
        "300",  // $ A FLAWLESS GIANT EMERALD $
        "400",  // $$ AN ENORMOUS ORANGE DIAMOND $$
        "IQs",  // TEN IRON QUARRELS
        "MQs",  // TEN MAGIC QUARRELS
        "TPs",  // FOUR VIALS OF TANA POWDER
        "Heal",  // A VIAL OF HEALING ELIXIR
        "The Magic Lamp",  // THE MAGIC LAMP is not hard-coded into a specific room
        "10K",  // MEGA-CROWN, worth 10000 gold (not that you need gold anymore since the game is over once you find the crown)
    };
    private static final int MAZE_WIDTH =   16;  // Rooms wide
    private static final int MAZE_HEIGHT =  64;  // Rooms high
    private static final int ROOM_WIDTH =   56;  // Pixels wide
    private static final int ROOM_HEIGHT =  46;  // Pixels high
    private static final Font TINY_FONT = new Font("SansSerif", Font.PLAIN, 9);

    private int pixelWidth, pixelHeight;
    private Image type8, type9, type9n, type9s, type9e, type9w;  // images of the interior walls for the "open" rooms
    FontMetrics fontMetrics;

    public MapPanel(int[] data) {
        this.data = data;
        pixelWidth = MAZE_WIDTH * ROOM_WIDTH + 1;
        pixelHeight = MAZE_HEIGHT * ROOM_HEIGHT + 1;
        setPreferredSize(new Dimension(pixelWidth, pixelHeight));
        fontMetrics = getFontMetrics(TINY_FONT);

        MediaTracker tracker = new MediaTracker(this);
        type8 = createImage(tracker, "type8.gif");
        type9 = createImage(tracker, "type9.gif");
        type9n = createImage(tracker, "type9n.gif");
        type9s = createImage(tracker, "type9s.gif");
        type9e = createImage(tracker, "type9e.gif");
        type9w = createImage(tracker, "type9w.gif");
        try { tracker.waitForAll(); }
        catch(InterruptedException e) {}
    }

    private Image createImage(MediaTracker tracker, String fileName) {
        Image image = Toolkit.getDefaultToolkit().createImage(fileName);
        tracker.addImage(image, 0);
        return image;
    }

    private boolean isOpen(int roomNumber) {
        int wall = data[WALL_OFFSET + roomNumber];
        int type = wall >> 4;
        return (type == 8 || type == 9 || type == 10);
    }

    public void paintComponent(Graphics g) {
        g.setFont(TINY_FONT);
        g.setColor(Color.white);
        g.fillRect(0, 0, pixelWidth, pixelHeight);
        g.setColor(new Color(216, 216, 216));  // light gray
        for (int i = 0; i <= pixelWidth; i += ROOM_WIDTH) {
            g.drawLine(i, 0, i, pixelHeight);
        }
        for (int j = 0; j <= pixelHeight; j += ROOM_HEIGHT) {
            g.drawLine(0, j, pixelWidth, j);
        }

        int y0 = 1;
        int y1 = 10;
        int y2 = ROOM_HEIGHT / 2 - 5;
        int y3 = ROOM_HEIGHT / 2 + 5;
        int y4 = ROOM_HEIGHT - 10;
        int y5 = ROOM_HEIGHT - 1;

        int x0 = 1;
        int x2 = ROOM_WIDTH / 2 - 5;
        int x1 = x2 - (y2 - y1);
        int x3 = ROOM_WIDTH / 2 + 5;
        int x4 = x3 + (y4 - y3);;
        int x5 = ROOM_WIDTH - 1;

        for (int y = 0; y < MAZE_HEIGHT; y++) {
            for (int x = 0; x < MAZE_WIDTH; x++) {
                int roomNumber = y * MAZE_WIDTH + x;
                int left = ROOM_WIDTH * x;
                int top = ROOM_HEIGHT * (MAZE_HEIGHT - y - 1);

                // highlight the "special" rooms:
                if (roomNumber == 29 || roomNumber == 104 || roomNumber == 226 || roomNumber == 151 || roomNumber == 262 ||
                    roomNumber == 548 || roomNumber == 383 || roomNumber == 558 || roomNumber == 936) {
                    g.setColor(Color.yellow);
                    g.fillRect(left + 1, top + 1, ROOM_WIDTH - 1, ROOM_HEIGHT - 1);
                }

                // Draw room walls:
                int wall = data[WALL_OFFSET + roomNumber];
                boolean north = (wall & 0x08) == 0x08;
                boolean south = (wall & 0x04) == 0x04;
                boolean east  = (wall & 0x02) == 0x02;
                boolean west  = (wall & 0x01) == 0x01;
                int type = wall >> 4;

                // In addition to the hard coded exits, these room types may randomly have additional exits:
                boolean randomExits = (type == 3 || type == 4 || type == 6);
                g.setColor(randomExits ? Color.magenta : Color.black);

                int openRoom = 0;
                if (type == 8 || type == 9 || type == 10) {  // open rooms
                    openRoom = 1;
                    Image image = null;
                    if (type == 8) { image = type8; }
                    if (type == 9 || type == 10) { image = type9; }
                    int imageWidth = image.getWidth(this);
                    int imageHeight = image.getHeight(this);
                    g.drawImage(image, left + 1 + (ROOM_WIDTH - imageWidth) / 2, top + 1 + (ROOM_HEIGHT - imageHeight) / 2, this);
                    if (north) {
                        if (!isOpen(roomNumber + MAZE_WIDTH)) {  // if non-open room to north, draw partial wall:
                            g.drawLine(left + x0, top + y0, left + x2, top + y0);
                            g.drawLine(left + x3, top + y0, left + x5, top + y0);
                            g.drawImage(type9n, left + 1 + (ROOM_WIDTH - imageWidth) / 2, top + 1 + (ROOM_HEIGHT - imageHeight) / 2, this);
                        }
                    }
                    else {
                        g.drawLine(left + x0, top + y0, left + x5, top + y0);
                    }
                    if (south) {
                        if (!isOpen(roomNumber - MAZE_WIDTH)) {  // if non-open room to south, draw partial wall:
                            g.drawLine(left + x0, top + y5, left + x2, top + y5);
                            g.drawLine(left + x3, top + y5, left + x5, top + y5);
                            g.drawImage(type9s, left + 1 + (ROOM_WIDTH - imageWidth) / 2, top + 1 + (ROOM_HEIGHT - imageHeight) / 2, this);
                        }
                    }
                    else {
                        g.drawLine(left + x0, top + y5, left + x5, top + y5);
                    }
                    if (east) {
                        if (!isOpen(roomNumber + 1)) {  // if non-open room to east, draw partial wall:
                            g.drawLine(left + x5, top + y0, left + x5, top + y2);
                            g.drawLine(left + x5, top + y3, left + x5, top + y5);
                            g.drawImage(type9e, left + 1 + (ROOM_WIDTH - imageWidth) / 2, top + 1 + (ROOM_HEIGHT - imageHeight) / 2, this);
                        }
                    }
                    else {
                        g.drawLine(left + x5, top + y0, left + x5, top + y5);
                    }
                    if (west) {
                        if (!isOpen(roomNumber - 1)) {  // if non-open room to west, draw partial wall:
                            g.drawLine(left + x0, top + y0, left + x0, top + y2);
                            g.drawLine(left + x0, top + y3, left + x0, top + y5);
                            g.drawImage(type9w, left + 1 + (ROOM_WIDTH - imageWidth) / 2, top + 1 + (ROOM_HEIGHT - imageHeight) / 2, this);
                        }
                    }
                    else {
                        g.drawLine(left + x0, top + y0, left + x0, top + y5);
                    }
                }

                if (type == 3 || type == 4 || type == 11 || type == 13) {  // hallway type rooms
                    if (north || randomExits) {
                        g.drawLine(left + x2, top + y0, left + x2, top + y2);
                        g.drawLine(left + x3, top + y0, left + x3, top + y2);
                    }
                    if (!north) {
                        g.drawLine(left + x2, top + y2, left + x3, top + y2);
                    }
                    if (south || randomExits) {
                        g.drawLine(left + x2, top + y3, left + x2, top + y5);
                        g.drawLine(left + x3, top + y3, left + x3, top + y5);
                    }
                    if (!south) {
                        g.drawLine(left + x2, top + y3, left + x3, top + y3);
                    }
                    if (east || randomExits) {
                        g.drawLine(left + x3, top + y2, left + x5, top + y2);
                        g.drawLine(left + x3, top + y3, left + x5, top + y3);
                    }
                    if (!east) {
                        g.drawLine(left + x3, top + y2, left + x3, top + y3);
                    }
                    if (west || randomExits) {
                        g.drawLine(left + x0, top + y2, left + x2, top + y2);
                        g.drawLine(left + x0, top + y3, left + x2, top + y3);
                    }
                    if (!west) {
                        g.drawLine(left + x2, top + y2, left + x2, top + y3);
                    }
                    if (type == 4 || type == 13) {  // Room 134 has a couple extra decorative walls
                        g.drawLine(left + x2, top + y2, left + x2 + 3, top + y2 + 3);
                        g.drawLine(left + x2 + 3, top + y2 + 3, left + x2 + 3, top + y2 + 5);
                        g.drawLine(left + x3, top + y3, left + x3 - 3, top + y3 - 3);
                        g.drawLine(left + x3 - 3, top + y3 - 3, left + x3 - 3, top + y3 - 5);
                    }
                }

                if (type == 4 || type == 12) {  // square type rooms
                    if (north || randomExits) {
                        g.drawLine(left + x1, top + y1, left + x2, top + y1);
                        g.drawLine(left + x2, top + y1, left + x2, top + y0);
                        g.drawLine(left + x3, top + y0, left + x3, top + y1);
                        g.drawLine(left + x3, top + y1, left + x4, top + y1);
                    }
                    if (!north) {
                        g.drawLine(left + x1, top + y1, left + x4, top + y1);
                    }
                    if (south || randomExits) {
                        g.drawLine(left + x1, top + y4, left + x2, top + y4);
                        g.drawLine(left + x2, top + y4, left + x2, top + y5);
                        g.drawLine(left + x3, top + y5, left + x3, top + y4);
                        g.drawLine(left + x3, top + y4, left + x4, top + y4);
                    }
                    if (!south) {
                        g.drawLine(left + x1, top + y4, left + x4, top + y4);
                    }
                    if (east || randomExits) {
                        g.drawLine(left + x4, top + y1, left + x4, top + y2);
                        g.drawLine(left + x4, top + y2, left + x5, top + y2);
                        g.drawLine(left + x5, top + y3, left + x4, top + y3);
                        g.drawLine(left + x4, top + y3, left + x4, top + y4);
                    }
                    if (!east) {
                        g.drawLine(left + x4, top + y1, left + x4, top + y4);
                    }
                    if (west || randomExits) {
                        g.drawLine(left + x1, top + y1, left + x1, top + y2);
                        g.drawLine(left + x1, top + y2, left + x0, top + y2);
                        g.drawLine(left + x0, top + y3, left + x1, top + y3);
                        g.drawLine(left + x1, top + y3, left + x1, top + y4);
                    }
                    if (!west) {
                        g.drawLine(left + x1, top + y1, left + x1, top + y4);
                    }
                }

                if (type == 3 || type == 4 || type == 6 || type == 14) {  // octagon type rooms
                    g.drawLine(left + x1, top + y2, left + x2, top + y1);
                    g.drawLine(left + x1, top + y3, left + x2, top + y4);
                    g.drawLine(left + x3, top + y1, left + x4, top + y2);
                    g.drawLine(left + x3, top + y4, left + x4, top + y3);
                    if (north || randomExits) {
                        g.drawLine(left + x2, top + y1, left + x2, top + y0);
                        g.drawLine(left + x3, top + y0, left + x3, top + y1);
                    }
                    if (!north) {
                        g.drawLine(left + x2, top + y1, left + x3, top + y1);
                    }
                    if (south || randomExits) {
                        g.drawLine(left + x2, top + y4, left + x2, top + y5);
                        g.drawLine(left + x3, top + y5, left + x3, top + y4);
                    }
                    if (!south) {
                        g.drawLine(left + x2, top + y4, left + x3, top + y4);
                    }
                    if (east || randomExits) {
                        g.drawLine(left + x4, top + y2, left + x5, top + y2);
                        g.drawLine(left + x5, top + y3, left + x4, top + y3);
                    }
                    if (!east) {
                        g.drawLine(left + x4, top + y2, left + x4, top + y3);
                    }
                    if (west || randomExits) {
                        g.drawLine(left + x1, top + y2, left + x0, top + y2);
                        g.drawLine(left + x0, top + y3, left + x1, top + y3);
                    }
                    if (!west) {
                        g.drawLine(left + x1, top + y2, left + x1, top + y3);
                    }
                }

                // Draw room numbers:
                String roomNumberString = "" + roomNumber;
                g.setColor(Color.blue);
                g.drawString(roomNumberString, left + 2 + openRoom, top + 9 + openRoom);

                // Draw monsters:
                int monster = data[MONSTER_OFFSET + roomNumber];
                String monsterString = "";
                int monsterNum = monster / 16;
                int monsterType = monster % 16;
                if (monsterNum > 0) {
                    monsterString = monsterNum + " " + MONSTERS[monsterType];
                }
                int width = fontMetrics.stringWidth(monsterString) + 1;
                g.setColor(Color.red);
                g.drawString(monsterString, left + ROOM_WIDTH - width - openRoom, top + 9 + openRoom);

                // Draw treasures:
                int treasure = data[TREASURE_OFFSET + roomNumber];
                String treasureString = TREASURES[treasure];
                g.setColor(Color.green.darker());
                g.drawString(treasureString, left + 2 + openRoom, top + ROOM_HEIGHT - 1 - openRoom);

                // Draw special room notes:
                if (roomNumber == 29 || roomNumber == 104 || roomNumber == 226) { drawMessage(g, left, top, "Puzzle Room"); }
                if (roomNumber == 151) { drawMessage(g, left, top, "Bazaar"); }
                if (roomNumber == 262) { drawMessage(g, left, top, "Trap"); }
                if (roomNumber == 548) { drawMessage(g, left, top, "All Treasures"); }
                if (roomNumber == 383 || roomNumber == 558) { drawMessage(g, left, top, "Hospice"); }
                if (roomNumber == 936) { drawMessage(g, left, top, "Crown"); }
            }
        }
    }

    private void drawMessage(Graphics g, int left, int top, String message) {
        g.setColor(Color.blue);
        g.setFont(TINY_FONT);
        int length = fontMetrics.stringWidth(message);
        g.drawString(message, left + ((ROOM_WIDTH - length) / 2) + 1, top + 4 + (ROOM_HEIGHT / 2));
    }
}
