import ij.*;
import ij.io.*;
import ij.process.*;
import ij.gui.*;
import ij.text.*;
import java.awt.*;
import ij.plugin.Duplicator;
import java.awt.geom.*;
import java.awt.event.*;
import java.awt.image.*;
import ij.plugin.frame.*;
import java.io.*;
import java.util.*;
import javax.swing.event.*;
import java.text.*;

@SuppressWarnings( "deprecation" )
public class Plugin_Livelli extends PlugInFrame  implements ActionListener, AdjustmentListener, ItemListener, MouseListener{
	
	private ImagePlus imp, imp_original;
	private ImageProcessor ip;
	private int type;
	private Panel panel;
	private Button buttonRc, buttonRa, buttonApply;
	private Checkbox cAll, cRed, cGreen,cBlue;
	private CheckboxGroup channel;
	private PlotPLE plot;
	private Label lChannel, lMouse, lombre, lalteluci, lmezzitoni;
	private TextField tOmbre, tMezzitoni, tAlteluci;
	private Scrollbar sOmbre, sMezzitoni, sAlteluci;
	private int ombre, mezzitoni, alteluci;
	private long time;
	private int currentChannel = 0;
	private TonoPLE[] tchannel;
	public Plugin_Livelli() {
		super("Plugin Livelli");
		
	}
      
	public void run(String arg){

		imp = WindowManager.getCurrentImage();

		if (imp==null) {
			IJ.beep();
			IJ.showStatus("No image");
           		IJ.noImage();
			return;
		}
		
		imp_original = new Duplicator().run(imp);
		tchannel = new TonoPLE[4];
		tchannel[0] = new TonoPLE();
		tchannel[1] = new TonoPLE();
		tchannel[2] = new TonoPLE();
		tchannel[3] = new TonoPLE();
		ombre = 0;
		mezzitoni = 0;
		alteluci = 0;
		
		
		ip = imp.getProcessor();
        	type = imp.getType(); 
		initPC();
		setSize(new Dimension(430,460));
		GUI.center(this);
		show();
	}

	private void initPC(){
		setResizable(false);
		Color color=new Color(237,237,237);  
  


		plot = new PlotPLE();
		panel = new Panel();
		panel.setBackground(color);
		buttonApply = new Button("Apply");
		buttonRc = new Button("Restore Channel");
		buttonRa = new Button("Restore All");

		channel = new CheckboxGroup();
		cAll = new Checkbox("All", channel, true);
		cBlue = new Checkbox("Blue", channel, false);
		cGreen = new Checkbox("Green", channel, false);
		cRed = new Checkbox("Red", channel, false);
		
		sOmbre = new Scrollbar(Scrollbar.HORIZONTAL, 0, 0, -100, 101);
		sAlteluci = new Scrollbar(Scrollbar.HORIZONTAL, 0, 0, -100, 101);
		sMezzitoni = new Scrollbar(Scrollbar.HORIZONTAL, 0, 0, -100, 101);

		tOmbre = new TextField("0");
		tMezzitoni = new TextField("0");
		tAlteluci = new TextField("0");



		lChannel = new Label("Channel Selection");
		lMouse = new Label("(--- , ---)", Label.LEFT);
		plot.setLabelMouse(lMouse);
		panel.setLayout(null);


		lombre = new Label("Shadows");
		lombre.setBounds(10, 290, 70, 20);
		tOmbre.setBounds(90,290,40,20);
		sOmbre.setBounds(150, 290, 250, 25);


		lmezzitoni = new Label("Midtones");
		lmezzitoni.setBounds(10, 340, 70, 20);
		tMezzitoni.setBounds(90, 340, 40, 20);
		sMezzitoni.setBounds(150, 340, 250, 25);


		lalteluci = new Label("Highlights");
		lalteluci.setBounds(10, 390, 70, 20);
		tAlteluci.setBounds(90, 390, 40, 20);
		sAlteluci.setBounds(150, 390, 250, 25);

		cAll.setBounds(280, 30, 65, 25);
		cRed.setBounds(280, 50, 65, 25);
		cGreen.setBounds(280,70, 65, 25);
		cBlue.setBounds(280, 90, 65, 25);
	
		plot.setBounds(10, 10, 255, 255);

		lChannel.setBounds(280,10,120,10);




		buttonRc.setBounds(280,120,130,30);
		buttonRa.setBounds(280,150,130,30);
		buttonApply.setBounds(280,210,130,30);





		lMouse.setBounds(265,245,75,30);
		
		panel.add(cAll);
		panel.add(cRed);
		panel.add(cGreen);
		panel.add(cBlue);
		panel.add(plot);
		panel.add(lChannel);
		panel.add(lMouse);
		panel.add(buttonRc);
		panel.add(buttonRa);
		panel.add(buttonApply);
		panel.add(sOmbre);
		panel.add(lombre);
		panel.add(sAlteluci);
		panel.add(lalteluci);
		panel.add(sMezzitoni);
		panel.add(lmezzitoni);
		panel.add(tOmbre);
		panel.add(tMezzitoni);
		panel.add(tAlteluci);
		add(panel);

		buttonRa.addActionListener(this);
		buttonRc.addActionListener(this);
		buttonApply.addActionListener(this);
		cAll.addItemListener(this);
		cRed.addItemListener(this);
		cGreen.addItemListener(this);
		cBlue.addItemListener(this);
		
		sOmbre.addAdjustmentListener(this);
		sOmbre.addMouseListener(this);
		sAlteluci.addAdjustmentListener(this);
		sAlteluci.addMouseListener(this);
		sMezzitoni.addAdjustmentListener(this);
		sMezzitoni.addMouseListener(this);
		
	}
	
