import imp.*;
import java.awt.Color;
import java.util.Random;
/**
 * Algorithmen, die auf der Veränderung der Farbinformationen eines Punktes und seines Umfeldes
 * beruhen. Diese Operationen werden als Faltung bezeichnet.
 *
 * @author Thomas Schaller  
 * @version 1.0 (7.11.2019)
 */
public class Mehrpixeloperationen
{

    Picture faltung(Picture originalbild, double[][] filter) {
        // Neue Pixel zunächst wie alte Pixel, damit der Rand kopiert wird.
        Color[][] pixel = originalbild.getPixelArray();
        Color[][] pixelNeu = originalbild.getPixelArray();

        // Größe der Filtermatrix bestimmen
        int laenge = filter.length; 
        int halb   = laenge / 2;    

        // Faltung berechnen
        // Schleife über alle Spalten
        for (int x = filter.length/2; x < originalbild.getWidth() - filter.length/2; x++){ 
            // Schleife über alle Zeilen
            for (int y = filter.length/2; y < originalbild.getHeight() - filter.length/2; y++) 
            { 
                // Deklaration der neuen Farbkomponenten
                double rot =0;
                double gruen = 0;
                double blau = 0;

                // Koordinaten des Pixels links oben in der Ecke der Filtermatrix
                int xx = x - filter.length/2;
                int yy = y - filter.length/2;

                // Schleifen über jeden Punkt der Filtermatrix
                for (int i = 0; i < filter.length; i++) {
                    for (int j = 0; j < filter.length; j++){  
                        // Summiere die gewichteten Farbkomponenten der Originalpixel 
                        rot   += filter[i][j] * pixel[xx+i][yy+j].getRed(); 
                        gruen += filter[i][j] * pixel[xx+i][yy+j].getGreen(); 
                        blau  += filter[i][j] * pixel[xx+i][yy+j].getBlue(); 
                    }
                }

                //Begrenzung auf zulässigen Bereich
                if(rot < 0.0)     rot   = 0.0;   
                if(rot > 255.0)   rot   = 255.0;
                if(gruen < 0.0)   gruen = 0.0; 
                if(gruen > 255.0) gruen = 255.0;
                if(blau < 0.0)    blau  = 0.0;  
                if(blau > 255.0)  blau  = 255.0;

                // Definiere Farbe des neuen Pixels
                pixelNeu[x][y] = new Color((int) rot, (int) gruen, (int) blau);
            } 
        }
        // Erzeuge neues Bild
        Picture neuesBild = new Picture();
        neuesBild.setPixelArray(pixelNeu);
        return neuesBild;
    } 

    /* Faltung: Pixel außerhalb werden schwarz gesetzt
     * 
     */
    Picture faltung_randbehandlung1(Picture originalbild, double[][] filter) {
        // Neue Pixel zunächst wie alte Pixel, damit der Rand kopiert wird.
        Color[][] pixel = originalbild.getPixelArray();
        Color[][] pixelNeu = originalbild.getPixelArray();

        // Größe der Filtermatrix bestimmen
        int laenge = filter.length; 
        int halb   = laenge / 2;    

        // Faltung berechnen
        // Schleife über alle Spalten
        for (int x = 0; x < originalbild.getWidth(); x++){ 
            // Schleife über alle Zeilen
            for (int y = 0; y < originalbild.getHeight(); y++) 
            { 
                // Deklaration der neuen Farbkomponenten
                double rot =0;
                double gruen = 0;
                double blau = 0;

                // Koordinaten des Pixels links oben in der Ecke der Filtermatrix
                int xx = x - filter.length/2;
                int yy = y - filter.length/2;

                // Schleifen über jeden Punkt der Filtermatrix
                for (int i = 0; i < filter.length; i++) {
                    for (int j = 0; j < filter.length; j++){ 
                        // erst mal auf schwarz setzen
                        Color c = Color.BLACK;
                        // wenn innerhalb des Bildes, dann Pixelfarbe nutzen
                        if(xx+i >= 0 && xx+i <originalbild.getWidth() && yy+j >=0 && yy+j < originalbild.getHeight()) {
                            c = pixel[xx+i][yy+j];
                        }
                        // Summiere die gewichteten Farbkomponenten der Originalpixel 
                        rot   += filter[i][j] * c.getRed(); 
                        gruen += filter[i][j] * c.getGreen(); 
                        blau  += filter[i][j] * c.getBlue(); 
                    }
                }

                //Begrenzung auf zulässigen Bereich
                if(rot < 0.0)     rot   = 0.0;   
                if(rot > 255.0)   rot   = 255.0;
                if(gruen < 0.0)   gruen = 0.0; 
                if(gruen > 255.0) gruen = 255.0;
                if(blau < 0.0)    blau  = 0.0;  
                if(blau > 255.0)  blau  = 255.0;

                // Definiere Farbe des neuen Pixels
                pixelNeu[x][y] = new Color((int) rot, (int) gruen, (int) blau);
            } 
        }
        // Erzeuge neues Bild
        Picture neuesBild = new Picture();
        neuesBild.setPixelArray(pixelNeu);
        return neuesBild;
    } 

