import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
import javax.imageio.*;
import java.awt.Dimension; 
import java.awt.Graphics;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.border.*;
import javax.swing.event.*; 
import java.util.Date;
import ij.*;
import ij.process.ImageProcessor.*;
import ij.process.*;
import ij.process.FHT.*;
import ij.plugin.filter.*;
import ij.plugin.*;
import ij.plugin.FFT.*;
import java.lang.Math.*;
import java.lang.String.*;
import javax.swing.*;
import ij.measure.Calibration;

class PanelPrincipale extends JPanel implements MouseMotionListener,MouseListener 
{
	Point st,fn;
	Semiretta semX,semY;	
	Image img;
	ListaConcatenata lista;
	Boolean drag=false;
	Boolean rettaX,rettaY,misura;
  
	public PanelPrincipale(Image img,ListaConcatenata lista)
	{
		super();
		this.img = img;
		this.lista = lista;   
   
		semX = semY = null;	  
		rettaX = true; 
		rettaY = false;
		misura = false;	  
		setPreferredSize( new Dimension ( img.getWidth( null ) , img.getHeight( null ))) ;
		addMouseMotionListener(this);  // il pannello ascolta se stesso
		addMouseListener(this); 	  
	}

	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		g.drawImage(img,0,0,this);
   
		float[] array = { 4f };
		Graphics2D g2 = ( Graphics2D ) g ;
		g2.setStroke ( new BasicStroke ( 3f, 1 , 1 ,0f,array,0));
		//colore retta x
		
		if ( semX != null )
		{
			g.setColor(Color.blue);
			g.drawLine(semX.st.width,semX.st.height,semX.fn.width,semX.fn.height); 
			//g.setColor(Color.white); 
			//g.fillRect(semX.st.width-3,semX.st.height-25,20, 20); 
			//g.setColor(Color.black); 
			//g.setFont(new Font("BOLD",1,14));
	        
			//g.drawString("X",semX.st.width,semX.st.height-5);
		}
		
		//colore retta y
		g.setColor(Color.green); 
		if( semY != null )
		{
			g.drawLine(semY.st.width,semY.st.height,semY.fn.width,semY.fn.height);
			g.setColor(Color.white); 
			//g.fillRect(semY.st.width+5,semY.st.height,20, 20); 
			//g.setColor(Color.black); 
			//g.setFont(new Font("BOLD",1,14));
			//g.drawString("Y",semY.st.width+10,semY.st.height+15);
		}
		
		//colore altre rette
		g.setColor(Color.red); 
		if( misura && st != null && fn != null && drag )
			g.drawLine((int)st.getX(),(int)st.getY(),(int)fn.getX(),(int) fn.getY());
    
		lista.reset(); 
		Semiretta s=null;
		int indiceRect=1;
		while((s=lista.next())!=null)
		{
			g.setColor(Color.white); 
			g.fillRect(s.st.width-3,s.st.height-20,33, 20); 
			g.setColor(Color.black); 
			g.setFont(new Font("BOLD",1,14));
	        
			g.drawString("Z_"+(indiceRect++),s.st.width,s.st.height-5);
			g.setColor(Color.red); 
			g.drawLine(s.st.width,s.st.height,s.fn.width,s.fn.height); 
		}
	}
	
	public void mouseMoved(MouseEvent e){}
		
	public void mouseDragged(MouseEvent e)
	{
		drag=true;
		fn=e.getPoint();
		
		if(rettaX)semX=new Semiretta((int)st.getX(),(int)st.getY(),(int)fn.getX(),(int) st.getY());
			else if(rettaY)semY=new Semiretta((int)st.getX(),(int)st.getY(),(int)st.getX(), (int)fn.getY());	
				
		repaint();
	}
	
	public void mousePressed(MouseEvent e)
	{ 
		st=e.getPoint();
	}

	public void mouseReleased(MouseEvent e)
	{
		drag=false;
		fn=e.getPoint();
		if(misura)
		{
			lista.add(new Semiretta((int)st.getX(),(int)st.getY(),(int)fn.getX(),(int)fn.getY()));
			lista.aggiornaMisure();
			lista.repaint();
		}
		repaint();
	}

	public void mouseClicked(MouseEvent e){}
		
	public void mouseEntered(MouseEvent e){}
		
	public void mouseExited(MouseEvent e){}
}

