import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Checkbox;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.List;
import java.awt.Rectangle;
import java.awt.Scrollbar;
import java.awt.TextField;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.filechooser.FileNameExtensionFilter;

import net.miginfocom.swing.MigLayout;
import ij.IJ;
import ij.ImagePlus;
import ij.gui.ImageCanvas;
import ij.gui.ImageWindow;
import ij.io.SaveDialog;
import ij.plugin.filter.PlugInFilter;
import ij.process.ImageProcessor;

public class CloneDetector implements PlugInFilter, MouseListener {
	private boolean enableMouse = false;
	private ImagePlus img, imgSwap;
	private ImageCanvas canvas;
	private ImageProcessor ip, ipSwap;
	private Button selectarea, start, genreport;
	private int threshold,firstPx,firstPy,secondPx, secondPy, width, resize,heigth, targetOffsetX, targetOffsetY, dim, scaling,dangle;
	private float MAE;
	private Rectangle myRoi;
	private JLabel labelarea, labelareax, labelareay, labelareaw, labelareah;
	private ImageProcessor SourceIp;
	private ArrayList<String> listaResult = new ArrayList<String>();
	private JTextArea txtfound, txtthreshold, txtresize, txtdim, txtangle;

	
	@Override
	public int setup(String arg, ImagePlus img) {
		// TODO Auto-generated method stub
		this.img = img;
		imgSwap = img;
		return DOES_ALL + SUPPORTS_MASKING;
	}

