/* class JProgressBar
 *
 * Copyright (C) 2001, 2002, 2003  R M Pitman
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package charvax.swing;

import charva.awt.Dimension;
import charva.awt.Insets;
import charva.awt.Point;
import charva.awt.Toolkit;
import charva.awt.event.KeyEvent;

/**
 * A component that displays an integer value within a bounded interval.
 * A progress bar is typically used to indicate the progress of some task
 * by displaying a percentage of completion and possibly a textual display
 * of this percentage.
 */
public class JProgressBar
        extends JComponent {

    /**
     * Creates a horizontal progress bar that displays a border
     * but no progress string.
     */
    public JProgressBar() {
    }

    /**
     * Creates a progress bar with the specified minimum and
     * maximum values.
     */
    public JProgressBar(int min_, int max_) {
        _minimum = min_;
        _maximum = max_;
    }

    /**
     * Set the progress bar's minimum value.
     */
    public void setMinimum(int min_) {
        _minimum = min_;
        if (_maximum <= _minimum)
            _maximum = _minimum + 1;
        if (_value < _minimum)
            _value = _minimum;
    }

    /**
     * Set the progress bar's value.
     */
    public void setValue(int value_) {
        if (value_ < _minimum)
            _value = _minimum;
        else
            _value = value_;

        /* If this component is already displayed, generate a PaintEvent
         * and post it onto the queue.
         */
        repaint();
    }

    /**
     * Set the progress bar's maximum value.
     */
    public void setMaximum(int max_) {
        _maximum = max_;
        if (_minimum > _maximum)
            _minimum = _maximum - 1;
        if (_value > _maximum)
            _value = _maximum;
    }

    /**
     * Set the size of the component on the screen.
     */
    public void setSize(Dimension size_) {
        _width = size_.width;

        if (_width < 3) {
            throw new IllegalArgumentException("length of progress bar must be at least 3");
        }
    }

    /**
     * Get the screen size of the progress bar.
     */
    public Dimension getSize() {
        return new Dimension(this.getWidth(), this.getHeight());
    }

    public int getWidth() {
        Insets insets = super.getInsets();
        return _width + insets.left + insets.right;
    }

    public int getHeight() {
        Insets insets = super.getInsets();
        return 1 + insets.top + insets.bottom;
    }

    public void draw() {

        // Draw the border if it exists
        super.draw();

        /* Get the absolute origin of this component.
         */
        Point origin = getLocationOnScreen();
        Insets insets = super.getInsets();
        origin.translate(insets.left, insets.top);

        Toolkit term = Toolkit.getDefaultToolkit();

        int colorpair = getCursesColor();

        int offset = ((_value - _minimum) * _width) / _maximum;

        if (!isIndeterminate()) {
            for (int i = 0; i < offset; i++) {
                term.setCursor(origin.addOffset(i, 0));
                term.addChar(' ', Toolkit.A_REVERSE, colorpair);
            }
            for (int k = offset; k < _width; k++) {
                term.setCursor(origin.addOffset(k, 0));
                term.addChar(Toolkit.ACS_CKBOARD, 0, colorpair);
            }
        } else {
            for (int i = 0; i < _width; i++) {
                term.setCursor(origin.addOffset(i, 0));
                term.addChar(' ', 0, colorpair);
            }
            term.setCursor(origin.addOffset(offset, 0));
            term.addChar(' ', Toolkit.A_REVERSE, colorpair);
        }

        // Display the progress string if required
        if (isStringPainted()) {
            offset = (getSize().width - _string.length()) / 2;
            term.setCursor(origin.addOffset(offset, 0));
            term.addString(_string, 0, colorpair);
        }
    }

    /**
     * This component will not receive focus when Tab or Shift-Tab is pressed.
     */
    public boolean isFocusTraversable() {
        return false;
    }

    /**
     * The JProgressBar class ignores key events. A JProgressBar should never
     * have input focus anyway.
     */
    public void processKeyEvent(KeyEvent ke_) {
    }

    /**
     * The JProgressBar component never gets the keyboard input focus.
     */
    public void requestFocus() {
    }

    public void debug(int level_) {
        for (int i = 0; i < level_; i++)
            System.err.print("    ");
        System.err.println("JProgressBar origin=" + _origin +
                " size=" + getSize() + " value=" + _value +
                " minimum=" + _minimum + " maximum=" + _maximum);
    }

    public Dimension minimumSize() {
        return getSize();
    }

    public int getMinimum() {
        return _minimum;
    }

    public int getValue() {
        return _value;
    }

    public int getMaximum() {
        return _maximum;
    }

    /**
     * Returns the value of the _stringPainted property
     */
    public boolean isStringPainted() {
        return _stringPainted;
    }

    /**
     * Set the value of the _stringPainted property
     */
    public void setStringPainted(boolean stringPainted_) {
        _stringPainted = stringPainted_;
    }

    /**
     * Sets the value of the progress string
     */
    public void setString(String string_) {
        _string = string_;
        repaint();
    }

    public void setIndeterminate(boolean newval) {
        if (newval == _indeterminate)
            return;   // no change in state.

        _indeterminate = newval;
        if (newval) {
            setMinimum(0);
            setMaximum(100);
            _indeterminateThread = new IndeterminateThread();
            _indeterminateThread.start();
        } else {
            if (_indeterminateThread != null &&
                    _indeterminateThread.isAlive()) {

                _indeterminateThread.interrupt();
            }
        }
    }

    public boolean isIndeterminate() {
        return _indeterminate;
    }

    /**
     * Returns the value of the progress string
     */
    public String getString() {
        return _string;
    }

    public void finalize() {
        if (_indeterminateThread != null &&
                _indeterminateThread.isAlive()) {

            _indeterminateThread.interrupt();
        }
    }

    /**
     * A nonstatic inner class that updates the progress bar
     * once per second when the progress bar is in "indeterminate" mode.
     */
    private class IndeterminateThread extends Thread {
        /**
         * Constructor
         */
        private IndeterminateThread() {
        }

        /**
         * Twice per second, wake up and update the progress bar. Note that
         * since this thread is not the event-dispatching thread, we cannot
         * manipulate the screen components directly; instead, we must
         * call the static method "invokeLater()" of the EventQueue class,
         * which will cause the event-dispatching thread to update the progress
         * bar.
         * See "Core Java, Volume II" by Horstmann and Cornell, chapter 1;
         * Also see
         * http://java.sun.com/docs/books/tutorial/uiswing/overview/threads.html
         */
        public void run() {
            try {
                while (true) {
                    this.adjust();
                    Thread.sleep(500L);
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            JProgressBar.this.setValue(_percent);
                        }
                    });
                }
            } catch (InterruptedException e) {
                return;
            }
        }

        /**
         * Adjust the percent-completed so that the indicator moves right
         * and left continuously.
         */
        private void adjust() {
            if (_right) {
                if (_percent < 96)
                    _percent += 4;
                else {
                    _right = false;
                }
            } else {
                if (_percent > 0)
                    _percent -= 4;
                else {
                    _right = true;
                }
            }
        }

        boolean _right = true;
        int _percent = 0;
    }

    //********************************************************************
    // INSTANCE VARIABLES

    protected int _minimum = 0;
    protected int _value = 0;
    protected int _maximum = 100;
    protected boolean _stringPainted = false;
    protected String _string = "";

    /**
     * The length of this component on the screen.
     */
    protected int _width = 50;	// default size

    protected boolean _indeterminate;

    protected Thread _indeterminateThread;
}