class PanelControllo extends JPanel implements ActionListener
{  
	private ListaConcatenata lista;
	private JTextField origineMisuraX;
	private PanelPrincipale principale;
	private JTextField origineMisuraY;
	private ImagePlus imp;
	JButton apriImm,apriR;
	String nomeFile,nomeFileI;
	
	public PanelControllo(ListaConcatenata lista,PanelPrincipale principale,ImagePlus imp)
	{      this.imp=imp; 
		nomeFile = nomeFileI = "";
		this.lista=lista;
		this.principale=principale;
		
		setLayout ( new GridLayout ( 4 , 1 , 15 , 15  ));
	    
		// Bordo RETTE DI RIFERIMENTO
		bordo riferimenti = new bordo ( " Rette di riferimento " );
		JPanel origine = new JPanel();
		origine.setLayout ( new GridLayout ( 3 , 3 , 10 , 10  ));
		
		ButtonGroup gruppo = new ButtonGroup();
		JRadioButton rettax = new JRadioButton ( " Retta X " ,true);
		JRadioButton rettay = new JRadioButton ( " Retta Y " );
		JRadioButton rettaz = new JRadioButton ( " Retta Z " );
		gruppo.add(rettax);
		gruppo.add(rettay);
		gruppo.add(rettaz);
		
		JButton conferma=new JButton("Applica");
		origineMisuraX=new JTextField("0.0",5);
		origineMisuraX.setHorizontalAlignment(JTextField.CENTER);
		origineMisuraY=new JTextField("0.0",5);
		origineMisuraY.setHorizontalAlignment(JTextField.CENTER);
		
		origine.add( rettax );
		origine.add(origineMisuraX);
		origine.add(new JLabel("metri"));
		
		origine.add( rettay );
		origine.add(origineMisuraY);
		origine.add(new JLabel("metri"));
		
		origine.add(rettaz);
		origine.add(conferma);
		riferimenti.add(origine);
		
		add(riferimenti);
		
		// Bordo OPZIONI 
		bordo opzioni = new bordo ("Opzioni");
		JPanel bottoni=new JPanel();
		bottoni.setLayout ( new GridLayout ( 3 , 2 , 10 , 10  ));
		
		JButton donate = new JButton("Credit");
		JButton salva = new JButton("Salva Immagine");
		apriImm = new JButton("Apri Immagine Salvata");
		apriR = new JButton("Apri Report Salvato");
		JButton report = new JButton("Salva Report");
		JButton help = new JButton("Help");
		
		apriImm.setEnabled(false);
		apriR.setEnabled(false);
		
		bottoni.add(salva);
		bottoni.add(report);
		bottoni.add(apriImm);
		bottoni.add(apriR);
		bottoni.add(help);
		bottoni.add(donate);
		opzioni.add(bottoni);
		add(opzioni);
		
		// Bordo Lista
		bordo storico = new bordo ("Storico");
		
		JButton prec=new JButton("Precedente");
		JButton next=new JButton("Successivo");
		
		JPanel p1 = new JPanel ();
		
		storico.setLayout ( new BoxLayout(storico,BoxLayout.Y_AXIS));
		
		p1.add( prec );
		p1.add( next );
		storico.add(p1);
		storico.add(lista);
		
		add(storico);
		
		//AGGIUNGE I BOTTONI ALL'ASCOLTATORE
		conferma.addActionListener(this);
		apriImm.addActionListener(this);
		apriR.addActionListener(this);
		prec.addActionListener(this);
		next.addActionListener(this);
		donate.addActionListener(this);
		salva.addActionListener(this);
		help.addActionListener(this);
		report.addActionListener(this);
	
		rettax.addActionListener(this);
		rettay.addActionListener(this);
		rettaz.addActionListener(this);
	}
	