	public void lutCopy(int[] dst, int[] src){
		for(int i=0;i<256;i++)
          {
            dst[i]=src[i];
          }
		
	}
	
		




private void preview(){
			ImagePlus orig = new Duplicator().run(imp_original);
			imp.setImage(orig);
			int[] lut_r = new int[256];
			int[] lut_g = new int[256];
			int[] lut_b = new int[256]; 
			int[] lut_a =  new int[256];

			lutCopy(lut_a, updateChannel(0));
			lutCopy(lut_r, updateChannel(1));
			lutCopy(lut_g, updateChannel(2));
			lutCopy(lut_b, updateChannel(3));
			updateChannel(currentChannel);

			TransformPLE cst = new TransformPLE(imp, imp.getTitle(), lut_a, lut_r, lut_g, lut_b);
            time=System.currentTimeMillis();
            ImagePlus processed = null;
            if(type==ImagePlus.GRAY8)  processed = cst.createProcessed8bitImage();                  
	        if(type==ImagePlus.COLOR_256) processed = cst.createProcessed8bitImage();                   
	        if(type==ImagePlus.COLOR_RGB) processed = cst.createProcessedRGBImage();
			imp.setImage(processed);
			imp.updateAndDraw();
		   	imp.changes = false;
	}





	private String getValues(){
		String str = "";
	
		str += " <b>Ombre:</b> "+tchannel[0].s_ombre+"<br><i>Posizione del valore minimo di luminosita'</i><br><br>";
		str += " <b>Mezzitoni:</b> "+tchannel[0].s_mezzi+"<br><i>Curvatura del valore di luminosita'</i><br><br>";
		str += " <b>Luci:</b> "+tchannel[0].s_alte+"<br><i>Posizione del valore massimo di luminosita'</i><br><br>";

		str += " <b>Ombre del rosso:</b> "+tchannel[1].s_ombre+"<br><i>Posizione del valore minimo di rosso</i><br><br>";
		str += " <b>Mezzitoni del rosso:</b> "+tchannel[1].s_mezzi+"<br><i>Curvatura del valore di rosso</i><br><br>";
		str += " <b>Luci del rosso:</b> "+tchannel[1].s_alte+"<br><i>Posizione del valore massimo di rosso</i><br><br>";

		str += " <b>Ombre del verde:</b> "+tchannel[2].s_ombre+"<br><i>Posizione del valore minimo di verde</i><br><br>";
		str += " <b>Mezzitoni del verde:</b> "+tchannel[2].s_mezzi+"<br><i>Curvatura del valore di verde</i><br><br>";
		str += " <b>Luci del verde:</b> "+tchannel[2].s_alte+"<br><i>Posizione del valore massimo di verde</i><br><br>";

		str += " <b>Ombre del blu:</b> "+tchannel[3].s_ombre+"<br><i>Posizione del valore minimo del blu</i><br><br>";
		str += " <b>Mezzitoni del blu:</b> "+tchannel[3].s_mezzi+"<br><i>Curvatura del valore del blu</i><br><br>";
		str += " <b>Luci del blu:</b> "+tchannel[3].s_alte+"<br><i>Posizione del valore massimo del blu</i><br><br>";
		return str;
	}
	
	
	
	public int[] updateChannel(int channel){
			TonoPLE t = tchannel[channel];	
				return plot.findLUTT(t);

	}

	public void updateScroll(int channel){
			TonoPLE t = tchannel[channel ];
			sOmbre.setValue(t.s_ombre);
			sMezzitoni.setValue(t.s_mezzi);
			sAlteluci.setValue(t.s_alte);
			tOmbre.setText(""+t.s_ombre);
			tMezzitoni.setText(""+t.s_mezzi);
			tAlteluci.setText(""+t.s_alte);
	}


