/*
 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.net;

import java.util.ArrayList;
import java.util.Iterator;
import java.net.URL;

/**
 * ProgressMonitor is a class for monitoring progress in network input stream.
 *
 * @author Stanley Man-Kit Ho
 */
public class ProgressMonitor
{
    /**
     * Return default ProgressMonitor.
     */
    public static synchronized ProgressMonitor getDefault() {
        return pm;
    }

    /**
     * Change default ProgressMonitor implementation.
     */
    public static synchronized void setDefault(ProgressMonitor m)   {
        if (m != null)
            pm = m;
    }

    /**
     * Change progress metering policy.
     */
    public static synchronized void setMeteringPolicy(ProgressMeteringPolicy policy)    {
        if (policy != null)
            meteringPolicy = policy;
    }


    /**
     * Return a snapshot of the ProgressSource list
     */
    public ArrayList<ProgressSource> getProgressSources()    {
        ArrayList<ProgressSource> snapshot = new ArrayList<ProgressSource>();

        try {
            synchronized(progressSourceList)    {
                for (Iterator<ProgressSource> iter = progressSourceList.iterator(); iter.hasNext();)    {
                    ProgressSource pi = iter.next();

                    // Clone ProgressSource and add to snapshot
                    snapshot.add((ProgressSource)pi.clone());
                }
            }
        }
        catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return snapshot;
    }

    /**
     * Return update notification threshold
     */
    public synchronized int getProgressUpdateThreshold()    {
        return meteringPolicy.getProgressUpdateThreshold();
    }

    /**
     * Return true if metering should be turned on
     * for a particular URL input stream.
     */
    public boolean shouldMeterInput(URL url, String method) {
        return meteringPolicy.shouldMeterInput(url, method);
    }

    /**
     * Register progress source when progress is began.
     */
    public void registerSource(ProgressSource pi) {

        synchronized(progressSourceList)    {
            if (progressSourceList.contains(pi))
                return;

            progressSourceList.add(pi);
        }

        // Notify only if there is at least one listener
        if (progressListenerList.size() > 0)
        {
            // Notify progress listener if there is progress change
            ArrayList<ProgressListener> listeners = new ArrayList<ProgressListener>();

            // Copy progress listeners to another list to avoid holding locks
            synchronized(progressListenerList) {
                for (Iterator<ProgressListener> iter = progressListenerList.iterator(); iter.hasNext();) {
                    listeners.add(iter.next());
                }
            }

            // Fire event on each progress listener
            for (Iterator<ProgressListener> iter = listeners.iterator(); iter.hasNext();) {
                ProgressListener pl = iter.next();
                ProgressEvent pe = new ProgressEvent(pi, pi.getURL(), pi.getMethod(), pi.getContentType(), pi.getState(), pi.getProgress(), pi.getExpected());
                pl.progressStart(pe);
            }
        }
    }

    /**
     * Unregister progress source when progress is finished.
     */
    public void unregisterSource(ProgressSource pi) {

        synchronized(progressSourceList) {
            // Return if ProgressEvent does not exist
            if (progressSourceList.contains(pi) == false)
                return;

            // Close entry and remove from map
            pi.close();
            progressSourceList.remove(pi);
        }

        // Notify only if there is at least one listener
        if (progressListenerList.size() > 0)
        {
            // Notify progress listener if there is progress change
            ArrayList<ProgressListener> listeners = new ArrayList<ProgressListener>();

            // Copy progress listeners to another list to avoid holding locks
            synchronized(progressListenerList) {
                for (Iterator<ProgressListener> iter = progressListenerList.iterator(); iter.hasNext();) {
                    listeners.add(iter.next());
                }
            }

            // Fire event on each progress listener
            for (Iterator<ProgressListener> iter = listeners.iterator(); iter.hasNext();) {
                ProgressListener pl = iter.next();
                ProgressEvent pe = new ProgressEvent(pi, pi.getURL(), pi.getMethod(), pi.getContentType(), pi.getState(), pi.getProgress(), pi.getExpected());
                pl.progressFinish(pe);
            }
        }
    }

    /**
     * Progress source is updated.
     */
    public void updateProgress(ProgressSource pi)   {

        synchronized (progressSourceList)   {
            if (progressSourceList.contains(pi) == false)
                return;
        }

        // Notify only if there is at least one listener
        if (progressListenerList.size() > 0)
        {
            // Notify progress listener if there is progress change
            ArrayList<ProgressListener> listeners = new ArrayList<ProgressListener>();

            // Copy progress listeners to another list to avoid holding locks
            synchronized(progressListenerList)  {
                for (Iterator<ProgressListener> iter = progressListenerList.iterator(); iter.hasNext();) {
                    listeners.add(iter.next());
                }
            }

            // Fire event on each progress listener
            for (Iterator<ProgressListener> iter = listeners.iterator(); iter.hasNext();) {
                ProgressListener pl = iter.next();
                ProgressEvent pe = new ProgressEvent(pi, pi.getURL(), pi.getMethod(), pi.getContentType(), pi.getState(), pi.getProgress(), pi.getExpected());
                pl.progressUpdate(pe);
            }
        }
    }

    /**
     * Add progress listener in progress monitor.
     */
    public void addProgressListener(ProgressListener l) {
        synchronized(progressListenerList) {
            progressListenerList.add(l);
        }
    }

    /**
     * Remove progress listener from progress monitor.
     */
    public void removeProgressListener(ProgressListener l) {
        synchronized(progressListenerList) {
            progressListenerList.remove(l);
        }
    }

    // Metering policy
    private static ProgressMeteringPolicy meteringPolicy = new DefaultProgressMeteringPolicy();

    // Default implementation
    private static ProgressMonitor pm = new ProgressMonitor();

    // ArrayList for outstanding progress sources
    private ArrayList<ProgressSource> progressSourceList = new ArrayList<ProgressSource>();

    // ArrayList for progress listeners
    private ArrayList<ProgressListener> progressListenerList = new ArrayList<ProgressListener>();
}


/**
 * Default progress metering policy.
 */
class DefaultProgressMeteringPolicy implements ProgressMeteringPolicy  {
    /**
     * Return true if metering should be turned on for a particular network input stream.
     */
    public boolean shouldMeterInput(URL url, String method)
    {
        // By default, no URL input stream is metered for
        // performance reason.
        return false;
    }

    /**
     * Return update notification threshold.
     */
    public int getProgressUpdateThreshold() {
        // 8K - same as default I/O buffer size
        return 8192;
    }
}
