package opartgui;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.image.BufferedImage;
import com.sun.image.codec.jpeg.*;


public class ImagePanel extends JPanel {
   protected BufferedImage displayImage;
   protected Graphics2D dispGc;
   protected boolean invert = false;
   protected boolean flickeringInvert = false;
   protected boolean inverted = false;
   protected boolean clipped = false;
   protected boolean transformApplied = false;


   protected AffineTransform atx = new AffineTransform();
   protected double rotationAngle = 0.0;
   protected boolean rotateOn = true;
   protected int rotationCenterX = 0;
   protected int rotationCenterY = 0;

   protected boolean shearOn = true;
   protected double shearFactor = 0.0;
   protected double shearX =0.0, shearY=0.0;
   int w = 600;
   int h = 600;

   public ImagePanel(){
       setBackground(Color.white);
   }

   public AffineTransform getTransform(){ return atx; }
   public void setTransform(AffineTransform at){ atx = at; }

   protected void applyTransform(AffineTransform atx){
      if(displayImage == null) createDisplayImage();
      BufferedImage bi = displayImage;
      createDisplayImage();
      dispGc.drawImage(bi,atx,this);
      resetManipulation();
      repaint();
   }

   public void rotate(double theta){
      double ang = this.rotationAngle -theta;
      setRotationAngle(theta);
      atx = getTransform();
      atx.rotate(ang, w/2, h/2);
      applyTransform(atx);
   }

   public void setRotationAngle(double rotationAngle){
      this.rotationAngle = rotationAngle;
   }

   public void rotate(double theta, int rotCenterX, int rotCenterY){
      double ang = this.rotationAngle -theta;
      setRotationAngle(theta);
      atx = getTransform();
      atx.rotate(ang, rotCenterX, rotCenterY);
      applyTransform(atx);
   }
   
   public void resetAndRotate(double theta) {
      setRotationAngle(theta);
      atx.setToRotation(theta, w/2, h/2);
      applyTransform(atx);
   }

   public void shear(double shx, double shy){
      double shxIncr = shearX -shx;
      double shyIncr = shearY -shy;
      setShearFactor(shx);
      this.shearX = shx;
      this.shearY = shy;
      atx = getTransform();
      atx.shear(shxIncr, shyIncr);
      applyTransform(atx);
   }

   public void shearIncr(double shxIncr, double shyIncr){
      shearX += shxIncr;
      shearY += shyIncr;
      setShearFactor(shearX);
      atx.shear(shxIncr, shyIncr);
      applyTransform(atx);
   }

   public void setShearFactor(double shearFactor){
      this.shearFactor = shearFactor;
   }

   public void resetAndShear(double shx, double shy){
      shearX =shx; shearY = shy;
      atx.setToShear(shx,shy);
      setShearFactor(shearX);
      applyTransform(atx);
   }

   public void resetManipulation(){
      shearX = 0.0; shearY = 0.0;
      rotationAngle = 0.0;
      atx = new AffineTransform();
   }

   public void doDisplay(BufferedImage bi){
       displayImage = bi;
       repaint();
   }

   protected void createDisplayImage(){
        Dimension dim = getSize();
        w = dim.width;
        h = dim.height;
        displayImage = new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
        setOffScrGc();
   }

   protected void setOffScrGc(){
        if (displayImage != null){
             dispGc = displayImage.createGraphics();
             dispGc.setColor(getBackground());
             dispGc.fillRect(0,0,w,h);
             dispGc.setColor(getForeground());
             dispGc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        }
   }


   public void invert(){
         transformApplied=false;
         if(invert){
                invert = false;
         }
         else{
             	invert = true;
         }
         repaint();

   }

   public void setClipped(){
         transformApplied=false;
         if(clipped){
                clipped=false;
         }
         else{
             	clipped = true;
         }
         repaint();

   }

   public void flickeringInvert(){
         transformApplied=false;
         if(flickeringInvert){
                flickeringInvert=false;
         }
         else{
             	flickeringInvert = true;
         }
         repaint();

   }

   public void doFlickeringInvert(){
       BufferedImage bi = null;
       byte lut[] = new byte[256];
       for (int j = 0; j<256;j++) lut[j] = (byte)(256-j);
       LookupOp lookupop = new LookupOp(new ByteLookupTable(0,lut),null);
       bi = lookupop.filter (displayImage, bi);
       createDisplayImage();
       dispGc.drawImage(bi,0,0,this);
       repaint();
   }

   public void doInvert(){
       BufferedImage bi = null;
       byte lut[] = new byte[256];
       for (int j = 0; j<256;j++) lut[j] = (byte)(256-j);
       LookupOp lookupop = new LookupOp(new ByteLookupTable(0,lut),null);
       bi = lookupop.filter (displayImage, bi);

       //else bi = displayImage;
       if(inverted==false){
            inverted = true;
       }
       else{
            inverted = false;
       }
       createDisplayImage();
       dispGc.drawImage(bi,0,0,this);

   }


   public void paintComponent(Graphics gc){
       super.paintComponent(gc);
       Graphics2D g = (Graphics2D)gc;
       if (displayImage != null){
          if(invert&&flickeringInvert==false&&inverted==false)doInvert();
          if(inverted && !invert)doInvert();
          if(clipped){
              	Rectangle2D rect = new Rectangle2D.Float();
                rect.setRect(10, 10, 580, 580);
                g.setClip(rect);

          }
          g.drawImage(displayImage, 0 ,0, this);
          if(flickeringInvert)doFlickeringInvert();

       }
       else createDisplayImage();
   }

}