	public void resetChannel(int channel){
			tchannel[channel] = new TonoPLE();
			updateChannel(channel);
			updateScroll(channel);
	}
	

	@Override
	public void itemStateChanged(ItemEvent e) {
				
		Checkbox selection;
		selection = channel.getSelectedCheckbox();
		if(cAll.getState())
		{
			currentChannel = 0;
			updateChannel(currentChannel);
			updateScroll(currentChannel);
		}

		if(cRed.getState())
		{
			currentChannel = 1;
			updateChannel(currentChannel);
			updateScroll(currentChannel);
		}

		if(cGreen.getState())
		{
			currentChannel = 2;
			updateChannel(currentChannel);
			updateScroll(currentChannel);
		}

		if(cBlue.getState())
		{
			currentChannel = 3;
			updateChannel(currentChannel);
			updateScroll(currentChannel);
		}
	}

	@Override
	public void adjustmentValueChanged(AdjustmentEvent evt) {
		Object source = evt.getSource();

		if (source == sOmbre) {
			ombre = sOmbre.getValue();
			tOmbre.setText(""+ombre);
			tchannel[currentChannel].s_ombre = ombre;
			updateChannel(currentChannel);	
			//preview();
		}

		if (source == sAlteluci) {
			alteluci = sAlteluci.getValue();
			tAlteluci.setText(""+alteluci);
			tchannel[currentChannel].s_alte = alteluci;
			updateChannel(currentChannel);
			//preview();

		}


		if (source == sMezzitoni) {
			mezzitoni = sMezzitoni.getValue();
			tMezzitoni.setText(""+mezzitoni);
			tchannel[currentChannel].s_mezzi =  mezzitoni;
			updateChannel(currentChannel);
			//preview();
		}

		repaint();
	}
	
	
	
	@Override
	public void actionPerformed(ActionEvent e)
    {
        Object source=e.getSource();


		if(source == buttonApply)
        	{
		    	imp.updateAndRepaintWindow();
		   	imp.changes = true;
			//HTML
			String str = "<H1 align = center>Livelli</H1>";
			str += "<i>Regola i valori di intenista' dei pixel dell'immagine secondo le curve di colore desiderate.</i>";
			str += "<br><br><b>Dettagli:</b><br><br>";
			str += "'Livelli' mappa i valori dell'immagine in ingresso sui valori dell'immagine in uscita secondo la curva di colore impostata: la coordinata x della curva verra' mappata sul valore di y corrispondente.<br><br>";
			str += getValues();
			J2Html report = new J2Html();
			report.changeTextToHtml(str);
		 
         }


		if(source == buttonRa){
			resetChannel(0);
			resetChannel(1);
			resetChannel(2);
			resetChannel(3);
			preview();
		}
		
		if(source == buttonRc){
			resetChannel(currentChannel);
			preview();
		}


	}


	@Override
	public void mouseReleased(MouseEvent e) {
			preview();		
	}
	@Override
    public void mousePressed(MouseEvent e) {}
	@Override
	public void mouseExited(MouseEvent e) {}
	@Override
	public void mouseEntered(MouseEvent e) { }
	@Override
    public void mouseClicked(MouseEvent e) {
	
}  

}


class TransformPLE
{
      private ImagePlus imp;
      private ImageProcessor ip;
      private int width;
      private int height;
      private int[] lut;
      private String title;        
      private int[] lut_red, lut_green, lut_blue;

      public TransformPLE(ImagePlus image, String t, int[] lut_a, int[] lut_r, int[] lut_g, int[] lut_b) 
      {
		imp=image;
        ip=imp.getProcessor();
        width=ip.getWidth();
        height=ip.getHeight();
		title = t;
	   	lut = lut_a;
	   	lut_red = lut_r;
	   	lut_green = lut_g;
	   	lut_blue = lut_b;
     }
      
      public void setLUT(int[] l)
      {
          for(int i=0;i<256;i++)
          {
            lut[i]=l[i];
          }
      }
      
      public byte[] applyTransform(byte[] pixels)
      {
          byte[] output=new byte[pixels.length];  
        
          for(int i=0;i<pixels.length;i++)
          {
            output[i]=(byte)lut[pixels[i]&0xff];
          }
          return output;          
      }
      
