/*
 *  @# 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.*;
import java.applet.*;
import java.util.zip.*;

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

public class ImageViewerApplet extends Applet implements Runnable,
                         DisplayController {
    static int MAX_IMAGES = 6,MAX_CINE_IMAGES = 10 ;
    static int MAX_LOAD_IMAGES = 4, MAX_IMAGECANVAS = 4;
    static final int RED =  1, GREEN =  2, BLUE = 3, ALPHA = 4;
    static final int JAR = 1, LOCAL_DIR = 2;
    static int vpWid = 400, vpHt = 400;
    static int cpWid = 256, cpHt  = vpHt;
    static int viewerWid = vpWid+cpWid +30;
    static int viewerHt = vpHt+95;

    private AppFrame viewerPanel;
    private CommandPanel commandPanel;
    private 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 int numImages=0;

    private int curVpNum =0, curImageNum =0;
    private boolean grabPixelsOn = false;

    private ImageCanvas cineVp;
    private int cinePortNum = 0, cineIndex =0, numCineImages =0;
    private boolean notAllLoaded = false;
    private int appWid, appHt;
    private String[] fileList;
    protected int loadMethod = JAR;
    private String path = "images/";
    private Loader loader;
    private Hashtable imageTable = new Hashtable();

    public void init() {
       String param = null;
       URL url = null;
       try {
	        param = getParameter("IMAGENAMES");
          getFilesFromHTML(param);
          param = getParameter("IMAGESOURCE");
          dir = (param == null) ? getDocumentBase() : new URL(getDocumentBase(), param );
          param = getParameter("WIDTH");
          appWid = Integer.valueOf(param).intValue();
          param = getParameter("HEIGHT");
          appHt = Integer.valueOf(param).intValue();
          initApp();
       } catch (MalformedURLException e) {}
    }


    protected void getFilesFromHTML(String param){
       StringTokenizer st = new StringTokenizer(param, ",");
       int size = st.countTokens();
       if(size <=0) return;
       fileList = new String[size];
       int i=0;
       while(st.hasMoreTokens()) fileList[i++] = st.nextToken();
       numImages = fileList.length;
    }

     protected void initApp(){
       createUI();
       initScreen();
       setCinePort(1);
       loader = new Loader();
       numCineImages = (numImages > MAX_CINE_IMAGES) ? MAX_CINE_IMAGES: numImages;
       mainThread = new Thread(this);
    }

    private void initScreen(){
       curVpNum = 0;
       curScreen = Screen[curVpNum];
       curScreen.createOffScrImage();
    }

    public void createUI(){
       viewerPanel = new AppFrame(viewerWid, viewerHt);
       viewerPanel.setTitle("Image Viewer");
       commandPanel = new CommandPanel(cpWid, cpHt, fileList);
       commandPanel.registerForDisplayCallback(this);
       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]);
       }
    }

    public void start(){ mainThread.start();}

    public void run() { loader.loadImages(); }

    public void quit(){ viewerPanel.dispose();}

    public void displayImage(int num){
       if(num <0 || num >= numImages){
          return;
        }
       ImageLoader img = (ImageLoader)imageTable.get(Integer.toString(num));
       if(img == null){
          if(loader.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;
    }

    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();
             if(loadMethod == LOCAL_DIR){
                URL url = null;
                im.setFileInfo(path, fileList[cineIndex], cineIndex);
                im.load();
             } else {
                 im.jarLoad(fileList[cineIndex], cineIndex);
             }
             scnImage[cineIndex] = im;
             scnImage[cineIndex].prepareImage();
             cineIndex++;
             if((cineIndex % numCineImages) ==0) cineIndex =0;
          } else {
             if(loader.checkAllLoaded()) notAllLoaded = true;
             if((cineIndex % numCineImages) ==0) cineIndex =0;
          }
       }else{
          if((scnImage[cineIndex].loadStatus & (ImageLoadedListener.ERRORED |ImageLoadedListener.ABORTED)) ==0){
            cineVp.cineDraw(scnImage[cineIndex].getImageBuffer());
          }
          cineIndex++;
          if((cineIndex % numCineImages) ==0) cineIndex =0;
       }
    }

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

    public void loadPixels(int id){
       ImageLoader im = loader.getImageLoader(id);
       ImageStats st = new ImageStats();
       st.loadPixels(im);
    }

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

    public static void main(String argv[]){
       ImageViewerApplet viewer = new ImageViewerApplet();
       if(argv.length >=1) {
          viewer.initApplication(argv[0]);
       } else {
         viewer.initApplication();
       }
    }

    public void initApplication(String jarfile){
       loader = new Loader();
       if(jarfile != null) loader.listFilesInJar(jarfile);
       if(fileList == null) initApplication();
       else {
         initApp();
         loader.loadImages();
       }
    }

    public void initApplication(){
       loader = new Loader();
       loadMethod = LOCAL_DIR;
       path = new String("images/");
       File imgDir= new File(path);
       loader.readFiles(imgDir);
       initApp();
       loader.loadImages();
   }


class Loader implements ImageLoadedListener{

    public void loadImages(){
       for(int i=0;i<MAX_LOAD_IMAGES;i++) loadAnImage(i);
       try{
          mainThread.sleep(50);
       }catch(InterruptedException e){}
    }

    private boolean loadAnImage(int num){
       ImageLoader im = new ImageLoader();
       im.addImageLoadedListener(this);
       imageTable.put(Integer.toString(num), im);

       for(int i=0;i<fileList.length;i++){
            //System.out.println(fileList[i]);
       }
       if(loadMethod == JAR) im.jarLoad(fileList[num],num);
       if(loadMethod == LOCAL_DIR) {
          im.setFileInfo(path, fileList[num], num);
          im.load();
       }
       return true;
    }

    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) return il;
       il = new ImageLoader();
       imageTable.put(Integer.toString(id), il);
       if(loadMethod == LOCAL_DIR){
          il.setFileInfo(path, fileList[id], id);
          il.load();
       } else {
          il.jarLoad(fileList[id],id);
       }
       return il;
    }

    public boolean checkAllLoaded(){
       int status = ImageLoadedListener.IMAGE_READY;
       for(int i=0;i<numCineImages;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 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;
    }

    public void listFilesInJar(URL url){ listFilesInJar(url.getFile());}

    public void listFilesInJar(String file){
       try {
       ZipFile zipf = new ZipFile(new File(file));
       String filter[] = {"jpeg", "jpg", "JPEG", "JPG", "gif", "GIF"};
       fileList = listJarEntries(zipf, filter);
       numImages = fileList.length;
       System.out.println(numImages);
       } catch (Exception e) {
         System.out.println(e);
       }
   }
     public String[] listJarEntries(ZipFile jarfile, String[] filter){
       String fileList[] = null;
       try{
            Vector list = new Vector();
            for(Enumeration e = jarfile.entries(); e.hasMoreElements();){
                String name = ((ZipEntry)e.nextElement()).getName();
                if(filterFileNames(name,filter))
                   list.addElement(name);
            }
            fileList = new String[list.size()];
            list.copyInto(fileList);
        } catch (Exception e){
           System.out.println("Jar file reading exception "+ e);}
        return fileList;
    }

    public boolean filterFileNames(String name, String filter[]){
       if(filter == null) return true;
        for(int i=0; i< filter.length;i++){
            if(name.endsWith(filter[i])) return true;
       }
       return false;
    }
 }
   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;
      }
  }
}