	public void generaReport()
	{
		Date dt=new Date();
		
		String html="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//IT\">\n"+
					"<html>\n<head>\n"+
					"<title>Report "+dt+"</title>\n</head>\n<body>"+
					"<center><h1> Amenta & Cristaldi Plugin Misurazione 2D - Report</h1></center>"+
					"    Report generation : "+ dt.toString() +"<BR><br>"+
					"    User : " + System.getProperty("user.name") + "<br>"+
					"<hr><h4> PASSI EFFETTUATI :</h4><span style=font-style: italic;font-weight:normal></ul><br><ol><LI><strong>Caricamento Immagine : </strong>"+imp.getTitle()+"</li>"+
					"<br>Da Image-J è stata caricata l'immagine.<br><br>"+
					"<li><strong>Misurazione 2D Plugin</strong><br><br> Calcola da singole immagini misurazioni relative al mondo reale, utilizzando due distanze note come riferimento.</li></ol></span><br>"+
					"<hr><br><strong>Dettagli</strong>: <br><ul>Misurazione 2d calcola distanze nel mondo reale a partire da un'immagine a cui è stata applicata una correzione prospettica.</ul>"+
					"<hr><center><h4><strong>Immagine risultante</strong>:</h4></center>"+
					"<center><img src="+nomeFileI+".jpg><br><br></center>"+
					"<hr><br><strong>Parametri Misurazione 2D</strong>:";
                                        lista.reset(); 
					Semiretta s=null;
					int indiceRect=1;
		                        if(principale.semX!=null){
						html+="<ul><li><span style=font-weight: bold><strong> Linea di riferimento asse X</strong></span> : Punto Iniziale ("+principale.semX.st.width+","+principale.semX.st.height+") , Punto finale ("+principale.semX.fn.width+","+principale.semX.fn.height+")</li><br><span style=font-style: italic;>Le coordinate dei pixel relative agli estremi della linea orizzontale la cui dimensione è nota all'utente.</span><br><BR></ul></li>";
						html+="<ul><li><span style=font-weight: bold><strong> Lunghezza di riferimento asse X</strong></span> : "+origineMisuraX.getText()+"<br></li><br><span style=font-style: italic;>La distanza nel mondo reale tra gli estremi della linea di riferimento lungo l'asse x dell'immagine.</span><BR><BR></ul></li>";
					}
					if(principale.semY!=null){
						html+="<ul><li><span style=font-weight: bold><strong> Linea di riferimento asse Y</strong></span> : Punto Iniziale ("+principale.semY.st.width+","+principale.semY.st.height+") punto finale ("+principale.semY.fn.width+","+principale.semY.fn.height+")</li><br><span style=font-style: italic;>Le coordinate dei pixel relative agli estremi della linea verticale la cui dimensione è nota all'utente.</span><BR><BR></ul></li>";
						html+="<ul><li><span style=font-weight: bold><strong> Lunghezza di riferimento asse Y</strong></span> : "+origineMisuraY.getText()+"<br></li><br><span style=font-style: italic;>La distanza nel mondo reale tra gli estremi della linea di riferimento lungo l'asse y dell'immagine.</span><BR><BR></ul></li>";
					}
					html+="<hr><br><strong>Risultati delle rette calcolate </strong>:";
					while((s=lista.next())!=null){
						html+="<ul><li><span style=font-weight: bold><strong> Linea "+"Z_"+(indiceRect)+" </strong></span> : Punto Iniziale ("+s.st.width+","+s.st.height+") Punto Finale ("+s.fn.width+","+s.fn.height+")</li><br><span style=font-style: italic;>Le coordinate dei pixel relative all'oggetto della misurazione.</span><BR><BR></ul></li><ul><li><span style=font-weight: bold><strong> Lunghezza linea "+"Z_"+(indiceRect++)+" misurata</strong></span> : "+s.misura+"<br><br></li><span style=font-style: italic;>Il valore della misurazione.</span><BR><BR></ul></li>";
					}

					html+="</body></html>";
					
		nomeFile = JOptionPane.showInputDialog("Inserisci nome file");
		nomeFile+=".html";				
		
		try
		{  
			new File("plugins/Misura2D/Report").mkdirs();
			FileWriter fw = new FileWriter("plugins/Misura2D/Report/"+nomeFile);
			fw.write(""+html);
			fw.close();
			
		}catch(Exception e)
		{
			JOptionPane.showMessageDialog(null, "Errore Scrittura File", "Errore Scrittura File", JOptionPane.ERROR_MESSAGE);
		}
	}
  
