// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.elevation.grid;

import static org.openstreetmap.josm.tools.I18n.tr;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

import org.openstreetmap.gui.jmapviewer.Tile;
import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.imagery.CoordinateConversion;
import org.openstreetmap.josm.gui.MapView;
import org.openstreetmap.josm.plugins.elevation.ElevationHelper;
import org.openstreetmap.josm.plugins.elevation.IVertexRenderer;
import org.openstreetmap.josm.plugins.elevation.gui.Triangle;

public class ElevationGridTile extends Tile {
    private final BlockingDeque<EleVertex> toDo = new LinkedBlockingDeque<>();
    private final BlockingDeque<EleVertex> vertices = new LinkedBlockingDeque<>();

    private Bounds box;

    public ElevationGridTile(TileSource source, int xtile, int ytile, int zoom) {
        super(source, xtile, ytile, zoom);

        box = tile2Bounds(xtile, ytile, zoom);
        initQueue();
    }

    public ElevationGridTile(TileSource source, int xtile, int ytile, int zoom,
            BufferedImage image) {
        super(source, xtile, ytile, zoom, image);
    }

    /**
     * Use {@link ElevationGridTile#paintTile(Graphics2D, MapView, IVertexRenderer)} to render the tile as grid.
     * This method just issues a debug text.
     */
    @Override
    public void paint(Graphics g, int x, int y) {
        super.paint(g, x, y);

        g.drawString(getStatus(), x, y);
    }

    /**
     * Paints the vertices of this tile.
     * @param g the graphics context
     * @param mv the map view
     * @param vertexRenderer the vertex renderer
     */
    public void paintTile(Graphics2D g, MapView mv, IVertexRenderer vertexRenderer) {
        BlockingDeque<EleVertex> list = getVertices();
        for (EleVertex eleVertex : list) {
            Point p0 = mv.getPoint(eleVertex.get(0));
            Point p1 = mv.getPoint(eleVertex.get(1));
            Point p2 = mv.getPoint(eleVertex.get(2));
            Triangle shape = new Triangle(p0, p1, p2);
            // obtain vertex color
            g.setColor(vertexRenderer.getElevationColor(eleVertex));
            // TODO: Move to renderer
            g.fill(shape);
        }
    }

    @Override
    public void loadImage(InputStream input) throws IOException {
        if (isLoaded()) return;

        // TODO: Save

        // We abuse the loadImage method to render the vertices...
        //
        while (!toDo.isEmpty()) {
            EleVertex vertex = toDo.poll();

            if (vertex.isFinished()) {
                vertices.add(vertex);
            } else {
                toDo.addAll(vertex.divide());
            }
        }
        setLoaded(true);
    }

    public BlockingDeque<EleVertex> getVertices() {
        return vertices;
    }

    /**
     * See also <a href="https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_bounding_box">OSM Wiki</a>
     * @param x the x
     * @param y the y
     * @param zoom the zoom
     * @return the bounds
     */
    private Bounds tile2Bounds(final int x, final int y, final int zoom) {
        return new Bounds(
                CoordinateConversion.coorToLL(source.tileXYToLatLon(x, y, zoom)),
                CoordinateConversion.coorToLL(source.tileXYToLatLon(x + 1, y + 1, zoom)));
    }

    /**
     * Inits the 'todo' queue with the initial vertices.
     */
    private void initQueue() {
        LatLon min = box.getMin();
        LatLon max = box.getMax();

        // compute missing coordinates
        LatLon h1 = new LatLon(min.lat(), max.lon());
        LatLon h2 = new LatLon(max.lat(), min.lon());

        double eleMin = ElevationHelper.getSrtmElevation(min);
        double eleMax = ElevationHelper.getSrtmElevation(max);

        // SRTM files present?
        if (!ElevationHelper.isValidElevation(eleMax) || !ElevationHelper.isValidElevation(eleMin)) {
            setError(tr("No SRTM data"));
            return;
        }

        // compute elevation coords
        EleCoordinate p0 = new EleCoordinate(min, eleMin);
        EleCoordinate p1 = new EleCoordinate(h1, ElevationHelper.getSrtmElevation(h1));
        EleCoordinate p2 = new EleCoordinate(max, eleMax);
        EleCoordinate p3 = new EleCoordinate(h2, ElevationHelper.getSrtmElevation(h2));

        // compute initial vertices
        EleVertex v1 = new EleVertex(p0, p1, p2);
        EleVertex v2 = new EleVertex(p2, p3, p0);
        // enqueue vertices
        toDo.add(v1);
        toDo.add(v2);
    }

    @Override
    public String toString() {
        return "ElevationGridTile [box=" + box + ", xtile=" + xtile
                + ", ytile=" + ytile + "]";
    }
}