	public void run(ImageProcessor ip) {
		threshold = 0;
		resize = 0;
		firstPx = 0;
		firstPy = 0;
		secondPx = 0;
		secondPy = 0;
		width = 0;
		heigth = 0;
		targetOffsetX=0;
		targetOffsetY = 0;
		this.ip = ip; 
		ipSwap = ip;
		ImageWindow win = img.getWindow();
		canvas = win.getCanvas();
		canvas.addMouseListener(this);		
		JFrame frame = new JFrame("Clone Detector");
	    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	    startInterface(frame);
	    frame.setVisible(true);
	}

	
	void startInterface(JFrame pane) {
		pane.setLocation(100, 100);
		pane.setPreferredSize(new Dimension(720,700));
		pane.setMinimumSize(new Dimension(720,700));
		pane.setLayout((new MigLayout("wrap 3")));
		selectarea = new Button("Select Area");
		labelarea = new JLabel("Coordinate:");
		labelareax = new JLabel();
		labelareay = new JLabel();
		labelareaw = new JLabel();
		labelareah = new JLabel();
		pane.add(selectarea, "wrap, w 150!");
		pane.add(labelarea, "wrap, w 150!");
		pane.add(labelareax, "wrap, w 150!");
		pane.add(labelareay, "wrap, w 150!");
		pane.add(labelareaw, "wrap, w 150!");
		pane.add(labelareah, "wrap, w 150!");
		JLabel labelthreshold = new JLabel("Threshold: (def=3)");
		txtthreshold = new JTextArea();
		txtthreshold.setLineWrap(true);
		txtthreshold.setWrapStyleWord(true);
		pane.add(labelthreshold, "w 150!");
		pane.add(txtthreshold, "wrap, w 200!");
		JLabel labelresize = new JLabel("Scaling: (def=0)");
		txtresize = new JTextArea();
		txtresize.setLineWrap(true);
		txtresize.setWrapStyleWord(true);
		pane.add(labelresize, "w 150!");
		pane.add(txtresize, "wrap, w 200!");
		JLabel labelangle = new JLabel("Rotation Angle: (def=90)");
		txtangle = new JTextArea();
		txtangle.setLineWrap(true);
		txtangle.setWrapStyleWord(true);
		pane.add(labelangle, "w 150!");
		pane.add(txtangle, "wrap, w 200!");
		JLabel labeldim = new JLabel("Submatrices: (def=0)");
    	txtdim = new JTextArea();
    	txtdim.setLineWrap(true);
    	txtdim.setWrapStyleWord(true);
    	pane.add(labeldim, "w 150!");
		pane.add(txtdim, "wrap, w 200!");
		selectarea.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				labelarea.setText("Coordinate: ");
				labelareax.setText("x= " + targetOffsetX);
				labelareay.setText("y= " + targetOffsetY);
				labelareaw.setText("width= " + width);
				labelareah.setText("heigth= " + heigth);
			}
		});
		start = new Button("Start");
		start.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				makeSource();
				try {
					threshold = Integer.parseInt(txtthreshold.getText());
				} catch (NumberFormatException a) {
					threshold = 3;
				}
				try {
					resize = Integer.parseInt(txtresize.getText());
				} catch (NumberFormatException a) {
					resize = 0;
				}
				try {
					dangle = Integer.parseInt(txtangle.getText());
				} catch (Exception a) {
					dangle = 90;
				}
				try {
					dim = Integer.parseInt(txtdim.getText());
					if (dim >3){
						dim=3;
					} else if (dim <0){
						dim=0;
					}
				} catch (NumberFormatException a) {
					dim = 0;
				}
				Search_Clone(threshold);
			}
		});
		genreport = new Button("Report");
		genreport.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				creaReportFile();
			}
		});

		pane.add(start, "wrap, w 150!");
		pane.add(genreport, "w 150!");
		
		

		txtfound = new JTextArea("");
		pane.add(txtfound, "wrap, w 500!, h 400!");

		
		
		txtfound.setVisible(true);
		txtthreshold.setVisible(true);
		//pane.pack();
		pane.setVisible(true);
		pane.setResizable(false);

	}


	public void makeSource() {
		myRoi = new Rectangle();
		myRoi.x = firstPx;
		myRoi.y = firstPy;
		myRoi.width = width;
		myRoi.height = heigth;
		ip.setRoi(myRoi);
		SourceIp = ip.crop();
	}
	public void makeSource2(int currentdim) {
		myRoi = new Rectangle();
		myRoi.x = firstPx+(5*currentdim);
		myRoi.y = firstPy+(5*currentdim);
		myRoi.width = width-(5*currentdim);
		myRoi.height = heigth-(5*currentdim);
		ip.setRoi(myRoi);
		SourceIp = ip.crop();
	}


	void Search_Clone(int threshold) {
		int width2 = ip.getWidth();
		int height2 = ip.getHeight();
		int[] arrayCopy = new int[3];
		int[] arrayReal = new int[3];
		MAE = 0;
		int t = 1;
		
		SourceIp=SourceIp.resize(SourceIp.getWidth() + resize,
				SourceIp.getHeight() + resize);
		width=SourceIp.getWidth();
		heigth=SourceIp.getHeight();
		for (int m=0;m<=dim;m++){
			makeSource2(m);
		
			for (int n=0; n<3;n++){
				if (n==1){
					SourceIp.flipHorizontal();
				}else if (n==2){
					SourceIp.flipHorizontal();
					SourceIp.flipVertical();
				}
			
				for (int r = 0; r < (360/dangle); r++) {
					if (r != 0) {
						t = 5;
						if(dangle==90){
							SourceIp = SourceIp.rotateRight();
						}else{
							SourceIp.rotate(dangle);
						}
					}
				
				
				
					for (int i = 0; i < width2; i++) {
						for (int l = 0; l < height2; l++) {		
								for (int j = 0; j < SourceIp.getWidth(); j++) {
									for (int k = 0; k < SourceIp.getHeight(); k++) {
										
										ip.getPixel(i + j, l + k, arrayReal);
										SourceIp.getPixel(j, k, arrayCopy);
										
										int primo = Math.abs(arrayReal[0]
												- arrayCopy[0]);
										int secondo = Math.abs(arrayReal[1]
												- arrayCopy[1]);
										int terzo = Math.abs(arrayReal[2]
												- arrayCopy[2]);
		
										MAE += (primo + secondo + terzo) / 3;
										
									}
		
								}
		
								
								if ((((MAE / (myRoi.width * myRoi.height))) <= (threshold * t) && MAE != 0)) {
									ip.setLineWidth(1);
									ip.setColor(Color.CYAN);
									ip.drawRect(i, l, SourceIp.getWidth(),
											SourceIp.getHeight());
									String clonefound = "Clone found, Coordinate: "
											+ "x: " + i + " y: " + l + " w: "
											+ myRoi.width + " h: " + myRoi.height +
											 " abs average: "+(MAE / (myRoi.width * myRoi.height)) + "\n" ;
									listaResult.add(clonefound);
									MAE = 0;
									
		
								} else {
									MAE = 0;
								}
						}
					}
				}
			}
		}

		SourceIp.reset();
		SourceIp = SourceIp.rotateRight();
		img.updateAndDraw();
		String result = "";
		for (int i = 0; i < listaResult.size() && listaResult.get(i) != null; i++) {
			txtfound.append(listaResult.get(i)+"; ");
		}
		txtfound.setCaretPosition(txtfound.getDocument().getLength());
	}

	@Override
	public void mouseClicked(MouseEvent arg0) {
		// TODO Auto-generated method stub
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
	}

	@Override
	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub
		firstPy = e.getY();
		firstPx = e.getX();
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		int roiX, roiY;
		secondPy = e.getY();
		secondPx = e.getX();
		width = Math.abs(secondPx - firstPx);
		heigth = Math.abs(secondPy - firstPy);
		if (firstPx < secondPx)
			roiX = firstPx;
		else
			roiX = secondPx;
		if (firstPy < secondPy)
			roiY = firstPy;
		else
			roiY = secondPy;
		ip.setRoi(roiX, roiY, width, heigth);
		myRoi = ip.getRoi();

		targetOffsetX = roiX;
		targetOffsetY = roiY;

	}

	public void creaReportFile() 
	{

		SaveDialog s = new SaveDialog("Save Report","report",".html");
		String file = s.getDirectory(); 
		file += s.getFileName();       
		try {
			String image = img.getOriginalFileInfo().directory;
			image += img.getOriginalFileInfo().fileName;
			GregorianCalendar gc = new GregorianCalendar();
			int yy = gc.get(Calendar.YEAR);
			int mm = gc.get(Calendar.MONTH) + 1;
			int dd = gc.get(Calendar.DATE);
			int hh = gc.get(Calendar.HOUR_OF_DAY);
			int mi = gc.get(Calendar.MINUTE);
			int ss = gc.get(Calendar.SECOND);
			
			FileWriter report = new FileWriter(file);
			report.append("<html>");
			report.append("<head>");
			report.append("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">");
			report.append("</head>");
			report.append("<body>");
			report.append("<h1>Clone Detector Resaults</h1>");
			report.append("Report Generation Date: "+"<strong>" + dd +"\\"+mm+"\\"+
					yy	+" "+hh+":"+mi+":"+ss+"</strong><BR>");
			report.append("Image: "+"<strong>"+image+"</strong><BR>");
			report.append("<p><ul><li><strong>Selected Area:</strong> The x and y coordinate of the first pixel, the weight and the height of the selected area.</li></ul>" + 
					"<ul><li><strong>Threshold:</strong> The Threshold value for the search.</li></ul>"+
					"<ul><li><strong>Scaling:</strong>The Scaling value, which is carried out both in width and in height.</li></ul>"+
					"<ul><li><strong>Rotation Angle:</strong>The incrase of the angle in every try.</li></ul>"+
					"<ul><li><strong>Number of Submatrices:</strong>The number of submatrices that will be detected after the original image.</li></ul>"+
					"<ul><li><strong>Resaults:</strong> The x and y coordinate of the first pixel of every clones detected.</li></ul></p>");
			

		
			report.append("Threshold="+threshold+"<br>");	
			report.append("Scaling="+resize+"<br>");
			report.append("Angle="+dangle+"<br>");	
			report.append("Number of submatrices="+dim+"<br>");	
			report.append("Resaults:");
			report.append(txtfound.getText()+"<br>");
		
					
		
				report.append("</ul>");	
				report.close();
			} catch (IOException e) {
			IJ.error("Error: File output not found");
			}
	}
}
