/*
 * Licensed Materials - Property of Rogue Wave Software, Inc. 
 * © Copyright Rogue Wave Software, Inc. 2014, 2015 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */
package chart3d;

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyDescriptor;
import java.beans.Introspector;
import java.lang.reflect.Method;

import ilog.views.chart.IlvChart3DView;

/**
 * A GUI panel that controls the settings of a chart 3-D view.
 */
public class ControlPanel3D extends JPanel
{
    private static final Font FONT = new Font("Dialog", Font.PLAIN, 10);

    protected IlvChart3DView       view3D;
    protected PropertyDescriptor[] pds;

    /**
     * Creates a new <code>ControlPanel3D</code> for the specified 3-D view.
     */
    public ControlPanel3D(IlvChart3DView view)
    {
        this.view3D = view;
        try {
            pds = Introspector.getBeanInfo(view3D.getClass()).getPropertyDescriptors();
        } catch (Exception x) {
            x.printStackTrace();
            pds = null;
        }
        GridLayout layout = new GridLayout(0, 4, 0, 6);
        setLayout(layout);

        Slider slider = new Slider(-90, 90, "rotation", "Rotation");
        add(slider);

        slider = new Slider(-90, 90, "elevation", "Elevation");
        add(slider);

        slider = new Slider(0, 100, "depth", "Depth");
        add(slider);

        slider = new Slider(0, 100, "depthGap", "Depth Gap");
        add(slider);

        slider = new Slider(0, 200, "zoom", "Zoom", .01);
        add(slider);

        slider = new Slider(0, 100, "ambientLight", "Ambient Light", .01);
        add(slider);

        slider = new Slider(-90, 90, "lightLatitude", "Light Latitude");
        add(slider);

        slider = new Slider(-90, 90, "lightLongitude", "Light Longitude");
        add(slider);
    }

    /**
     * A slider used to control the value of a numeric property of the 3-D view.
     */
    class Slider extends JSlider implements ChangeListener, PropertyChangeListener
    {
        private String label;
        private String propName;
        private boolean internalUpdate;
        private double multiplier;
        private PropertyDescriptor pd;

        /** Creates a new slider connected to the specified property. */
        Slider(int min, int max, String propName, String label)
        {
            this(min, max, propName, label, 1.);
        }

        /** Creates a new slider connected to the specified property. */
        Slider(int min, int max, String propName, String label, double multiplier)
        {
            super(min, max, min);
            updateUI();
            this.label      = label;
            this.multiplier = multiplier;
            Border b1 = BorderFactory.createMatteBorder(1,0,0,0,Color.black);
            TitledBorder b2 = BorderFactory.createTitledBorder(b1, computeLabel());
            b2.setTitleColor(Color.black);
            b2.setTitleFont(FONT);
            setBorder(b2);
            addChangeListener(this);
            if (propName != null) {
                this.pd = findPropertyDescriptor(propName);
                this.propName = propName;
                view3D.addPropertyChangeListener(propName, this);
                updateValue();
            }
        }

        /**
         * Computes the title label.
         */
        private String computeLabel()
        {
            return " " + label + ": " + getValue() + " ";
        }

        /**
         * Invoked when the property connected to the slider has changed.
         */
        public void propertyChange(PropertyChangeEvent evt)
        {
            if (evt.getPropertyName().equals(propName)) { // Should always be true
                updateValue();
            }
        }

        /**
         * Updates the value of the slider.
         */
        private synchronized void updateValue()
        {
            if (pd == null)
                return;
            internalUpdate = true;
            try {
                Object o =  pd.getReadMethod().invoke(view3D, (Object[])null);
                setValue((int)(Double.parseDouble(o.toString())/multiplier));
            } catch (Exception x) {
                x.printStackTrace();
            }
            internalUpdate = false;
        }

        /** Invoked when the slider state has changed. */
        public void stateChanged(ChangeEvent evt)
        {
            ((TitledBorder)getBorder()).setTitle(computeLabel());
            repaint();
            if (internalUpdate || pd == null)
                return;
            try {
                Method m      = pd.getWriteMethod();
                Object[] arg  = null;
                Class argType = m.getParameterTypes()[0];
                if (argType == double.class)
                    arg = new Object[] {new Double(getValue()*multiplier)};
                else if (argType == float.class)
                    arg = new Object[] {new Float(getValue()*(float)multiplier)};
                else if (argType == int.class)
                    arg = new Object[] {new Integer((int)(getValue()*multiplier))};
                if (arg != null)
                    pd.getWriteMethod().invoke(view3D, arg);
            } catch (Exception x) {
                x.printStackTrace();
            }
        }
    }

    /** Finds the descriptor for the specified property. */
    private PropertyDescriptor findPropertyDescriptor(String propName)
    {
        if (propName != null && pds != null) {
            final int count = pds.length;
            for (int i=0; i<count; ++i) {
                if (propName.equals(pds[i].getName()))
                    return pds[i];
            }
        }
        return null;
    }
}