/*
 *  @# ImageViewer.java 1.0 99/8/20
 *  Copyright (c) 1999 Lawrence Rodrigues
 */

import java.io.*;
import java.awt.*;
import java.awt.image.*;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.*;

/** ImageViewer Class.
  * @version 1.0  18 sep 1999
  * @author Lawrence Rodrigues
  **/

public class ImageViewer implements Runnable,
                         DisplayController, ImageLoadedListener {

    static int MAX_IMAGES = 6;
    static int MAX_CINE_IMAGES = 10;

    static int MAX_LOAD_IMAGES = 4;
    static int MAX_IMAGECANVAS = 4;

    static final int RED =  1;
    static final int GREEN =  2;
    static final int BLUE = 3;
    static final int ALPHA = 4;
    static int vpWid = 400;
    static int vpHt = 400;
    static int cpWid = 256;
    static int cpHt  = vpHt;
    static int viewerWid = vpWid+cpWid +30;
    static int viewerHt = vpHt+95;

    public AppFrame viewerPanel;
    public CommandPanel commandPanel;
    Panel canvasPanel = new Panel();

    private URL dir = null;
    private Thread mainThread;
    private ImageLoader  scnImage[] = new ImageLoader[MAX_CINE_IMAGES];
    private ImageBuffer curDispImage;
    private ImageCanvas Screen[] = new ImageCanvas[MAX_IMAGECANVAS];
    private ImageCanvas curScreen;

    private String[] fileList;
    private int numImages=0;
    private Hashtable imageTable = new Hashtable();
    private int curVpNum =0;
    private int curImageNum =0;
    private boolean grabPixelsOn = false;

    private ImageCanvas cineVp;
    private int cinePortNum = 0;
    private int cineIndex =0;
    private boolean notAllLoaded = false;

    public static void main(String argv[]){
     ImageViewer Viewer = new ImageViewer();
    }
    public ImageViewer(){
       init();
    }

    public void init() {
       String param;
       try{
          dir = new URL("images");
       } catch(MalformedURLException e){}

       File imgDir= new File("images");
       readFiles(imgDir);
       createUI();
       initScreen();
       setCinePort(0);
       mainThread = new Thread(this);
       start();
    }


    private void initScreen(){
          curVpNum = 0;
          curScreen = Screen[curVpNum];
          // Different types of Cursors are  available in Frame Only
          // curScreen.registerForCursorCallback(viewerPanel);
          // Callback for executing display commands.
          commandPanel.registerForDisplayCallback(this);
          curScreen.createOffScrImage();
    }
    /**
      * Creates  user interface.
      **/
    public void createUI(){
       viewerPanel = new AppFrame(viewerWid, viewerHt);
       viewerPanel.setTitle("Image Viewer");
       commandPanel = new CommandPanel(cpWid, cpHt, fileList);
       commandPanel.cinePanel.registerForDisplayCallback(this);
       GridBagLayout    gb = new GridBagLayout() ;
       GridBagConstraints c = new GridBagConstraints();
       viewerPanel.setLayout(gb);
       viewerPanel.setBackground(Color.lightGray);
       viewerPanel.setForeground(Color.black);

       createCanvasPanel();
       c.gridheight = 1; c.weighty = 0.0; c.weightx = 0.0;
       c.insets = new Insets(10,10, 0,10);
       c.gridwidth = 1;

       gb.setConstraints(commandPanel,c);
       viewerPanel.add(commandPanel);
       c.gridwidth = GridBagConstraints.REMAINDER;
       c.insets = new Insets(10,5, 10,10);
       c.gridheight = 1; c.weighty = 0.0; c.weightx = 0.0;
       gb.setConstraints(canvasPanel,c);
       viewerPanel.add(canvasPanel);
       viewerPanel.pack();
       viewerPanel.setSize(viewerWid, viewerHt);
       viewerPanel.show();
    }

    public void createCanvasPanel(){
       canvasPanel.setLayout(new GridLayout(2,2));
       canvasPanel.setBackground(Color.lightGray);
       for(int i=0;i<MAX_IMAGECANVAS;i++){
	     Screen[i] =  new ImageCanvas(vpWid/2, vpHt/2);
           Screen[i].setBackground(Color.black);
           canvasPanel.add(Screen[i]);
       }
    }

    /** Thread starts here
      */
    public void start(){
       mainThread.start();
    }

    /** Run the application now.
      */
    public void run() {
       loadImages();
    }
    public void readFiles(File dir){
       if(!dir.exists()) return;
       ExtensionFilter gifFilter = new ExtensionFilter("gif,jpeg,jpg,JPG,JPEG,GIF");
       fileList = dir.list(gifFilter);
       if(fileList == null) return;
       numImages = fileList.length;
    }

    /** Loads all the images in all the image canvases.
    */
    public void loadImages(){
       for(int i=0;i<MAX_LOAD_IMAGES;i++){
           loadAnImage(i);
       }
       try{
          mainThread.sleep(50);
       }catch(InterruptedException e){}
    }

     /**
      * Loads an image and creates its corresponding screen image.
      */
     private boolean loadAnImage(int num){
        ImageLoader im = new ImageLoader();
        im.addImageLoadedListener(this); 
        imageTable.put(Integer.toString(num), im);
        String path = new String("images");
           im.setFileInfo(path, fileList[num], num);
/*           try{
           im.setUrlInfo(new URL("images/"+fileList[num]), fileList[num], num);
           } catch (MalformedURLException e){} 
*/
        im.load();
        return true;
     }
  
     public void loadPixels(int id){
        ImageLoader im = getImageLoader(id);
        ImageStats st = new ImageStats();
        st.loadPixels(im);
     }


     public void loadPixels(){
        for(int i=0;i<MAX_LOAD_IMAGES;i++){
            loadPixels(i);
         }
      }

     public void imageLoaded(ImageLoadedEvent e){
        int status = e.getLoadStatus();
        ImageLoader il = (ImageLoader)e.getSource();

        ImageBuffer img = (ImageBuffer)il.getImageBuffer();
        if((status & ImageLoadedListener.IMAGE_READY) != 0){
           if((img.imageId) >=0 && (img.imageId) < numImages){
               displayImage(img);
               il.removeImageLoadedListener(this);
           }
        }
    }

    public ImageLoader getImageLoader(int id) {
       ImageLoader il = (ImageLoader)imageTable.get(Integer.toString(id));
       if(il == null){
          il = new ImageLoader();
          imageTable.put(Integer.toString(id), il);
          String path = new String("images");
          il.setFileInfo(path, fileList[id], id);
          il.load();
        }
       return il;
    }

     /** Displays an image
       * Before displaying, it checks the loading status of that image.
       * If the image is not ready, it will execute the loadImage method
       * ***/
     public void displayImage(int num){
        if(num <0 || num >= numImages){
             System.out.println(" Image does not exist ");
             return;
        }
        ImageLoader img = (ImageLoader)imageTable.get(Integer.toString(num));
        if(img == null){
           if(loadAnImage(num)){
              img = (ImageLoader)imageTable.get(Integer.toString(num));
           }
         } else {
           displayImage(img.getImageBuffer());
         }
    }

    public synchronized void displayImage(ImageBuffer img) {
       curDispImage = img; 
       curScreen = Screen[(curVpNum++)%MAX_IMAGECANVAS];
       curScreen.setImageBuffer(curDispImage);
       curScreen.imagePaint();
       curImageNum = img.imageId;
    }

    /** Clean up and quit
     **/
    public void quit(){
       viewerPanel.dispose();
    }

    public int getCinePortNum(){
       return cinePortNum;
    }
  
    public void setCinePort(int portNum){
       cineVp = Screen[portNum];
       cinePortNum = portNum;
    }


    public void cineDispImage(){
       if(cineVp == null) return;
       if(!notAllLoaded){
          if(scnImage[cineIndex] == null){
             ImageLoader im = new ImageLoader();
             URL url = null;
             String path = new String("images");
             im.setFileInfo(path, fileList[cineIndex], cineIndex);
             im.load();
             scnImage[cineIndex] = im;
             scnImage[cineIndex].prepareImage();
             cineIndex++;
             if((cineIndex % MAX_CINE_IMAGES) ==0) cineIndex =0;
          } else {
             if(checkAllLoaded()) notAllLoaded = true;
             if((cineIndex % MAX_CINE_IMAGES) ==0) cineIndex =0;
          }
       }else{
          if((scnImage[cineIndex].loadStatus & (ImageLoadedListener.ERRORED |ImageLoadedListener.ABORTED)) ==0){
            cineVp.cineDraw(scnImage[cineIndex].getImageBuffer());  
          }
          cineIndex++;
          if((cineIndex % MAX_CINE_IMAGES) ==0) cineIndex =0;
       }
    }
 
    public boolean checkAllLoaded(){
       int status = ImageLoadedListener.IMAGE_READY;
       for(int i=0;i<MAX_CINE_IMAGES;i++){
           if((scnImage[i].loadStatus & (ImageLoadedListener.ERRORED |ImageLoadedListener.ABORTED)) ==0){
              status &= scnImage[i].loadStatus & ImageLoadedListener.IMAGE_READY;
           }
       }
       if((status & ImageLoadedListener.IMAGE_READY) != 0)return true;
       return false;
   }

   public void cineReset(){
      notAllLoaded = false;
      if(curScreen != null)curScreen.resetDisplay();
   }

   class  ImageStats implements ImageLoadedListener{
    
     public void loadPixels(ImageLoader il){
        il.addImageLoadedListener(this); 
        try{
           mainThread.sleep(100);
        } catch(InterruptedException e){}
        il.fetchPixels();
     }

        
     public void imageLoaded(ImageLoadedEvent e){
        int status = e.getLoadStatus();
        ImageLoader il = (ImageLoader)e.getSource();
        ImageBuffer img = (ImageBuffer)il.getImageBuffer();
          if((status & ImageLoadedListener.PIXELS_AVAILABLE) != 0){
             if((img.imageId) >=0 && (img.imageId) < numImages){
               generateAndDisplayStats(img);
             }
          il.removeImageLoadedListener(this);
        }
    }

    private void generateAndDisplayStats(ImageBuffer img){
        String fname = fileList[img.imageId];
        commandPanel.statsPanel.imgname.setText(fname);
        int[] stats = computeStats(img.origPixels,RED);              
        commandPanel.statsPanel.redmin.setText(Integer.toString(stats[0]));
        commandPanel.statsPanel.redmax.setText(Integer.toString(stats[1]));
        commandPanel.statsPanel.redave.setText(Integer.toString(stats[2]));

        stats = computeStats(img.origPixels,GREEN);              
        commandPanel.statsPanel.greenmin.setText(Integer.toString(stats[0]));
        commandPanel.statsPanel.greenmax.setText(Integer.toString(stats[1]));
        commandPanel.statsPanel.greenave.setText(Integer.toString(stats[2]));

        stats = computeStats(img.origPixels,BLUE);              
        commandPanel.statsPanel.bluemin.setText(Integer.toString(stats[0]));
        commandPanel.statsPanel.bluemax.setText(Integer.toString(stats[1]));
        commandPanel.statsPanel.blueave.setText(Integer.toString(stats[2]));
    }

    public int[] computeStats(int inp[], int clr){
       if(inp == null) return null;
       int pix;
       int stats[] =  new int[3];
       int max = Integer.MIN_VALUE;
       int min = Integer.MAX_VALUE;
       ColorModel cm = ColorModel.getRGBdefault();
       int sum =0;
       for(int i=0; i<inp.length;i++){
           switch(clr){
               case RED:
                  pix = cm.getRed(inp[i]);
                  break;
               case GREEN:
                  pix = cm.getGreen(inp[i]);
                  break;
               case BLUE:
                  pix = cm.getBlue(inp[i]);
                  break;
               case ALPHA:
                  pix = cm.getAlpha(inp[i]);
                  break;
               default:
                  pix =0;
           } 
           if(pix > max) max = pix;
           if(pix < min ) min = pix;
           sum += pix;  
       }
       double average = sum/inp.length;
       stats[0] = min; stats[1] = max; stats[2] = (int)average;
       return stats;
    }
  }
  class  ExtensionFilter implements FilenameFilter{
      private String extString;
      public ExtensionFilter(String ext){
         extString = ext;
      }
      public boolean accept(File dir, String name){
         StringTokenizer st = new StringTokenizer(extString, ",");
         while(st.hasMoreTokens()){
             if(name.endsWith(st.nextToken())){
                return true;
             }
          }
         return false;
      }
    }

}
