package visualiser.layout;

import javafx.scene.image.Image;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.MeshView;
import visualiser.utils.PerlinNoiseGenerator;

/**
 * Creates a SeaSurface
 */
public class SeaSurface {
    private float[][] noiseArray;
    private Subject3D surface;

    /**
     * Sea Surface Constructor
     * @param size size of the sea surface (has to be square for simplicity's sake)
     * @param freq frequency the perlin noise is to be generated at
     * @param x offset that the sea should be set at position-wise
     * @param y offset that the sea should be set at position-wise
     * @param z offset that the sea should be set at position-wise
     */
    public SeaSurface(int size, double freq, double x, double y, double z){
        noiseArray = PerlinNoiseGenerator.createNoise(size, freq);
        createSurface();
        surface.setZ(z);
        surface.setY(y);
        surface.setX(x);
    }

    /**
     * Creates the sea surface
     */
    private void createSurface(){
        Image diffuseMap = createImage(noiseArray.length, noiseArray);

        PhongMaterial material = new PhongMaterial();
        material.setDiffuseMap(diffuseMap);
        //material.setSpecularColor(Color.WHITE);

        Plane3D seaPlane = new Plane3D(noiseArray.length, noiseArray.length, 10, 10);
        MeshView seaSurface = new MeshView(seaPlane);
//        Box seaSurface = new Box(noiseArray.length, 0.1, noiseArray.length);
        seaSurface.setMaterial(material);
        seaSurface.setMouseTransparent(true);
        seaSurface.toFront();
        //seaSurface.setRotationAxis(new Point3D(1, 0, 0));
        //seaSurface.setRotate(90);

        surface = new Subject3D(seaSurface);
    }

    /**
     * Create texture for uv mapping
     * @param size size of the image to make
     * @param noise array of noise
     * @return image that is created
     */
    private Image createImage(double size, float[][] noise) {

        int width = (int) size;
        int height = (int) size;

        WritableImage wr = new WritableImage(width, height);
        PixelWriter pw = wr.getPixelWriter();
        //interpolate colours based on noise
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {

                float value = noise[x][y];

                double gray = normalizeValue(value, -.5, .5, 0., 1.);

                gray = clamp(gray, 0, 1);

                //values to interpolate on
                Color brightBlue = new Color(0.06, 0.5, .78, 1);
                Color lightBlue = new Color(0.15, 0.68, .88, 1);
                Color lighterBlue = new Color(0.28, 0.73, .91, 1);

                Color colour = Color.WHITE.interpolate(brightBlue, gray).interpolate(lighterBlue, gray).interpolate(lightBlue, gray);

                pw.setColor(x, y, colour);

            }
        }

        return wr;

    }

    /**
     * Nomalises the values so that the colours are correct
     * @param value value to normalise
     * @param min current min
     * @param max current max
     * @param newMin new min
     * @param newMax new max
     * @return returns normalised value
     */
    private static double normalizeValue(double value, double min, double max, double newMin, double newMax) {

        return (value - min) * (newMax - newMin) / (max - min) + newMin;

    }

    /**
     * clamps a value between a min and max
     * @param value value to clamp
     * @param min minimum value it can be
     * @param max maximum value it can be
     * @return result after clamp
     */
    private static double clamp(double value, double min, double max) {

        if (Double.compare(value, min) < 0)
            return min;

        if (Double.compare(value, max) > 0)
            return max;

        return value;
    }

    /**
     * Get surface
     * @return the surface so it can be drawn
     */
    public Subject3D getSurface(){
        return surface;
    }

}