/*
 *  Showcase.java, the main demo application of project jchart2d.
 *  Copyright (C) 2007 - 2011 Achim Westermann, created on 10.12.2004, 13:48:55
 *
 *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  If you modify or optimize the code in a useful way please let me know.
 *  Achim.Westermann@gmx.de
 *
 */
package info.monitorenter.gui.chart.demos;

import info.monitorenter.gui.chart.Chart2D;
import info.monitorenter.gui.chart.controls.LayoutFactory;
import info.monitorenter.gui.chart.events.Chart2DActionSaveImageSingleton;
import info.monitorenter.gui.chart.io.ADataCollector;
import info.monitorenter.gui.chart.io.RandomDataCollectorOffset;
import info.monitorenter.gui.chart.rangepolicies.RangePolicyMinimumViewport;
import info.monitorenter.gui.chart.traces.Trace2DLtd;
import info.monitorenter.gui.chart.views.ChartPanel;
import info.monitorenter.util.Range;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * Advanced demonstration applet for jchart2d.
 * <p>
 * Please right click on the chart and on the label for the traces to see popup
 * menus that offer the freshest new features.
 * <p>
 * 
 * @author <a href="mailto:Achim.Westermann@gmx.de">Achim Westermann </a>
 * 
 * @version $Revision: 1.15 $
 * 
 */
