/** 
 *  BlackHole Source File
 *
 *  GNU Copyright (C) 2008  Gaspar Sinai gaspar(at)adys.org 
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2,
 *  dated June 1991. See file COPYYING for details.
 *
 *  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.
 *
 *  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 sinai.gaspar.blackhole;

import java.awt.Panel;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.InputStream;
import java.io.BufferedInputStream;

import java.util.Hashtable;

/**
 * The options are selected in this panel.
 * This is using Xpm.java from http://www.bolthole.com/java/Xpm.html
 * @author Gaspar Sinai
 * @version 2008-05-03
 */
public class OptionPanel extends Panel 
    implements MouseListener, MouseMotionListener, IChangeListener  {

    IOptions options;
    int      zoomValue = 0;
    int      speedValue = 0;

    int typeComponent;
    int speedComponent = -1;
    int zoomComponent = -1;
    int backComponent = -1;
    int resetComponent = -1;

    public OptionPanel (IOptions options) {
        this.options = options;
        addMouseListener (this);
        addMouseMotionListener (this);
        states[COMP_SPIRAL] = 1;
        typeComponent = COMP_SPIRAL;
        changed ();
    }
    // setPreferredSize is JDK1.5
    public Dimension getPreferredSize () {
       return new Dimension (32, super.getSize().height);
    }


    public void setType (int type) {
        states[COMP_TRIANGLE] = 0;
        states[COMP_SPIRAL] = 0;
        typeComponent = -1;
        if (type == IOptions.TYPE_TRIANGLE) {
            states[COMP_TRIANGLE] = 1;
            typeComponent = COMP_TRIANGLE;
        }
        if (type == IOptions.TYPE_SPIRAL) {
            states[COMP_SPIRAL] = 1;
            typeComponent = COMP_SPIRAL;
        }
        options.setType (type);
        repaint ();
    }

    private static final int COMP_SPIRAL = 0;
    private static final int COMP_TRIANGLE = 1;
    private static final int COMP_ZOOM_IN = 2;
    private static final int COMP_ZOOM_OUT = 3;
    private static final int COMP_SPEED_IN= 4;
    private static final int COMP_SPEED_OUT = 5;
    private static final int COMP_RESET = 6;
    private static final int COMP_BACK = 7;
    private static final int COMP_FORWARD = 8;
    private static final int COMP_MAX = 9;

    /* Component x,y coordinates */
    private static final int COMP_XY[][] = {
       { 4, 4 + 0 },
       { 4, 4 + 32 },
       { 4, 4 + 64 },
       { 4, 4 + 96 },
       { 4, 4 + 128 },
       { 4, 4 + 160 },
       { 4, 4 + 192 },
       { 4, 4 + 224 },
       { 4, 4 + 256 }
    };
    /* Component icon names */
    private static final String iconName[] = {
        "spiral", 
        "triangle", 
        "zoomin", 
        "zoomout", 
        "speedin", 
        "speedout", 
        "reset", 
        "back", 
        "forward", 
    };

    private int states [] = new int[COMP_MAX];
    

    /**
     * In awt this is called first, and paint is called from here.
     * We implemented a double buffer here.
     */
    public void update (Graphics graphics) {
        Image db = createImage (getSize().width, getSize().height);
        if (db == null) {
            return;
        }
        Graphics dbg = db.getGraphics();
        paint (dbg);
        graphics.drawImage (db, 0, 0, this);
    }

    public void paint (Graphics graphics) {
        graphics.setColor (getBackground());
        graphics.fillRect(0, 0, getSize().width, getSize().height);
        graphics.setColor (getForeground());
        for (int i=0; i<COMP_MAX; i++) {
            graphics.drawImage (getImage("icons/" 
                + iconName[i] + states[i] + ".xpm"),
                COMP_XY[i][0], COMP_XY[i][1], this);
        }
    }

    static Hashtable iconCache = new Hashtable();
    static int MAX_ICON_SIZE = 10000;

    public static Image getImage (String name) {
        Image ret = (Image) iconCache.get (name);
        if (ret != null) return ret;
        try {
            InputStream is = OptionPanel.class.getResourceAsStream(name);
            BufferedInputStream bis = new BufferedInputStream(is);
            byte tmp[] = new byte[MAX_ICON_SIZE];
            int len = bis.read (tmp, 0, MAX_ICON_SIZE);
            String str = new String (tmp, 0, len, "ISO8859-1");
            // We are strictly java 1.0.2, we dont use javax.ImageIO
            ret = Xpm.XpmToImage (str);
            iconCache.put (name, ret);
        } catch (Exception e) {
            System.err.println ("Can not read " + name);
            e.printStackTrace ();
        }
        return ret;
    }

    public int getComponent (int x, int y) {
        for (int i=0; i<COMP_MAX; i++) {
            if (x >= COMP_XY[i][0] && x < COMP_XY[i][0] + 24
               && y >= COMP_XY[i][1] && y < COMP_XY[i][1] + 24) return i;
        }
        return -1;
    }


    public void mouseClicked(MouseEvent e) {
    }
    public void mouseEntered(MouseEvent e) {
        setLastComponentState (false);
    }
    public void mouseExited(MouseEvent e) {
        setLastComponentState (false);
    }
    
    int lastComponent = -1;
    public void mousePressed(MouseEvent e) {
        lastComponent = getComponent (e.getX(), e.getY());
        if (lastComponent < 0) return;
        states[lastComponent] = 1;
        repaint();
    }

    public void mouseReleased(MouseEvent e) {
        int comp = getComponent (e.getX(), e.getY());
        if (comp == lastComponent) {
            componentActivated (comp);
        }
        lastComponent = -1;
        compInitialState();
    }

    public void compInitialState () {
        for (int i=0; i<COMP_MAX; i++) {
            if (typeComponent == i || speedComponent == i 
                    || zoomComponent == i || backComponent == i
                    || resetComponent == i) {
                states[i] = 1;
            } else {
                states[i] = 0;
            }
        }
        repaint();
    }

    public void setLastComponentState (boolean isOn) {
        if (lastComponent < 0) return;
        int state = isOn ? 1 : 0;
        if (states[lastComponent] != state) {
            states[lastComponent] = state;
            repaint();
        }
    }

    public void mouseDragged (MouseEvent e) {
        int comp = getComponent (e.getX(), e.getY());
        setLastComponentState (comp == lastComponent);
    }

    public void mouseMoved (MouseEvent e) {
    }

    public void componentActivated (int compIndex) {
        switch (compIndex) {
        case COMP_SPIRAL:
            setType (BoardPanel.TYPE_SPIRAL);
            break;
        case COMP_TRIANGLE:
            setType (BoardPanel.TYPE_TRIANGLE);
            break;
        case COMP_ZOOM_IN:
            zoomValue = options.zoom (zoomValue+1);
            // reached max speed.
            if (options.isZoomLimited()) {
                zoomComponent = COMP_ZOOM_IN;
            } else {
                zoomComponent = -1;
            }
            break;
        case COMP_ZOOM_OUT:
            zoomValue = options.zoom (zoomValue-1);
            // reached max speed.
            if (options.isZoomLimited()) {
                zoomComponent = COMP_ZOOM_OUT;
            } else {
                zoomComponent = -1;
            }
            break;
        case COMP_SPEED_IN:
            speedValue = options.speed (speedValue+1);
            // reached max speed.
            if (options.isDelayLimited()) {
                speedComponent = COMP_SPEED_IN;
            } else {
                speedComponent = -1;
            }
            break;
        case COMP_SPEED_OUT:
            speedValue = options.speed (speedValue-1);
            // reached max speed.
            if (options.isDelayLimited()) {
                speedComponent = COMP_SPEED_OUT;
            } else {
                speedComponent = -1;
            }
            break;
        case COMP_RESET:
            options.setStart (1);
            break;
        case COMP_BACK:
            options.back ();
            break;
        case COMP_FORWARD:
            options.forward ();
            break;
        default:
            return;
        }
    }

    /**
     * @see sinai.gaspar.blackhole.ChangeListener#changed
     */
    public void changed () {
        int newComp = -1;
        int newResetComp = -1;
        if (options.getStart () == 1L ) {
            newComp = COMP_BACK;
            newResetComp = COMP_RESET;
        }
        if (backComponent != newComp || resetComponent != newResetComp) {
            backComponent = newComp;
            resetComponent = newResetComp;
            compInitialState ();
        }
    }
}
