package dr.geo;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.util.ArrayList;

/**
 * @author Alexei Drummond
 */
public class KMLRenderer implements Lattice {

    BufferedImage image;
    int[][] lattice;
    Rectangle2D bounds;

    java.util.List<Polygon2D> polygons;
    java.util.List<Shape> shapes;

    ViewTransform viewTransform;

    Color shapeColor;
    Color background;

    public KMLRenderer(String kmlFileName, Color shapeColor, Color background) {

        polygons = Polygon2D.readKMLFile(kmlFileName);

        this.shapeColor = shapeColor;
        this.background = background;

        System.out.println("Read " + polygons.size() + " polygons");

        System.out.println("Converting polygons to shapes");

        double minX = Double.MAX_VALUE, maxX = -Double.MAX_VALUE, minY = Double.MAX_VALUE, maxY = -Double.MAX_VALUE;

        shapes = new ArrayList<Shape>();

        for (Polygon2D p : polygons) {

            Shape shape = p.getShape();

            bounds = shape.getBounds();

            if (bounds.getMinX() < minX) minX = bounds.getMinX();
            if (bounds.getMaxX() > maxX) maxX = bounds.getMaxX();
            if (bounds.getMinY() < minY) minY = bounds.getMinY();
            if (bounds.getMaxY() > maxY) maxY = bounds.getMaxY();

            shapes.add(shape);
            System.out.print(".");
            System.out.flush();
        }

        bounds = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);

        System.out.println();

    }

    public Rectangle2D getBounds() {
        return bounds;
    }

    public BufferedImage render(int size) {
        int width;
        int height;
        if (bounds.getHeight() > bounds.getWidth()) {
            height = size;
            width = (int) (height * bounds.getWidth() / bounds.getHeight());
        } else {
            width = size;
            height = (int) (width * bounds.getHeight() / bounds.getWidth());
        }
        return render(width, height);
    }

    public BufferedImage render(int width, int height) {
        image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        render(image);

        Raster raster = image.getData();

        lattice = new int[width][height];
        int[] pixel = new int[4];
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                raster.getPixel(i, j, pixel);
                if (colorDistanceSquared(pixel, shapeColor) < colorDistanceSquared(pixel, background)) {
                    lattice[i][j] = 1;
                } else {
                    lattice[i][j] = 0;
                }
            }
        }

        return image;
    }

    private double colorDistanceSquared(int[] pixel, Color color) {

        double[] argb = new double[4];

        argb[0] = Math.abs(pixel[0] - color.getAlpha());
        argb[1] = Math.abs(pixel[1] - color.getRed());
        argb[2] = Math.abs(pixel[2] - color.getGreen());
        argb[3] = Math.abs(pixel[3] - color.getBlue());

        double dist = 0;
        for (double a : argb) {
            dist += a * a;
        }
        return dist;
    }

    public void render(BufferedImage image) {
        Graphics2D g2d = image.createGraphics();

        g2d.setColor(background);
        g2d.fillRect(0, 0, image.getWidth(), image.getHeight());

        viewTransform = new ViewTransform(bounds, image.getWidth(), image.getHeight());

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2d.setColor(shapeColor);
        AffineTransform transform = viewTransform.getTransform();
        for (Shape s : shapes) {
            GeneralPath path = new GeneralPath(s);
            path.transform(transform);
            g2d.fill(path);
        }
    }

    public int latticeWidth() {
        return lattice.length;
    }

    public int latticeHeight() {
        return lattice[0].length;
    }

    public void setState(int i, int j, int state) {
        lattice[i][j] = state;
    }

    public int getState(int i, int j) {
        return lattice[i][j];
    }

    public void paintLattice(Graphics g) {
        g.drawImage(image, 0, 0, null);
    }

    public void setBounds(Rectangle2D bounds) {
        this.bounds = bounds;
    }
}