    /* Faltung: Pixel außerhalb werden auf Randfarbe gesetzt
     * 
     */
    Picture faltung_randbehandlung2(Picture originalbild, double[][] filter) {
        // Neue Pixel zunächst wie alte Pixel, damit der Rand kopiert wird.
        Color[][] pixel = originalbild.getPixelArray();
        Color[][] pixelNeu = originalbild.getPixelArray();

        // Größe der Filtermatrix bestimmen
        int laenge = filter.length; 
        int halb   = laenge / 2;    

        // Faltung berechnen
        // Schleife über alle Spalten
        for (int x = 0; x < originalbild.getWidth(); x++){ 
            // Schleife über alle Zeilen
            for (int y = 0; y < originalbild.getHeight(); y++) 
            { 
                // Deklaration der neuen Farbkomponenten
                double rot =0;
                double gruen = 0;
                double blau = 0;

                // Koordinaten des Pixels links oben in der Ecke der Filtermatrix
                int xx = x - filter.length/2;
                int yy = y - filter.length/2;

                // Schleifen über jeden Punkt der Filtermatrix
                for (int i = 0; i < filter.length; i++) {
                    for (int j = 0; j < filter.length; j++){ 
                        // Bestimme Pixelkoordinaten
                        int xxx = xx+i;
                        int yyy = yy+j;
                        
                        // Korrigiere Pixelkoordinaten, falls außerhalb
                        if(xxx<0) xxx=0;
                        if(xxx>= originalbild.getWidth()) xxx = originalbild.getWidth()-1;
                        if(yyy<0) yyy=0;
                        if(yyy>= originalbild.getHeight()) yyy = originalbild.getHeight()-1;
                        
                        // Summiere die gewichteten Farbkomponenten der Originalpixel 
                        rot   += filter[i][j] * pixel[xxx][yyy].getRed(); 
                        gruen += filter[i][j] * pixel[xxx][yyy].getGreen(); 
                        blau  += filter[i][j] * pixel[xxx][yyy].getBlue(); 
                    }
                }

                //Begrenzung auf zulässigen Bereich
                if(rot < 0.0)     rot   = 0.0;   
                if(rot > 255.0)   rot   = 255.0;
                if(gruen < 0.0)   gruen = 0.0; 
                if(gruen > 255.0) gruen = 255.0;
                if(blau < 0.0)    blau  = 0.0;  
                if(blau > 255.0)  blau  = 255.0;

                // Definiere Farbe des neuen Pixels
                pixelNeu[x][y] = new Color((int) rot, (int) gruen, (int) blau);
            } 
        }
        // Erzeuge neues Bild
        Picture neuesBild = new Picture();
        neuesBild.setPixelArray(pixelNeu);
        return neuesBild;
    } 

    /* Faltung: Bild wird am Rand nach außen gespiegelt
     * 
     */
    Picture faltung_randbehandlung3(Picture originalbild, double[][] filter) {
        // Neue Pixel zunächst wie alte Pixel, damit der Rand kopiert wird.
        Color[][] pixel = originalbild.getPixelArray();
        Color[][] pixelNeu = originalbild.getPixelArray();

        // Größe der Filtermatrix bestimmen
        int laenge = filter.length; 
        int halb   = laenge / 2;    

        // Faltung berechnen
        // Schleife über alle Spalten
        for (int x = 0; x < originalbild.getWidth() ; x++){ 
            // Schleife über alle Zeilen
            for (int y = 0; y < originalbild.getHeight() ; y++) 
            { 
                // Deklaration der neuen Farbkomponenten
                double rot =0;
                double gruen = 0;
                double blau = 0;

                // Koordinaten des Pixels links oben in der Ecke der Filtermatrix
                int xx = x - filter.length/2;
                int yy = y - filter.length/2;

                // Schleifen über jeden Punkt der Filtermatrix
                for (int i = 0; i < filter.length; i++) {
                    for (int j = 0; j < filter.length; j++){ 
                        // Bestimme Pixelkoordinaten
                        int xxx = xx+i;
                        int yyy = yy+j;
                        
                        // Korrigiere Pixelkoordinaten, falls außerhalb
                        if(xxx<0) xxx=-xxx;
                        if(xxx>= originalbild.getWidth()) xxx = 2*originalbild.getWidth()-xxx-1;
                        if(yyy<0) yyy=-yyy;
                        if(yyy>= originalbild.getHeight()) yyy = 2*originalbild.getHeight()-yyy-1;
                        
                        // Summiere die gewichteten Farbkomponenten der Originalpixel 
                        rot   += filter[i][j] * pixel[xxx][yyy].getRed(); 
                        gruen += filter[i][j] * pixel[xxx][yyy].getGreen(); 
                        blau  += filter[i][j] * pixel[xxx][yyy].getBlue(); 
                    }
                }

                //Begrenzung auf zulässigen Bereich
                if(rot < 0.0)     rot   = 0.0;   
                if(rot > 255.0)   rot   = 255.0;
                if(gruen < 0.0)   gruen = 0.0; 
                if(gruen > 255.0) gruen = 255.0;
                if(blau < 0.0)    blau  = 0.0;  
                if(blau > 255.0)  blau  = 255.0;

                // Definiere Farbe des neuen Pixels
                pixelNeu[x][y] = new Color((int) rot, (int) gruen, (int) blau);
            } 
        }
        // Erzeuge neues Bild
        Picture neuesBild = new Picture();
        neuesBild.setPixelArray(pixelNeu);
        return neuesBild;
    } 

    
    public  Picture weichzeichnen(Picture originalbild) {
        double[][] mittelwertfilter ={{1.0/9,1.0/9,1.0/9},
                {1.0/9,1.0/9,1.0/9},
                {1.0/9,1.0/9,1.0/9}};
        return faltung(originalbild,mittelwertfilter);
    }