	public void actionPerformed(ActionEvent e)
	{
		if(e.getActionCommand().equals("Precedente"))
		{
			lista.removeNodo();
			principale.repaint();
		}
		else if(e.getActionCommand().equals("Successivo"))
		{
			lista.ctrlY();
			principale.repaint();
		}
		else if(e.getActionCommand().equals(" Retta X "))
		{
			principale.rettaX=true;
			principale.rettaY=false;
			principale.misura=false;
		}
		else if(e.getActionCommand().equals(" Retta Y "))
		{
			principale.rettaX=false;
			principale.rettaY=true;
			principale.misura=false;
		}
		else if(e.getActionCommand().equals(" Retta Z "))
		{
			principale.rettaX=false;
			principale.rettaY=false;
			principale.misura=true;
		}
		else if(e.getActionCommand().equals("Applica"))
		{
		//calcola unità d misura
			if(principale.semX==null||principale.semY==null)
				return;
			float f1=Float.parseFloat(origineMisuraX.getText());
			float f2=Float.parseFloat(origineMisuraY.getText());
			float ipoteX=(float)Math.abs(principale.semX.st.width-principale.semX.fn.width);
			float ipoteY=(float)Math.abs(principale.semY.st.height-principale.semY.fn.height);
			lista.aggiornaMisure(f1/ipoteX,f2/ipoteY);
		}
		else if(e.getActionCommand().equals("Salva Report"))
		{
			generaReport();
			apriR.setEnabled(true);
		}
		else if(e.getActionCommand().equals("Credit"))
		{
			try{
			Desktop desktop = null;
				if (Desktop.isDesktopSupported())
				{
					desktop = Desktop.getDesktop();
				}
				String path = ricerca("credits2d.htm", new File ( System.getProperty("user.dir")));
				if ( path == null )
					JOptionPane.showMessageDialog ( null, "Non è stato salvato il file credits2d.htm", "Errore Apertura File", JOptionPane.ERROR_MESSAGE);
				else
				desktop.open(new File(path));
				}
				catch (IOException ioe) {ioe.printStackTrace();}
		}
		else if(e.getActionCommand().equals("Apri Report Salvato"))
		{
			try {
				Desktop desktop = null;
				if (Desktop.isDesktopSupported())
				{
					desktop = Desktop.getDesktop();
				}
				desktop.open(new File("plugins/Misura2D/Report/"+nomeFile));
			       }
			       catch (IOException ioe) {ioe.printStackTrace();}
		}
		else if(e.getActionCommand().equals("Help"))
		{
			try{
			Desktop desktop = null;
				if (Desktop.isDesktopSupported())
				{
					desktop = Desktop.getDesktop();
				}
				String path = ricerca("Misura2D.pdf", new File ( System.getProperty("user.dir")));
				if ( path == null )
					JOptionPane.showMessageDialog ( null, "Non è stato salvato il file Misura2D.pdf", "Errore Apertura File", JOptionPane.ERROR_MESSAGE);
				else
				desktop.open(new File(path));
				}
				catch (IOException ioe) {ioe.printStackTrace();}
		}
		else if(e.getActionCommand().equals("Apri Immagine Salvata"))
		{
			try {
				Desktop desktop = null;
				if (Desktop.isDesktopSupported())
				{
					desktop = Desktop.getDesktop();
				}
				desktop.open(new File("plugins/Misura2D/Report/"+nomeFileI+".jpg"));
			       }
			       catch (IOException ioe) {ioe.printStackTrace();}
		}
		else if ( e.getActionCommand().equals("Salva Immagine"))
		{
			nomeFileI = JOptionPane.showInputDialog("Inserisci nome file");
			apriImm.setEnabled(true);
			float[] array = { 4f };
		
			BufferedImage ims=new BufferedImage(principale.img.getWidth(null),principale.img.getHeight(null),imp.getType());
                Graphics2D g2 = ims.createGraphics();
                g2.drawImage(principale.img,0,0,null); 
                
		g2.setStroke ( new BasicStroke ( 3f, 1 , 1 ,0f,array,0));
		//colore retta x
		g2.setColor(Color.blue); 
		if(principale.semX!=null){
			g2.drawLine(principale.semX.st.width,principale.semX.st.height,principale.semX.fn.width,principale.semX.fn.height); 
			g2.setColor(Color.white); 
			g2.fillRect(principale.semX.st.width-3,principale.semX.st.height-25,20, 20); 
			g2.setColor(Color.black); 
			g2.setFont(new Font("BOLD",1,14));
	        
			g2.drawString("X",principale.semX.st.width+2,principale.semX.st.height-8);
		}
		//colore retta y
		g2.setColor(Color.green); 
		if(principale.semY!=null) {  
			g2.drawLine(principale.semY.st.width,principale.semY.st.height,principale.semY.fn.width,principale.semY.fn.height);
			g2.setColor(Color.white); 
			g2.fillRect(principale.semY.st.width+5,principale.semY.st.height,20, 20); 
			g2.setColor(Color.black); 
			g2.setFont(new Font("BOLD",1,14));
			g2.drawString("Y",principale.semY.st.width+10,principale.semY.st.height+15);
		}
		//colore altre rette
		g2.setColor(Color.red); 
		if(principale.misura&&principale.st!=null&&principale.fn!=null&&principale.drag)
			g2.drawLine((int)principale.st.getX(),(int)principale.st.getY(),(int)principale.fn.getX(),(int) principale.fn.getY());
		
		lista.reset(); 
		Semiretta s=null;
		int indiceRect=1;
		while((s=lista.next())!=null)
		{
			g2.setColor(Color.white); 
			g2.fillRect(s.st.width-3,s.st.height-20,33, 20); 
			g2.setColor(Color.black); 
			g2.setFont(new Font("BOLD",1,14));
	        
			g2.drawString("Z_"+indiceRect++,s.st.width,s.st.height-5);
			g2.setColor(Color.red); 
			g2.drawLine(s.st.width,s.st.height,s.fn.width,s.fn.height);
		}
		
		salvataggio A = new salvataggio ((BufferedImage)ims , nomeFileI);
		
		}
	}
	public String ricerca (String s, File p)
	{
		if ( p.isDirectory())
		{
			String tmp;
			for ( File f : p.listFiles())
			{
				tmp = ricerca ( s , f );
				if ( tmp != null )
					return tmp;
			}
		}
		else if ( p.getName().contains(s))
			return p.getAbsolutePath();
		return null;
	}	
        
}