public final class Showcase
    extends JApplet { 

  /**
   * Panel with controls for the chart.
   * <p>
   * 
   * @author <a href="mailto:Achim.Westermann@gmx.de">Achim Westermann </a>
   * 
   * 
   * @version $Revision: 1.15 $
   */
  final class ControlPanel
      extends JPanel {
    /**
     * Generated for <code>serialVersionUID</code>.
     */
    private static final long serialVersionUID = 3257005441048129846L;

    /** Slider for the maximum amount of points to show. */
    private JSlider m_amountPointsSlider;

    /** Button to clear data from the chart. */
    private JButton m_clear;

    /**
     * <p>
     * The <code>JComboBox</code> used to choose the color of the chart.
     * </p>
     */
    private JComboBox m_colorChooser;

    /** The slider for choosing the speed of adding new points. */
    private JSlider m_latencyTimeSlider;

    /** Button for the action of capturing a snapshot image. */
    private JButton m_snapshot;

    /** Button to start or stop data collection. */
    private JButton m_startStop;

    /**
     * Defcon.
     * <p>
     */
    protected ControlPanel() {
      // create the components:
      this.setBackground(Color.WHITE);
      this.createAmountPointSlider();
      this.createLatencySlider();
      this.createStartStopButton();
      this.createSnapShotButton();
      this.createClearButton();
      this.createColorChooserButton();

      // Layouting: Vertical Grid Layout for putting the sliders...
      this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
      this.add(this.m_amountPointsSlider);
      this.add(this.m_latencyTimeSlider);
      // GridLayout stretches components (no respect for their preferred size):
      // Small trick by inserting another component with different Layout.
      JComponent stretch = new JPanel();
      stretch.setBackground(Color.WHITE);
      stretch.setLayout(new BoxLayout(stretch, BoxLayout.X_AXIS));
      stretch.add(Box.createHorizontalGlue());
      stretch.add(this.m_startStop);
      stretch.add(Box.createHorizontalGlue());
      stretch.add(this.m_clear);
      if (this.m_snapshot != null) {
        // for applet usage snapshot is null!
        stretch.add(Box.createHorizontalGlue());
        stretch.add(this.m_snapshot);
      }
      stretch.add(Box.createHorizontalGlue());
      stretch.add(this.m_colorChooser);
      stretch.add(Box.createHorizontalGlue());
      this.add(stretch);
    }

    /**
     * Helper to create a slider for maximum amount of points to show.
     * <p>
     */
    private void createAmountPointSlider() {
      // amountPointsSlider
      this.m_amountPointsSlider = new JSlider(10, 410);
      this.m_amountPointsSlider.setBackground(Color.WHITE);
      // find the value of max points:
      int maxPoints = Showcase.this.getTrace().getMaxSize();
      this.m_amountPointsSlider.setValue(maxPoints);
      this.m_amountPointsSlider.setMajorTickSpacing(40);
      this.m_amountPointsSlider.setMinorTickSpacing(20);
      this.m_amountPointsSlider.setSnapToTicks(true);
      this.m_amountPointsSlider.setPaintLabels(true);
      this.m_amountPointsSlider.setBorder(BorderFactory.createTitledBorder(BorderFactory
          .createEtchedBorder(), "Amount of points.", TitledBorder.LEFT, TitledBorder.BELOW_TOP));
      this.m_amountPointsSlider.setPaintTicks(true);
      this.m_amountPointsSlider.addChangeListener(new ChangeListener() {
        public void stateChanged(final ChangeEvent e) {
          JSlider source = (JSlider) e.getSource();
          // Only if not currently dragged...
          if (!source.getValueIsAdjusting()) {
            int value = source.getValue();
            Showcase.this.getTrace().setMaxSize(value);
          }
        }
      });

    }

    /**
     * Helper to create a button for clearing data from the chart.
     * <p>
     */
    private void createClearButton() {
      // clear Button
      this.m_clear = new JButton("clear");
      this.m_clear.setBackground(Color.WHITE);
      this.m_clear.setBackground(Color.WHITE);
      this.m_clear.addActionListener(new ActionListener() {
        public void actionPerformed(final ActionEvent e) {
          Showcase.this.clearTrace();
        }
      });

    }

    /**
     * Helper to create a button for choosing trace colors.
     * <p>
     */
    private void createColorChooserButton() {
      // color chooser JComboBox
      this.m_colorChooser = new JComboBox();
      this.m_colorChooser.setBackground(Color.WHITE);

      /**
       * Color with a name.
       * <p>
       * 
       * @author <a href="mailto:Achim.Westermann@gmx.de">Achim Westermann </a>
       * @version $Revision: 1.15 $
       */
      final class ColorItem
          extends Color {
        /**
         * Generated <code>serialVersionUID</code>.
         */
        private static final long serialVersionUID = 3257854281104568629L;

        /** The name of the color. */
        private String m_name;

        /**
         * Creates an instance with the given color and it's name.
         * <p>
         * 
         * @param c
         *          the color to use.
         * 
         * @param name
         *          the name of the color.
         */
        public ColorItem(final Color c, final String name) {
          super(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
          this.m_name = name;
        }

        /**
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
          return this.m_name;
        }
      }
      this.m_colorChooser.addItem(new ColorItem(Color.BLACK, "black"));
      this.m_colorChooser.addItem(new ColorItem(Color.BLUE, "blue"));
      this.m_colorChooser.addItem(new ColorItem(Color.CYAN, "cyan"));
      this.m_colorChooser.addItem(new ColorItem(Color.DARK_GRAY, "darg gray"));
      this.m_colorChooser.addItem(new ColorItem(Color.GRAY, "gray"));
      this.m_colorChooser.addItem(new ColorItem(Color.GREEN, "green"));
      this.m_colorChooser.addItem(new ColorItem(Color.LIGHT_GRAY, "light gray"));
      this.m_colorChooser.addItem(new ColorItem(Color.MAGENTA, "magenta"));
      this.m_colorChooser.addItem(new ColorItem(Color.ORANGE, "orange"));
      this.m_colorChooser.addItem(new ColorItem(Color.PINK, "pink"));
      this.m_colorChooser.addItem(new ColorItem(Color.RED, "red"));
      this.m_colorChooser.addItem(new ColorItem(Color.YELLOW, "yellow"));

      this.m_colorChooser.addActionListener(new ActionListener() {
        public void actionPerformed(final ActionEvent ae) {
          Color color = (Color) ((JComboBox) ae.getSource()).getSelectedItem();
          Showcase.this.getTrace().setColor(color);
        }
      });
      this.m_colorChooser.setSelectedIndex(10);
      this.m_colorChooser.setMaximumSize(new Dimension(200, this.m_clear.getMaximumSize().height));

    }

    /**
     * Helper to create a slider for speed of adding new points.
     * <p>
     */

    private void createLatencySlider() {
      // Latency slider:
      this.m_latencyTimeSlider = new JSlider(10, 210);
      this.m_latencyTimeSlider.setBackground(Color.WHITE);
      this.m_latencyTimeSlider.setValue((int) Showcase.this.getCollector().getLatency());
      this.m_latencyTimeSlider.setMajorTickSpacing(50);
      this.m_latencyTimeSlider.setMinorTickSpacing(10);
      this.m_latencyTimeSlider.setSnapToTicks(true);
      this.m_latencyTimeSlider.setPaintLabels(true);
      this.m_latencyTimeSlider.setBorder(BorderFactory.createTitledBorder(BorderFactory
          .createEtchedBorder(), "Latency for adding points.", TitledBorder.LEFT,
          TitledBorder.BELOW_TOP));
      this.m_latencyTimeSlider.setPaintTicks(true);

      this.m_latencyTimeSlider.addChangeListener(new ChangeListener() {
        public void stateChanged(final ChangeEvent e) {
          JSlider source = (JSlider) e.getSource();
          // Only if not currently dragged...
          if (!source.getValueIsAdjusting()) {
            int value = source.getValue();
            Showcase.this.getCollector().setLatency(value);
          }
        }
      });
    }

    /**
     * Helper to create a button for taking snapshot images.
     * <p>
     */
    private void createSnapShotButton() {
      // the button for snapshot:
      this.m_snapshot = new JButton(Chart2DActionSaveImageSingleton.getInstance(
          Showcase.this.m_chart, "Save image"));
      this.m_snapshot.setBackground(Color.WHITE);
    }

    /**
     * Helper to create a button to start and stop button for data collection.
     * <p>
     */
    private void createStartStopButton() {
      // Start stop Button
      this.m_startStop = new JButton("start");
      this.m_startStop.setBackground(Color.WHITE);
      this.m_startStop.addActionListener(new ActionListener() {
        public void actionPerformed(final ActionEvent e) {
          JButton source = (JButton) e.getSource();
          if (Showcase.this.getCollector().isRunning()) {
            Showcase.this.stopData();
            source.setText("start");
          } else {
            Showcase.this.startData();
            source.setText("stop");
          }
          source.invalidate();
          source.repaint();
        }
      });
    }
  }

  /**
   * Generated <code>serialVersionUID</code>.
   */
  private static final long serialVersionUID = 3904676068135678004L;

  /**
   * Main entry that uses the applet initialization.
   * <p>
   * 
   * @param args
   *          ignored.
   * 
   * @see #init()
   */
  public static void main(final String[] args) {
    JFrame frame = new JFrame("Showcase");
    Showcase showcase = new Showcase();
    showcase.init();
    frame.getContentPane().add(showcase);
    frame.setSize(400, 600);
    // Enable the termination button [cross on the upper right edge]:
    frame.addWindowListener(new WindowAdapter() {
      /**
       * @see java.awt.event.WindowAdapter#windowClosing(java.awt.event.WindowEvent)
       */
      @Override
      public void windowClosing(final WindowEvent e) {
        System.exit(0);
      }
    });
    frame.setVisible(true);
  }

  /** The char to use. */
  protected Chart2D m_chart;

  /** The data collector to use. */
  private transient ADataCollector m_collector;

  /** The trace to use. */
  private Trace2DLtd m_trace;

  /**
   * Defcon.
   * 
   */
  public Showcase() {
    super();
  }

  /**
   * Clears the internal trace.
   * <p>
   */
  public synchronized void clearTrace() {
    this.getTrace().removeAllPoints();
  }

  /**
   * Returns the chart.
   * <p>
   * 
   * @return the chart to use.
   */
  public Chart2D getChart() {
    return this.m_chart;
  }

  /**
   * Returns the collector to use.
   * <p>
   * 
   * @return the collector to use.
   */
  public ADataCollector getCollector() {
    return this.m_collector;
  }

  /**
   * Returns the trace.
   * <p>
   * 
   * @return the trace.
   */
  public Trace2DLtd getTrace() {
    return this.m_trace;
  }

  /**
   * @see java.applet.Applet#init()
   */
  @Override
  public void init() {
    super.init();
    Chart2D chart = new Chart2D();
    this.setChart(chart);
    this.setSize(new Dimension(600, 500));
    this.m_chart.getAxisX().setPaintGrid(true);
    this.m_chart.getAxisY().setPaintGrid(true);
    chart.getAxisY().setRangePolicy(new RangePolicyMinimumViewport(new Range(-20, +20)));
    chart.setGridColor(Color.LIGHT_GRAY);
    this.setTrace(new Trace2DLtd(100));
    this.getTrace().setName("random");
    this.getTrace().setPhysicalUnits("Milliseconds", "random value");
    this.getTrace().setColor(Color.RED);
    chart.addTrace(this.getTrace());
    Container content = this.getContentPane();
    content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
    LayoutFactory factory = LayoutFactory.getInstance();
    ChartPanel chartpanel = new ChartPanel(chart);

    this.setJMenuBar(factory.createChartMenuBar(chartpanel, false));

    content.add(chartpanel);
    content.addPropertyChangeListener(chartpanel);
    this.setCollector(new RandomDataCollectorOffset(this.getTrace(), 50));
    content.add(new ControlPanel());
  }

  /**
   * Sets the chart to use.
   * <p>
   * 
   * I would never code this but applets won't access private members and
   * Checkstyle does not accept non-private members.
   * <p>
   * 
   * So it is only accepted if the member is null. Don't try calling.
   * <p>
   * 
   * @param chart2D
   *          the chart to use.
   */
  public void setChart(final Chart2D chart2D) {
    if (this.m_chart == null) {
      this.m_chart = chart2D;
    }
  }

  /**
   * Sets the collector to use.
   * <p>
   * 
   * @param collector
   *          the collector to use.
   */
  private void setCollector(final RandomDataCollectorOffset collector) {
    this.m_collector = collector;
  }

  /**
   * Sets the trace to use.
   * <p>
   * 
   * This will do nothing if the internal trace has been set before and is only
   * intended for the applet which needs public members or setters.
   * <p>
   * 
   * @param trace
   *          the trace to use.
   */
  public void setTrace(final Trace2DLtd trace) {
    if (this.m_trace == null) {
      this.m_trace = trace;
    }
  }

  /**
   * Starts data collection.
   * <p>
   * 
   */
  public synchronized void startData() {
    if (!this.getCollector().isRunning()) {
      this.getCollector().start();
    }
  }

  /**
   * Stops data collection.
   * 
   * <p>
   */
  public synchronized void stopData() {
    if (this.getCollector().isRunning()) {
      this.getCollector().stop();
    }
  }
}