    public  Picture schaerfen(Picture originalbild) {
        double[][] schaerfen ={{0,-1,0},
                {-1,5,-1},
                {0,-1,0}};
        return faltung(originalbild,schaerfen);
    }

    public  Picture relief(Picture originalbild) {
        double[][] relieffilter ={{-2,-1,0},
                {-1,1,1},
                {0,1,2}};
        return faltung(originalbild,relieffilter);
    }

    public  Picture kantenfinden(Picture originalbild) {
        double[][] kantenfilter ={{0,1,0},
                {1,-4,1},
                {0,1,0}};
        return faltung(originalbild,kantenfilter);
    }

    public  Picture kantenfindenHorizontal(Picture originalbild) {
        double[][] sobelx ={{-1,0,1},
                {-2,0,2},
                {-1,0,1}};
        return faltung(originalbild,sobelx);
    }

    public  Picture kantenfindenVertikal(Picture originalbild) {
        double[][] sobely={{-1,-2,-1},
                {0,0,0},
                {1,2,1}};
        return faltung(originalbild,sobely);
    }

    // Ergänzungen: Größere Filter

    public  Picture weichzeichnen(Picture originalbild, int groesse) {
        // Deklariere die Matrix in der richtigen Größe.
        double[][] mittelwertfilter = new double[groesse][groesse];
        // Berechne Anzahl der Zellen.
        int anzahl = groesse * groesse;

        // Berechne für jede Zelle der Matrix ...
        for(int i=0; i< groesse; i++) {
            for(int j=0; j<groesse; j++) {
                //... den Wert 
                mittelwertfilter[i][j] = 1.0/anzahl; 
            }
        };

        return faltung_randbehandlung3(originalbild,mittelwertfilter);
    }

    public  Picture gaussfilter(Picture originalbild, int groesse) {
        // Deklariere die Matrix in der richtigen Größe.
        double[][] gaussfilter = new double[groesse][groesse];
        // Berechne Sigma.
        double sigma = (double) groesse / 6;
        // Berechne die Koordinaten der Mitte der Matrix.
        int mitteX = groesse/2;
        int mitteY = groesse/2;

        // Berechne für jede Zelle der Matrix ...
        for(int i=0; i< groesse; i++) {
            for(int j=0; j<groesse; j++) {
                //... den Abstand von Mitte. 
                double r= Math.sqrt(Math.pow(i-mitteX,2)+Math.pow(j-mitteY,2));
                //... setze den Wert der Zelle 
                gaussfilter[i][j] = Math.exp(-(double)(Math.pow(r,2))/(2*Math.pow(sigma,2))); 
            }
        };

        // Berechne K, indem du alle Werte der Matrix addierst ...
        double summe = 0;
        for(int i=0; i< groesse; i++) {
            for(int j=0; j<groesse; j++) {
                summe += gaussfilter[i][j];
            }
        };

        // ... und den Kehrwert bildest.
        double k = 1/summe;

        // Multipliziere jede Zelle der Matrix mit K. 
        for(int i=0; i< groesse; i++) {
            for(int j=0; j<groesse; j++) {
                gaussfilter[i][j] *= k ; 
            }
        };
        return faltung(originalbild,gaussfilter);
    }

}