// ---------------------------------------------- BORDO PANEL ------------
  
class bordo extends JPanel
{
    // ______________________ COSTRUTTORE BORDO _____________________
    
	public bordo (String nomeBordo)
	{
		super();
		Border line = BorderFactory.createLineBorder ( Color.blue );
		TitledBorder lineTitle = BorderFactory.createTitledBorder(line,nomeBordo);
		setBorder ( lineTitle );
	}
}

// ------------------- SALVATAGGIO -------------------
class salvataggio
{
	public salvataggio (BufferedImage I, String nome )
	{
		File f = null;
		String est = "jpg" ;
				try
				{
					f = new File ( "plugins/Misura2D/Report/"+nome+"."+est);
				}
				catch ( Exception e )
				{JOptionPane.showMessageDialog(null, "Errore Salvataggio File", "Errore Salvataggio File", JOptionPane.ERROR_MESSAGE);}
			try
			{
				ImageIO.write ( I , est , f );
			}
			catch ( Exception e )
			{JOptionPane.showMessageDialog(null, "Errore Salvataggio File", "Errore Salvataggio File", JOptionPane.ERROR_MESSAGE);}
	}
}

class ListaConcatenata extends JPanel
{
        Nodo testa,coda;
        Nodo iter;          
	JTextArea view;
	JScrollPane scrollPaneLista;
	float valX,valY;
  
