/////////////////////////////////////////////////////////
// Copyright 1997 A.A. Butterfield (DCompose)
/////////////////////////////////////////////////////////
package com.dcompose.slideshow;

import java.util.*;
import java.net.*;
import java.awt.*;
import java.applet.*;

/*************************************************
* The Slideshow applet is designed to display photographs
* loaded over the internet. It has the following advantages of
* a regular HTML solution <UL>
* <LI> An album can be established very quickly with many pages
* <LI> Titles can overlayed on the picture
* <LI> Index is build automatically for random access
* <LI> Slideshow mode allows images displayed as fast as they cam be loaded
* <LI> Screen isn't blank whilst next image is loading
* </UL>
* Images are loaded from /images directory. Three button
* graphic files are needed but1.gif, but2.gif, but3.gif, sound files
* of no.au, slide.au.
* Applet arguments of <UL>
* <LI> Images list of ; separated image files
* <LI> Titles list of ; separated titles corresponding to files
* </UL>
*************************************************/
public class Slideshow extends Applet implements Runnable
{
   /** image directory **/
   final String mDir="images/";
   /** x coordinate of toolbar **/
   final int mToolbarX=20;
   /** y coordinate of toolbar **/
   final int mToolbarY=20;
   /** *maximum speed of slideshow (ms) */
   final int mDelay=6000;
   /** width of toolbar **/
   final int mToolbarWidth=6*40;
   /** height of toolbar **/
   final int mToolbarHeight=40;
   /** garbage collect after this many images **/
   final int mGCEvery=5;

   private Font mFont;
   private int mGCCount;
   private boolean mReady;
   private Vector mImages;
   private Vector mTitles;
	private int mIndex=0;
	private int mActual=-1;
	private int mLast=-1;
   private Image mCurrent;
   private int mWidth;
   private int mHeight;
   private Frame mFrame;
   private boolean mToolbarVisible;
   private boolean mSlideshow;
   private boolean mBusy;
   private WaitThread mThread;
   private Thread mControlThread;
   private int mPressed=-1;
	private Toolbar mToolbar;
	private Choice mChoice;
   private AudioClip mNo,mSlide;
   private int mTitleY;
   private Button mGoButton, mCancelButton;
	private int mOldIndex;

   /*************************************************
   * initialise the applet
   *************************************************/
   public void init()
   {
      super.init();
      setBackground(Color.black);
   }

   public void start()
   {
      mControlThread = new Thread(this);
      mControlThread.start();
   }
   public void stop()
   {
      mControlThread.stop();
   }


   /*************************************************
   * @return number of images
   *************************************************/
	public int getImages()
	{
		return mImages.size();
	}

   /*************************************************
   * paint the image and title
   * @see java.awt.Component
   *************************************************/
   public void paint(Graphics g)
   {
      if (!mReady)
      {
         g.setColor(Color.white);
         g.drawString("Loading...",mToolbarX,g.getFontMetrics().getAscent()+mToolbarY);

      }
      else
      {
      	int w=mCurrent.getWidth(null);
      	int h=mCurrent.getHeight(null);

         g.drawImage(mCurrent,(mWidth-w)/2,(mHeight-h)/2,this);


         g.setFont(mFont);
         if (mActual!=-1)
         {
            String title=(String)mTitles.elementAt(mActual);
            int x=(size().width-g.getFontMetrics().stringWidth(title))/2;

            g.setColor(Color.black);
            g.drawString(title,x-1,mTitleY-1);
            g.drawString(title,x-1,mTitleY+1);
            g.drawString(title,x+1,mTitleY-1);
            g.drawString(title,x+1,mTitleY+1);
            g.setColor(Color.white);
            g.drawString(title,x,mTitleY);
         }
         else
         {
            mGoButton.show();
            mCancelButton.show();
         }

      }
   }

   /*************************************************
   * special handling of index of -1 to load credits image
   * @param aIndex the index of image to return
   * @return the relative URL string for the image
   *************************************************/
   private String imageFor(int aIndex)
   {
      String ret=null;
      if (aIndex==-1)
      {
         ret=mDir+"Title.gif";
      }
      else
      {
         ret=mDir+(String)mImages.elementAt(aIndex);
      }
      return ret;
   }