      public int[] applyTransform(int[] pixels)
      {
          int[] output=new int[pixels.length];  
		int p_red, p_green, p_blue;

          for(int i=0;i<pixels.length;i++)
          {
            int c=pixels[i];
		
            p_red=(c&0xff0000)>>16;
            p_green=(c&0x00ff00)>>8;
            p_blue=(c&0x0000ff);

           	p_red=lut_red[p_red&0xff];
           	p_green=lut_green[p_green&0xff];
            p_blue=lut_blue[p_blue&0xff];

            p_red=lut[p_red&0xff];
           	p_green=lut[p_green&0xff];
            p_blue=lut[p_blue&0xff];

            output[i]=((p_red&0xff)<<16)+((p_green&0xff)<<8)+(p_blue&0xff);
          }
          return output;          
      }
      
      public ImagePlus createProcessed8bitImage()
      {
        ImageProcessor processed_ip = ip.crop();
        ImagePlus processed= imp.createImagePlus();
        processed.setProcessor(title, processed_ip);
        byte[] processed_pixels = (byte[])processed_ip.getPixels();
        byte[] output=applyTransform(processed_pixels);
        for (int i=0; i<output.length; i++) 
        {
          processed_pixels[i]=output[i];
        }
        //processed.show();
        return processed;
      }
      
      public void createProcessed8bitImage(ImagePlus img)
      {
        byte[] processed_pixels = (byte[])(img.getProcessor()).getPixels();
        byte[] output=applyTransform(processed_pixels);
        for (int i=0; i<output.length; i++) 
        {
          processed_pixels[i]=output[i];
        }
      }
      
      public ImagePlus createProcessedRGBImage()
      {
        ImageProcessor processed_ip = ip.crop();
        ImagePlus processed= imp.createImagePlus();
        processed.setProcessor(title, processed_ip);
        int[] processed_pixels = (int[])processed_ip.getPixels();
        int[] output= applyTransform(processed_pixels);
        for (int i=0; i<output.length; i++) 
        {
          processed_pixels[i]=output[i];
        }
        return processed;
      }
      
      public void createProcessedRGBImage(ImagePlus img)
      {
        int[] processed_pixels = (int[])(img.getProcessor()).getPixels();
        int[] output= applyTransform(processed_pixels);
        for (int i=0; i<output.length; i++) 
        {
          processed_pixels[i]=output[i];
        }
      }
}



@SuppressWarnings( "unchecked" )
class PlotPLE extends Canvas implements MouseMotionListener, MouseListener
{
	
	private static final int WIDTH=255, HEIGHT=255;
	private static final int X1 = 0, X2 = 255;
	private double min = 0;
	private double max = 255;
	private Label labMouse;
    private int[] y_values=new int[256];
	private int currentChannel = 0;
    private int r1, r2, s1, s2;

        
	public PlotPLE() 
        {
		  r1 =  X1;
		  r2 = X2;
          addMouseMotionListener(this);
          addMouseListener(this);
          setSize(WIDTH, HEIGHT);
		}
        
        

        
        public void setLabelMouse(Label l)
        {
          labMouse=l;  
        }

        public Dimension getPreferredSize() 
        {
          return new Dimension(WIDTH+1, HEIGHT+1);
        }

	public void update(Graphics g) 
        {
		paint(g);
	}

	public void paint(Graphics g) 
        {
          g.setColor( Color.gray);
          g.fillRect(0, 0, WIDTH, HEIGHT);
          g.drawRect(0, 0, WIDTH, HEIGHT);
          g.setColor( new Color(140,140,140));
	      g.drawLine(128,0,128,255);
	      g.drawLine(64,0,64,255);
	      g.drawLine(192,0,192,255);
	      g.drawLine(0,64,255,64);
	      g.drawLine(0,128,255,128);
	      g.drawLine(0,192,255,192);
          g.setColor( Color.white );
          Vector v=new Vector();
          for(int i=0;i<255;i++)
          {
           int[] first_couple=coordsConverter(i,y_values[i]);
           int[] second_couple=coordsConverter((i+1),y_values[i+1]);
           g.drawLine(first_couple[0], first_couple[1], first_couple[0], first_couple[1]);
           g.drawLine(second_couple[0], second_couple[1], second_couple[0], second_couple[1]);
           approximation(g, second_couple[0], second_couple[1], first_couple[0], first_couple[1],v);
          }

          int[] first_couple=coordsConverter(r1,s1);
          int[] second_couple=coordsConverter(r2,s2);
          Graphics2D g2=(Graphics2D)g;
          g=(Graphics)g2;
          g.setColor(Color.white);
        }
        
