/***********************************************************************

GECCO, a platform for command and control gaming

Copyright (C) 2001 Henrik Bäärnhielm, Andreas Enblom, Niklas
Hallenfur, Karl Hasselström, Henrik Hägerström, Oskar Linde, Jing Fu
Zi and Jon Åslund

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

***********************************************************************/

package gecco.client;
import java.awt.*;
import java.awt.image.*;

import java.util.*;

/**
 *  This class represents the map of a game. The map should be given an Image
 *  and a map size. The map has a coordinate system of its own, where the upper
 *  left corner is (0,0), and every pixel is an automaton. The coordinates are
 *  then given as integer numbers. <p>
 *
 *  Every point of the map can be set as either visible or invisible. When a
 *  point is invisible it is painted darker. To make a point invisible, call
 *  invisiblizePoint(). To make a point visible, or to change the state of som
 *  point (automaton), call method updatePoint. <p>
 *
 *  When the map is painted, a certain visible area of the map is painted. The
 *  currently visible area is specified with a left upper point (in the map's
 *  coordinates), and the size (in pixels) of the visible area. The resoultion
 *  is also affectable, by setting the size of the pixels (i.e. a number 1, 2, 3
 *  indicating how many screen pixels the side of every map pixel should
 *  occupy).
 *
 * @author     Andreas Enblom
 * @created    June 27, 2001
 */
public class Map {
    /**
     *  Description of the Field
     */
    Dimension size;
    /**
     *  Description of the Field
     */
    Point visibleAreaUpperLeft;
    /**
     *  Description of the Field
     */
    int squareSize;
    /**
     *  Description of the Field
     */
    Dimension visibleAreaSize;
    /**
     *  Description of the Field
     */
    BufferedImage map;
    /**
     *  Description of the Field
     */
    boolean[][] visibilities;


    /**
     *  Initilizes a new map.
     *
     * @param  size  The size of the map.
     */
    public Map(Dimension size) {
        this.size = new Dimension(size);
        visibilities = new boolean[size.width][size.height];
        for (int i = 0; i < size.width; ++i) {
            for (int j = 0; j < size.height; ++j) {
                visibilities[i][j] = true;
            }
        }

        map = new BufferedImage(size.width, size.height,
                BufferedImage.TYPE_3BYTE_BGR);
        Graphics g = map.getGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, size.width, size.height);
        g.setColor(Color.black);
    }


    /**
     *  Set the currently visible area of the map. Remember that the map has
     *  (0,0) as its upper left corner.
     *
     * @param  upperLeft   The upper left corner of the currently visible area.
     *      Relative to the map's coordinate system, with (0,0) in the upper
     *      left corner.
     * @param  squareSize  The size of every square (automaton) in pixels on the
     *      screen when the map is painted.
     * @param  size        The size (in squares, not actual screen size) of the
     *      currently visible area of the map.
     */
    public void setVisibleArea(Point upperLeft, int squareSize,
            Dimension size) {
        visibleAreaUpperLeft = new Point(upperLeft);
        visibleAreaSize = new Dimension(size);
        this.squareSize = squareSize;
    }


    /**
     *  Gets the Size attribute of the Map object
     *
     * @return    The Size value
     */
    public Dimension getSize() {
        return new Dimension(size);
    }


    /**
     *  Clears the map.
     */
    public void clear() {
        loadMap(null);
    }


    /**
     *  Loads the given image as map. The map is set as invisible.
     *
     * @param  map  Description of Parameter
     */
    public void loadMap(Image map) {
        if (this.map == null) {
            return;
        }

        // the map is initially invisible
        for (int i = 0; i < size.width; ++i) {
            for (int j = 0; j < size.height; ++j) {
                visibilities[i][j] = false;
            }
        }
        Graphics g = this.map.getGraphics();

        // if there is already a map, clear it
        if (map == null) {
            g.setColor(Color.white);
            g.fillRect(0, 0, size.width, size.height);
            g.setColor(Color.black);
            return;
        }

        // darken the map; it is initially invisible
        g.drawImage(map, 0, 0, size.width, size.height, null);
        g.setColor(new Color(0, 0, 0, 64));
        g.fillRect(0, 0, size.width, size.height);
        g.setColor(Color.black);
    }


    /**
     *  Makes the given point invisibile by making it darker. If the point is
     *  already set as invisible, nothing is done.
     *
     * @param  p  Description of Parameter
     */
    public void invisibilizePoint(Point p) {
        if ((p.x >= size.width) || (p.y >= size.height) ||
                (p.x < 0) || (p.y < 0)) {
            return;
        }
        if (!visibilities[p.x][p.y]) {
            return;
        }
        visibilities[p.x][p.y] = false;
        // make the color of the point 1/4 times darker (i.e.
        // new brightness = 3/4 * old brighness)
        int color = map.getRGB(p.x, p.y);
        map.setRGB(p.x, p.y, ((color >> 1) & 0x7F7F7F) +
                ((color >> 2) & 0x3F3F3F));
    }


    /**
     *  Updates the given point of the map to the new color. This marks the
     *  point as visible.
     *
     * @param  p      The point of the map to update, remember that the map has
     *      (0,0) as its upper left corner.
     * @param  color  The color of the point, given as an int as described in
     *      java.awt.Color.
     */
    public void updatePoint(Point p, int color) {
        if ((p.x >= size.width) || (p.y >= size.height) ||
                (p.x < 0) || (p.y < 0)) {
            return;
        }
        visibilities[p.x][p.y] = true;
        map.setRGB(p.x, p.y, color);
    }


    /**
     *  Paints the map on the givewn graphics, with the currently set visible
     *  area and resolution.
     *
     * @param  g  Description of Parameter
     */
    public void paint(Graphics g) {
        g.drawImage(map, 0, 0, visibleAreaSize.width * squareSize,
                visibleAreaSize.height * squareSize,
                visibleAreaUpperLeft.x, visibleAreaUpperLeft.y,
                visibleAreaUpperLeft.x + visibleAreaSize.width,
                visibleAreaUpperLeft.y + visibleAreaSize.height,
                null);
    }
}