   /*************************************************
   * run all the functionality of the applet
   *************************************************/
   public void run()
   {
      mImages=split("Images");
      mTitles=split("Titles");

		mFont=new Font("Helvetica",Font.PLAIN,20);

		Component c=this;
		while (! (c instanceof Frame))
		{
			c=c.getParent();
		}
		mFrame=(Frame)c;

		mNo=getAudioClip(getDocumentBase(),"no.au");
		mSlide=getAudioClip(getDocumentBase(),"slide.au");

		mGCCount=mGCEvery;

		Dimension d=size();
		mWidth=d.width;
		mHeight=d.height;

      mTitleY=mHeight-40;

      setLayout(null);
		mToolbar=new Toolbar(this);
		add(mToolbar);
		mToolbar.reshape(mToolbarX,mToolbarY,mToolbarWidth,mToolbarHeight);
		mToolbarVisible=true;

		mChoice=new Choice();
		mChoice.setBackground(Color.black);
		mChoice.setForeground(Color.white);
		for(Enumeration e=mTitles.elements(); e.hasMoreElements();)
		{
			String t=(String)e.nextElement();
			mChoice.addItem(t);
		}
		add(mChoice);
		mChoice.reshape(mToolbarX,mToolbarY+mToolbarHeight,mToolbarWidth,mToolbarHeight);


		String file;
		LoadThread t;
		boolean flag=false;
      while (true)
      {
      	//load current picture and wait for it
      	if (mLast!=mIndex)
      	{
				mChoice.select(mIndex);

      		file=imageFor(mIndex);
				t=new LoadThread(this,file);
				setBusy(true);
				boolean isAlive=true;
				while (isAlive)
				{
				   try
				   {
				      t.join(1000);
				   }
				   catch(Exception e) { System.out.println(e); }
				   if (!t.isAlive())
				   {  isAlive=false;
				   }
			   }

	      	mReady=true;
				setBusy(false);
		      mCurrent=t.getImage();
		      mLast=mIndex;
		      mActual=mIndex;
        	   mSlide.play();
	      	repaint();
	      	flag=false;

	      	// do GC if necessary
	      	if (--mGCCount==0)
	      	{
	      		Runtime r=Runtime.getRuntime();
	      		//System.out.println(r.freeMemory());
	      		r.gc();
	      		//System.out.println(r.freeMemory());
	      		mGCCount=mGCEvery;
	      	}

			}
      	//pre-load next picture if possible
			int i=mIndex+1;
			if (i<mImages.size() && !flag)
			{
	   		file=imageFor(i);
				t=new LoadThread(this,file);
				flag=true;
			}

      	// wait a bit before going round again
			try
			{ Thread.currentThread().sleep(200);} catch(Exception e) { System.out.println(e); }

			if (mSlideshow)
			{
				if (mThread==null)
				{
					mThread=new WaitThread(mDelay);
				}
				else
				{
					if (!mThread.isAlive())
					{
						mThread=null;
						mIndex++;
						if (mIndex>=mImages.size())
						{	mIndex=mImages.size()-1;
							mSlideshow=false;
							mToolbarVisible=true;
            			mToolbar.show();
            			mChoice.show();
							repaint();
               	   mNo.play();
						}
					}
				}
			}
		}
   }

   /*************************************************
   * split out the text using ; separator
   * @param aText input string
   * @return a vector of strings
   *************************************************/
   Vector split(String aText)
   {
    String s=getParameter(aText);
    Vector v=new Vector();
      for (int i=0; i<s.length(); )
      {
         int j=s.indexOf(";",i);
         if (j<0)
         {
            j=s.length();
         }
         String found=s.substring(i,j);
         v.addElement(found);
         i=j+1;
      }
      return v;
   }