        public ListaConcatenata()
	{
                testa=coda=iter= null;
		valX=valY=0.0f;
		view = new JTextArea(5 , 20 );
		JScrollPane scrollPaneLista = new JScrollPane ( view , ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		add ( scrollPaneLista );
        }
        
	public void aggiornaMisure(float strX,float strY)
	{
		valX=strX;
		valY=strY;
		for(Nodo tmp=testa;tmp!=null;tmp=tmp.next)
		{
			tmp.value.misura=(float)Math.sqrt(Math.pow(valX*Math.abs(tmp.value.st.width-tmp.value.fn.width),2.0)+
		                                  Math.pow(valY*Math.abs(tmp.value.st.height-tmp.value.fn.height),2.0));
		}
                repaint();		
	}
	
	public void aggiornaMisure()
	{
		aggiornaMisure(valX,valY);
	}
        
	public void add(Semiretta r1)
	{
		if (testa==null)
		{ 
			testa=new Nodo(r1);
			coda=testa;
		}
		else
		{  
			coda.next=new Nodo(r1);
			coda=coda.next;
		}
        }

        public Semiretta next()
	{
		Semiretta s=null;
		if( iter==null )
			return null;
		if( coda != null && coda.equals(iter))
		{
			s=iter.value;
			iter=null;
		}
		else
		{
			s=iter.value;
			iter=iter.next;
		}	
	return s;
        }
        
        public void reset()
	{
		iter=testa;
        }
        
        public void removeNodo()
	{//rimuove ultimo nodo
		if(testa!=null)
		{
			if(testa.equals(coda))
			{
			testa=null;
			}
			else
			{
				for(Nodo tmp=testa;tmp!=null;tmp=tmp.next)
				if(tmp.next.equals(coda))
				{
					coda=tmp;
					break;
				}
			}
		repaint();
		}
        }
        
        public void ctrlY()
	{
		if(testa==null)
			testa=coda;
		else if(coda!=null&&coda.next!=null)
			coda=coda.next;
		repaint(); 
        }          
        //zona disegno
        public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		reset();
		Semiretta s=null;
		String temp = "";
		int i=1;
		while((s=next())!=null)
			temp+=" Retta Z_"+(i++)+" misura : "+s+"\n";
	view.setText(temp);
        }
}

class Nodo
{
        Semiretta value;
        Nodo next;
      
	public Nodo(Semiretta r)
	{
		value=r;
		next=null;
	}
}

class Semiretta
{ 
	Dimension st; //punto iniziale
	Dimension fn; // punto finale
	float misura;
    
	public Semiretta(Dimension s,Dimension f)
	{
		st=s;
		fn=f;
	}
    
	public Semiretta(int x1,int y1,int x2,int y2)
	{
		this(new Dimension(x1,y1),new Dimension(x2,y2));
	}
	
	public String toString()
	{
		return ""+misura;
	}
  }

public class Misura2D implements PlugInFilter
{
   // questa classe inizializzatrice ha solo il metodo main
   // il cui unico scopo è creare gli "oggetti" grafici
   // che costituiscono la vera applicazione.

	private ImagePlus imp;
	
	 public void run(ImageProcessor ip)
        {
		JFrame frame= new JFrame("Misurazione 2D Plugin ImageJ - Andrea Amenta & Francesco Cristaldi");
		frame.setSize(1000,700);
		
		ListaConcatenata lista=new ListaConcatenata(); 
		frame.setLayout(new BorderLayout());
		//pannello principale in cui si visualizza l'immagine e le linee    
		PanelPrincipale p1=new PanelPrincipale(imp.getImage(),lista);                 
		JScrollPane scrollPane = new JScrollPane(p1);                        
		frame.getContentPane().add(scrollPane,BorderLayout.CENTER) ;
    //pannello secondario in cui avremo la lista delle semirette con le effettive misure + i tasti d annulla e report
		frame.getContentPane().add(new PanelControllo(lista,p1,imp),BorderLayout.EAST) ;                   
    
		frame.setVisible(true);
        }

        // method from PlugInFilter Interface
	public int setup(String arg, ImagePlus imp)
	{
		this.imp = imp;
		return DOES_ALL;
	}
}