package ch.epfl.isochrone.tiledmap;

import static ch.epfl.isochrone.math.Math.divF;
import static ch.epfl.isochrone.math.Math.modF;

import java.awt.image.BufferedImage;

/**
 * Interface représentant un modicateur de fournisseur de tuile pour les rendre
 * transparentes
 * 
 * @author Charles Thiebaut (238935)
 * @author Arnaud Delamare (234841)
 * 
 */
public class TransparentTileProvider extends FilteringTileProvider {

    private final double opacity;

    /**
     * 
     * @param opacity
     *            L'opacité future des tuiles
     * @param provider
     *            Le fournisseur à transformer
     */
    public TransparentTileProvider(double opacity, TileProvider provider) {
        this.opacity = opacity;
        this.provider = provider;
    }

    /**
     * 
     * Retourne la tile du fournisseur sous-jacent avec la bonne opacité
     * 
     * @param zoom
     *            Le zoom de la Tile
     * 
     * @param x
     *            La position x de la Tile
     * 
     * @param y
     *            La position y de la Tile
     */
    @Override
    public Tile tileAt(int zoom, int x, int y) {

        BufferedImage imageBase = provider.tileAt(zoom, x, y).image();

        BufferedImage image = new BufferedImage(imageBase.getWidth(),
                imageBase.getHeight(), BufferedImage.TYPE_INT_ARGB);

        for (int i = 0; i < imageBase.getWidth(); i++) {
            for (int j = 0; j < imageBase.getWidth(); j++) {
                image.setRGB(i, j, transformARGB(imageBase.getRGB(i, j)));

                // on rend l'image partiellement transparente en la parcourant
                // pixel par pixel
            }
        }

        return new Tile(x, y, zoom, image);
    }

    /**
     * Modifie un int de type ARGB en changeant son opacité en celle de
     * l'instance
     */
    @Override
    public int transformARGB(int argb) {
        return argb(opacity, r(argb), g(argb), b(argb));
    }

    /**
     * Extrait la valeur de a d'un int de type ARBG
     */
    public double a(int argb) {
        return modF(divF(argb, (int) Math.pow(2, 24)), (int) Math.pow(2, 8)) / 255.0;
    }

    /**
     * Extrait la valeur de r d'un int de type ARBG
     */
    public double r(int argb) {
        return modF(divF(argb, (int) Math.pow(2, 16)), (int) Math.pow(2, 8)) / 255.0;
    }

    /**
     * Extrait la valeur de g d'un int de type ARBG
     */
    public double g(int argb) {
        return modF(divF(argb, (int) Math.pow(2, 8)), (int) Math.pow(2, 8)) / 255.0;
    }

    /**
     * Extrait la valeur de b d'un int de type ARBG
     */
    public double b(int argb) {
        return modF(divF(argb, (int) Math.pow(2, 0)), (int) Math.pow(2, 8)) / 255.0;
    }

    /**
     * Transforme 4 valeurs entre 0 et 1 en un int de type ARGB
     */
    public int argb(double a, double r, double g, double b) {
        return ((int) Math.pow(2, 24)) * ((int) (255 * a))
                + ((int) Math.pow(2, 16)) * ((int) (255 * r))
                + +((int) Math.pow(2, 8)) * ((int) (255 * g))
                + +((int) (255 * b));
    }

}