   /*************************************************
   * handle mouse press to restore toolbar
   *************************************************/
   public boolean handleEvent(Event event)
   {
   	if (!mBusy)
   	{
	   	if (event.id==Event.MOUSE_DOWN && !mToolbarVisible && mIndex!=-1)
	   	{
				mSlideshow=false;
				restoreToolbar();
			}
			//System.out.println(event);
			if (event.id==Event.ACTION_EVENT && event.target==mChoice)
			{
				String x=(String)event.arg;
				//System.out.println("Action"+x);
				int i=0;
				for(Enumeration e=mTitles.elements(); e.hasMoreElements();)
				{
					String t=(String)e.nextElement();
					if (t.equals(x))
					{
						mIndex=i;
						break;
					}
					i++;
				}
			}
			if (mGoButton!=null && event.id==Event.ACTION_EVENT && event.target==mGoButton)
			{
			   visitDcompose();
			}
			if (mCancelButton!=null && event.id==Event.ACTION_EVENT && event.target==mCancelButton)
			{
			   endCredits();
			}
		}
		return false;
	}

   /*************************************************
   * move to first image
   *************************************************/
	public void first()
	{
		if (mIndex!=0)
		{
			mIndex=0;
			//repaint();
		}
	}

   /*************************************************
   * more to previous image
   *************************************************/
	public void previous()
	{
		if (mIndex>0)
		{
			mIndex--;
		}
		else
   	   mNo.play();
	}

   /*************************************************
   * more to next image
   *************************************************/
	public void next()
	{
		if (mIndex<getImages()-1)
		{
			mIndex++;
		}
		else
   	   mNo.play();
	}

   /*************************************************
   * start slideshow
   *************************************************/
	public void slideshow()
	{
		mSlideshow=true;
		minimize();
	}

   /*************************************************
   * show credits
   *************************************************/
	public void list()
	{
	   int Off=5;
      mGoButton=new Button("Visit Dcompose Homepage");
      mGoButton.hide();
      mCancelButton=new Button("Cancel");
      mCancelButton.hide();
      mOldIndex=mIndex;
      mIndex=-1;
      add(mGoButton);
      add(mCancelButton);
      Dimension d1=mGoButton.preferredSize();
      Dimension d2=mCancelButton.preferredSize();
      int x=(mWidth-d1.width-d2.width-Off)/2;
      mGoButton.reshape(x,mTitleY,d1.width,d1.height);
      mCancelButton.reshape(x+d1.width+Off,mTitleY,d2.width,d2.height);
      minimize();
	}
   /*************************************************
   * end credits
   *************************************************/
	public void endCredits()
	{
	   remove(mGoButton);
	   remove(mCancelButton);
	   mIndex=mOldIndex;
	   mGoButton=null;
	   mCancelButton=null;
	   restoreToolbar();
	}

   /*************************************************
   * visit dcompose homepage
   *************************************************/
	public void visitDcompose()
	{
	   try
	   {
   	   URL url=new URL("http://www.ozemail.com.au/~butter");
   	   getAppletContext().showDocument(url);
   	}
   	catch (MalformedURLException e)
   	{
   	   System.out.println("visitDcompose failed, malformedURL");
   	}
	}

   /*************************************************
   * minimise toolbar
   *************************************************/
	public void minimize()
	{
		mToolbarVisible=false;
		mToolbar.hide();
		mChoice.hide();
	}
   /*************************************************
   * restore the toolbar
   *************************************************/
	public void restoreToolbar()
	{
		mToolbarVisible=true;
		mToolbar.show();
		mChoice.show();
	}

   /*************************************************
   * set the busy state of the applet- changes cursor and
   * disables the toolbar and choice
   * @param aBusy true if busy
   *************************************************/
	void setBusy(boolean aBusy)
	{
		mBusy = aBusy;
		if (aBusy)
		{
			mFrame.setCursor(Frame.WAIT_CURSOR);
			//mChoice.disable(); //seems to make IE4.0 hang!
		}
		else
		{	mFrame.setCursor(Frame.DEFAULT_CURSOR);
			//mChoice.enable();
		}
		mToolbar.setBusy(aBusy);

	}

}