        public void approximation(Graphics g, int succ_x, int succ_y, int prev_x, int prev_y, Vector v)
        {
          double hypo=Math.sqrt(Math.pow((prev_x-succ_x),2)+Math.pow((prev_y-succ_y),2));
          if(hypo > 1D)
          {
            int middle_x=(prev_x+succ_x)/2;
            int middle_y=(prev_y+succ_y)/2;
            approximation(g, succ_x, succ_y, middle_x, middle_y, v);
            approximation(g, prev_x, prev_y, middle_x, middle_y, v);
            g.drawLine(middle_x, middle_y, middle_x, middle_y);
            v.addElement(new Point(middle_x,middle_y));
          }
        }
        
        private int[] coordsConverter(int x, int y)
        {  
          int[] converted_coord=new int[2];
          converted_coord[0]=x;
          converted_coord[1]=255-y;
          return converted_coord;
        }
        
                
        public int[] findLUTT(TonoPLE t)
        {
          int[] y_axis=new int[256]; 
		double altL = 255;
		double altS = 255;
		double gamma = 1;
		double bas = ((64*t.s_ombre)/100);
		double alt = ((64*t.s_alte)/100);
		double scale = 1+((1F*t.s_alte)/500);
		int shift = 0;
		if(bas < 0) shift = (int)bas*-1;
		for(int i=0;i<256;i++){
            		y_axis[i]=0;
          		}

			if(t.s_mezzi < 0)
				gamma = ((5F*(-20+t.s_mezzi))/100)*-1D;
			else
				gamma = ((1F*(100 - t.s_mezzi))/100);

				if(t.s_ombre < 0 && t.s_mezzi < 0 )
				bas = 0;


			if(t.s_alte > 0) {
				if(t.s_ombre < 0){
					altL =255 - bas ;
					altS = 255 - alt - shift;
				} else{
					altL = 255 - bas;
					altS = 255 - alt - shift;
				}

			scale = 1;

			} else  {
				if(t.s_ombre < 0){
					altL = 255 - bas;
					altS = 255-shift;
				} else{
					altL = 255 - bas;
					altS = 255 - shift ;
				}
			}
		


			 for(int i=0;i<256-shift;i++){
          			int processed = (int)(Math.pow((float)(i / altS*1F), gamma) * altL*1D);
            		processed=(int)((processed*(scale))+bas);
            		if (processed > 255) processed = 255;
            		if (processed < 0) processed =0;
            		y_axis[i+shift]=processed;
          		}
			setYValues(y_axis);
			repaint();
			return y_axis;


		        }



                

	public void setYValues(int[] values)
        {
          for(int i=0;i<256;i++)
          {
            y_values[i]=values[i];

          }
        }

		
	
    

        public void mouseMoved(MouseEvent e)
        {
          int x=e.getX();
          int y=e.getY();
          int[] cartes_coord=coordsConverter(x,y);
          labMouse.setText("("+cartes_coord[0]+" , "+cartes_coord[1]+")");
        }
        
        public void mouseDragged(MouseEvent e)
        {
          int x=e.getX();
          int y=e.getY();
          int[] cartes_coord=coordsConverter(x,y);
		labMouse.setText("("+cartes_coord[0]+" , "+cartes_coord[1]+")");
        }

    public void mousePressed(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}
    public void mouseClicked(MouseEvent e) {}          
}


class Punto {
	public int x, y;

	public Punto(int x, int y){
		this.x = x;
		this.y = y;
	}

}

class TonoPLE {
	public Punto p_ombre, p_mezzi, p_alte;
	public int s_ombre, s_mezzi, s_alte;

	public  TonoPLE(){
		p_ombre = new Punto(0,0);
		p_mezzi = new Punto(0,0);
		p_alte = new Punto(0,0);
		s_ombre = 0;
		s_mezzi = 0;
		s_alte = 0;
	}

}

	class J2Html {
		public void changeTextToHtml(String inputTxtFile){
			String [] temp = inputTxtFile.split(".");
			String s,formName = "testForm";
			try{
				FileWriter htmlFile = new FileWriter("report.htm");
				PrintWriter htmlPrintWriter = new PrintWriter(htmlFile);
				htmlPrintWriter.println("<html>");
				htmlPrintWriter.println("<head>");
				htmlPrintWriter.println("<script>");
				
				htmlPrintWriter.println("</script>");
				htmlPrintWriter.println("</head>");
				htmlPrintWriter.println("<form name ="+formName+" >");
				htmlPrintWriter.println("<body>");
				
				htmlPrintWriter.println(inputTxtFile);
				htmlPrintWriter.println("</body>");
				htmlPrintWriter.println("</form>");
				htmlPrintWriter.println("</html>");
				
				htmlPrintWriter.close();
				
				
			}
			catch(Exception e){e.printStackTrace();}
			
		}
		
	}
