import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.plugin.filter.*;
import java.util.GregorianCalendar;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import ij.plugin.*;


public class Misalignment_ implements PlugInFilter, ActionListener, ItemListener, ChangeListener{
	private ImagePlus imp;
	private GenericDialog gd;
	private int height, width;
	private int line, trasl;
	private ImageProcessor ipOriginal;
	private ImageProcessor ip;
	private JSpinner spinner_line, spinner_trasl; 
	private List lst;
	private JSlider linea, traslation;
	private JLabel label1,label2;
	private int [] reportInfo;
	private boolean add = false;
	private Zoom Z = new Zoom();
			
	public int setup(String arg, ImagePlus imp) {
		if (imp==null) { 
			IJ.showStatus("No image");
                   IJ.noImage();
			return -1;
		}
		ip=imp.getProcessor();
		ipOriginal = ip.duplicate();
		this.imp = imp;
		return DOES_ALL;
	}

	public void run(ImageProcessor ip) {
		IJ.showStatus("Select line and traslate");
	      initialize();
		
		if (gd.wasCanceled()) {
			Z.run("orig");
			imp.setProcessor(ipOriginal); 
			return;
		}
		
		imp.show();

		boolean report = gd.getNextBoolean();
  		if(report)
			makeReport();
	}
	
	private void initialize()
	{
		height= imp.getHeight();
		width= imp.getWidth();
		
		reportInfo=new int[height];
		setReportInfo();
		
		gd = new GenericDialog("Insert the input");
		
		linea=new JSlider(JSlider.HORIZONTAL,0, height-1,0); 		
		traslation= new JSlider(JSlider.HORIZONTAL,-width,width,0);
		
		SpinnerModel l=new SpinnerNumberModel(0, 0,height-1,1);
		spinner_line=new JSpinner(l);
		
		SpinnerModel t=new SpinnerNumberModel(0,-width, width,1);  
		spinner_trasl=new JSpinner(t);
		
		linea.addChangeListener(this);
		traslation.addChangeListener(this);
		
		spinner_line.addChangeListener(new SpinnerChange()); 
		spinner_trasl.addChangeListener(new SpinnerChange());
		
		label1 = new JLabel("Line ");
		label2 = new JLabel("Traslation ");
	
		lst = new List(5, false);
		lst.addItemListener(this);

		Button buttonAdd = new Button("Add");
		Button buttonCancel = new Button("Cancel");
		Button buttonDeleteAll = new Button("Delete All");
		Button buttonZoom_in = new Button("Zoom in");
		Button buttonZoom_out = new Button("Original size");

		Panel panelLine = new Panel();
		panelLine.setLayout(new GridLayout(1,3));
		Panel panelTrasl = new Panel();
		panelTrasl.setLayout(new GridLayout(1,3));
		Panel panelZoom = new Panel();
		panelZoom.setLayout(new GridLayout(1,2));
		
		Panel panel = new Panel();		
		panel.setLayout(new GridLayout(2,1));
		Panel panel1 = new Panel();
		
		panel1.add(buttonAdd);
		panel1.add(buttonCancel);
		panel1.add(buttonDeleteAll);
		panelZoom.add(buttonZoom_in);
		panelZoom.add(buttonZoom_out);
		panelLine.add(label1);
		panelLine.add(spinner_line);
		panelLine.add(linea);
		panelTrasl.add(label2);
		panelTrasl.add(spinner_trasl);
		panelTrasl.add(traslation);

		panel.add(lst);
		panel.add(panel1);

		gd.addPanel(panelZoom);
		gd.addPanel(panelLine);
		gd.addPanel(panelTrasl);
		gd.addPanel(panel);
		
		buttonAdd.addActionListener(this);
		buttonCancel.addActionListener(this);
		buttonDeleteAll.addActionListener(this);
		buttonZoom_in.addActionListener(this);
		buttonZoom_out.addActionListener(this);

		gd.addCheckbox("Print report ", true);

		gd.showDialog();
	}
	
	private void setReportInfo() {
		for(int i=0; i< reportInfo.length;i++)
			reportInfo[i]=0;
	}
		
	private void makeReport() {
		GregorianCalendar calendar = new GregorianCalendar();
		String user= System.getProperty("user.name");
		
		//Name and Directory of the current image
		String shortname=IJ.getImage().getShortTitle();
		String name= IJ.getImage().getTitle();
		String dir = IJ.getDirectory("image");
		
		String info=arrayReportToString();
		String report= "<html><Head> <title>Report </title></head><body><h1>ImageJ Report</h1>Report Generation: "+ calendar.getTime()+"<br />User: "+user+" <br />File: "+dir+name+" <br /> <br /><h3> Disallineamento </h3><h4>Dettagli:</h4><ul>\"Disallineamento\" permette di correggere l'errore di allineamento delle righe. Ogni riga selezionata viene traslata orizzontalmente di un numero di pixel scelto a discrezione dell'utente.</ul><BR><h4>Parametri: </h4> <ul> <li><b>Spostamenti: </b><br/>"+info+"</li></ul> <br/></body></html>";
		
		try{ 
			//Creates a new file (.html) containing of the information filter applied
			PrintWriter pw=new PrintWriter(new BufferedWriter (new FileWriter(dir+shortname+".html")));
			pw.print(report);
        		pw.close();
		}
		catch (Exception e) {}
	}

