/**
 *#########################################################################
 *
 * A component of the Gatherer application, part of the Greenstone digital
 * library suite from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * <BR><BR>
 *
 * Author: John Thompson, Greenstone Digital Library, University of Waikato
 *
 * <BR><BR>
 *
 * Copyright (C) 1999 New Zealand Digital Library Project
 *
 * <BR><BR>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * <BR><BR>
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * <BR><BR>
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *########################################################################
 */
package org.greenstone.gatherer.download;

import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
import javax.swing.tree.*;
import org.greenstone.gatherer.*;
import org.greenstone.gatherer.util.SafeProcess;

/** This class provides access to the functionality of the WGet program, either by calling it via a shell script or by the JNI. It maintains a queue of pending jobs, and the component for showing these tasks to the user.
 * @author John Thompson, Greenstone Digital Library, University of Waikato
 * @version 2.3
 */
public class DownloadScrollPane
    extends Thread {
    
    /** <i>true</i> if verbose debug messages should be displayed, <i>false</i> otherwise. */
    private boolean debug = false;
    /** <i>true</i> if successfully completed tasks should be automatically removed from the job queue. */
    private boolean remove_complete_jobs = true;

    private JPanel filler_pane = null;
    /** The panel that the task list will be shown in. */
    private JPanel list_pane;
    /** The job currently underway. */
    private DownloadJob job;
    /** A scroll pane which will be used to display the list of pending tasks. */
    private JScrollPane list_scroll;
    /** A queue of download tasks. */
    private Vector job_queue;
    static final private boolean simple = true;

        
    public DownloadScrollPane() {
	job = null;
	job_queue = new Vector();

	Color colour_ctb = Configuration.getColor("coloring.collection_tree_background", false);

	filler_pane = new JPanel();
	filler_pane.setBackground(colour_ctb);

	list_pane = new JPanel();
	list_pane.setLayout(new BoxLayout(list_pane, BoxLayout.Y_AXIS));
	list_pane.setBackground(colour_ctb);
	
	list_scroll = new JScrollPane(list_pane);
    }

    /** 
     * To be used with DownloadJob.java's old_callDownload() and old_actionPerformed()
     * OR by uncommenting the "synchronized(this)" section in Download.java at the end of
     * its new callDownload() along with commenting out "mummy.deleteCurrentDownloadJob(this);"
     * in Download.java's doneCleanup().
     */
    public void old_deleteDownloadJob(DownloadJob delete_me) {
	if (delete_me == job) {
	    try {
		// Wait for the job to finish cleaning up, before we can continue cleaning up here.
		// But sometimes the job has completed (terminated naturally) and in that case there's
		// nothing to wait for.
		synchronized(delete_me) {
		    if (!delete_me.hasSignalledStop()) { // don't wait if DownloadJob.COMPLETED 
			///SafeProcess.log("**************** download scrollpane waiting for downloadjob to stop");
			delete_me.wait();
		    }
		}
	    } catch (Exception e) {
		e.printStackTrace();
	    }	    
	}

	///System.err.println("**************** Deleting job from download scroll pane");
	// Close button pressed, get rid of this download's dedicated pane
	finishedDownloadJob(delete_me, true);
    }

    /**
     * If called to delete the current download job, this method won't do anything.
     * But if called on any inactive download job, its display is removed.
     */
    public void deleteDownloadJob(DownloadJob delete_me) {
	if (delete_me != job) {

	    delete_me.setState(DownloadJob.STOPPED); // it's not the current job, so not running any SafeProcess, so don't need to send interrupt, just set state to stopped
	    SafeProcess.log("**************** Deleting job from download scroll pane");
	    // Close button pressed, get rid of this download's dedicated pane
	    finishedDownloadJob(delete_me, true);
	} // else don't do anything, we'll be contacted again when the current job can be deleted

    }

    /**
     * To be called when we're ready to delete the current download job,
     * else this method won't do anything
     */
    public void deleteCurrentDownloadJob(DownloadJob delete_me) {
	if (delete_me == job) {
	    SafeProcess.log("**************** Deleting current job from download scroll pane");
	    // Close button pressed, get rid of this download's dedicated pane
	    finishedDownloadJob(delete_me, true);
	}
    }


    /** To be called when a download job has terminated naturally or was prematurely stopped
     *	via the close button. 
     *  Gets rid of this download's pane with buttons and progress bar if prematurely stopped. */
    protected void finishedDownloadJob(DownloadJob delete_me, boolean removeDisplay) {
	Gatherer.invokeInEDT_replacesProceedInCurrThread(
	     "DownloadScrollPane.finishedDownloadJob()",
	     Gatherer.SYNC,
	     new Runnable() {
		 public void run() {
		     if (delete_me.hasSignalledStop()) {
			 if(removeDisplay) {
			     list_pane.remove(delete_me.getProgressBar());
			     list_pane.remove(filler_pane);
			 }
			 job_queue.remove(delete_me);
			 if(job_queue.size() > 0) {
			     Dimension progress_bar_size = delete_me.getProgressBar().getPreferredSize();
			     Dimension list_pane_size = list_pane.getSize();
			     int height = list_pane_size.height - (job_queue.size() * progress_bar_size.height);
			     progress_bar_size = null;
			     if(height > 0) {
				 filler_pane.setPreferredSize(new Dimension(list_pane_size.width, height));
				 list_pane.add(filler_pane);
			     }
			     list_pane_size = null;
			 }
			 list_pane.updateUI();
		     }
		     else {
			 DebugStream.println("Somehow we're trying to delete a job that is still running.");
		     }
		 }
	     });
    }

    public synchronized void downloadComplete() {
	job.downloadComplete();
    }

    public synchronized void downloadFailed() {
	job.downloadFailed();
    }

    public synchronized void downloadWarning() {
	job.downloadWarning();
    }

    public JScrollPane getDownloadJobList() {
	return list_scroll;
    }

    public synchronized boolean hasSignalledStop() {
	return job.hasSignalledStop();
    }

    public void newDownloadJob(Download download, String mode, Properties proxy_urls) {
	// Create the job and fill in the details from gatherer.config.

	DebugStream.println("About to create a new job");
	
        DownloadJob new_job = new DownloadJob(download, Configuration.proxy_pass, Configuration.proxy_user, this, mode, proxy_urls);
	// Tell it to run as soon as possible

	new_job.setState(DownloadJob.RUNNING);
	
	// Add to job_queue job list.
	job_queue.add(new_job);

	// Now add it to the visual component, job list.

	list_pane.remove(filler_pane);

	Dimension progress_bar_size = new_job.getProgressBar().getPreferredSize();

	Dimension list_pane_size = list_pane.getSize();

	int height = list_pane_size.height - (job_queue.size() * progress_bar_size.height);

	progress_bar_size = null;

	list_pane.add(new_job.getProgressBar());

	if(height > 0) {
	    filler_pane.setPreferredSize(new Dimension(list_pane_size.width, height));
	    list_pane.add(filler_pane);
	}

	list_pane_size = null;
	//list_pane.setAlignmentX(Component.LEFT_ALIGNMENT);
	list_pane.updateUI();
	new_job = null;		    //job = (DownloadJob) job_queue.get(index);

	synchronized(this) {
	    notify(); // Just incase its sleeping.
	}
    }
    
    public synchronized void updateProgress(long current, long expected) {
	job.updateProgress(current, expected);
    }
    
    /* There may be times when the download thread is sleeping, but the
     * user has indicated that a previously paused job should now begin
     * again. The flag within the job will change, so we tell the thread
     * to start again.
     */
    public void resumeThread() {
	synchronized(this) {
	    notify(); // Just incase its sleeping.
	}
    }
    
    public void run() {
	while(true) {
	    if(job_queue.size() > 0) {
		while(!job_queue.isEmpty()) { 
		    job = (DownloadJob) job_queue.firstElement(); 
		    
		    if(job.getState() == DownloadJob.RUNNING) {
			DownloadJob delete_me = job;
			String jobDisplayString = job.toString();
			DebugStream.println("DownloadJob " + jobDisplayString + " Begun.");
			System.err.println("DownloadJob " + job.port + " " + job.toString() + " Begun.");
			job.callDownload();
			// Job is done. Ended naturally, don't get rid of this download's separate display panel
			finishedDownloadJob(delete_me, false); 
			System.err.println("DownloadJob " + jobDisplayString + " complete.");
			DebugStream.println("DownloadJob " + jobDisplayString + " complete."); // by this point job is null!
			job = null;
			delete_me = null;
		    }
		}
		try {
		    synchronized(this) {
			DebugStream.println("WGet thread is waiting for DownloadJobs.");
			wait();
		    }
		} catch (InterruptedException e) {
		    // Time to get going again.
		}
	    }
	    else {
		try {
		    synchronized(this) {
			DebugStream.println("WGet thread is waiting for DownloadJobs.");
			wait();
		    }
		} catch (InterruptedException e) {
		    // Time to get going again.
		}
	    }
	}
    }
}