	private String arrayReportToString(){
		String rep=new String();
		for(int i=0;i<reportInfo.length;i++){
			if(reportInfo[i]!=0)
				rep=rep +"Line "+ i +": "+reportInfo[i]+"<br/>";
		}
		return rep;
	}
	
	
	private void shift()
	{
		// get pixel value of selected line		
		int[] data = new int[width];
		ipOriginal.getRow(0, line, data, width); 		
		int[] dataTraslate = new int[width];
		int j;
		
		if(trasl>=0) {
			j=0;
			for (int i=0; i<width; i++) {
				dataTraslate[i]=0;
				if(i>=trasl) {	
					dataTraslate[i]=data[j];
					j++;
				}
			}
		}
		else {
			j=width-1;
			for (int i=width-1; i>=0; i--) {
				dataTraslate[i]=0;
				if(i<=width+trasl) {	
					dataTraslate[i]=data[j];
					j--;
				}
			}
		}
		ip.putRow(0, line, dataTraslate, width);
		imp.updateAndDraw();
	}

	private int searchLine(int x){
		String[] info;
		for (int i=0;i<lst.getItemCount();i++) {
			info = lst.getItem(i).split(" ");
			if (Integer.parseInt(info[1])==x)
				return i; 
		}
		return -1;
	}
	
	public void insertOrd(int x, String s){
		if(lst.getItemCount()==0){
			lst.add(s,0);
			return;
		}
		String[] info;
		for (int i=0; i<lst.getItemCount(); i++){
			info =lst.getItem(i).split(" ");
				if(Integer.parseInt(info[1])>x){
					lst.add(s, i);
					return;
				}
		}
		info=lst.getItem(lst.getItemCount()-1).split(" ");
		if(Integer.parseInt(info[1])<=x){
			lst.add(s);
			return;
		}
	}
		
	// ===============================================
	// Method for events handling
	// ===============================================

	public void itemStateChanged(ItemEvent e) {
			int pos =lst.getSelectedIndex();
			String item = lst.getItem(pos);
			String[] dettagli = item.split(" ");
			linea.setValue(Integer.parseInt(dettagli[1]));
			traslation.setValue(Integer.parseInt(dettagli[3]));
	}

	public void actionPerformed(ActionEvent e){
		String command = e.getActionCommand();
		if (command==null) return;
		else if(command.equals("Add"))  {
			add = true; 
			if (trasl==0) return;
			else if(reportInfo[line]==trasl) return;
			String newvalue="Line "+line+" : "+trasl;
			if(reportInfo[line]!=0) {
				int pos=searchLine(line);
				lst.replaceItem(newvalue, pos);
				reportInfo[line]=trasl; 
			}
			else {
				
				insertOrd(line, newvalue); 
				reportInfo[line]=trasl; 
			}
			line++;
			linea.setValue(line);
			traslation.setValue(0);
			
		}
		else if(command.equals("Cancel")) {
			// restore the original selected line
			int pos = lst.getSelectedIndex();
			String item = lst.getItem(pos);
			String[] dettagli = item.split(" ");
			int lineSelect = Integer.parseInt(dettagli[1]);
			int[] data = new int[width];
			ipOriginal.getRow(0, line, data, width);
			ip.putRow(0, line, data, width);
			reportInfo[lineSelect]=0;
			imp.updateAndDraw();
			lst.remove(pos);
		}
		else if(command.equals("Delete All")) {
			// restore the original image
			lst.removeAll();
			setReportInfo();
			linea.setValue(0);
			imp.setProcessor(ipOriginal); 
			ip=imp.getProcessor();
			ipOriginal = ip.duplicate();
		}
		else if(command.equals("Zoom in")) {
			Z.run("in");
		}
		else if(command.equals("Original size")) {
			Z.run("orig");
		}
	}
	
	public void stateChanged(ChangeEvent e){
		Object source = e.getSource();
		if (source==linea) {
			
			if(add==false){ 
				traslation.setValue(0); 
				trasl = reportInfo[line];
				shift();
			}
			//view recent value of trasl for each line
			if(reportInfo[line]!=0)
				traslation.setValue(reportInfo[line]);
			line = linea.getValue();
			spinner_line.setValue(line);
			add=false; 
		}
		else if (source==traslation) {
			trasl = traslation.getValue();
			spinner_trasl.setValue(trasl);	
			if(reportInfo[line]==trasl) return; // no new traslation
			else if (trasl==0)  
				shift(); // the line remains the same
			else if ((reportInfo[line]!=0) || (reportInfo[line]==0)) { 
				shift();  
			}	
		}	
	}
	
	
	class SpinnerChange implements ChangeListener{
		public void stateChanged(ChangeEvent e) {
			Object source = e.getSource();
			if (source==spinner_line){
				if(add==false && trasl!=0){ 
					traslation.setValue(0); 
					trasl = reportInfo[line];
					shift();
				}
				add=false;
				line=Integer.parseInt(spinner_line.getValue().toString());
				linea.setValue((int)line);
			}
			else if(source == spinner_trasl){
				trasl=Integer.parseInt(spinner_trasl.getValue().toString());
				traslation.setValue(trasl);
			}
		}
	}
	
	
	
}

