/*
 * Licensed Materials - Property of Rogue Wave Software, Inc. 
 * © Copyright Rogue Wave Software, Inc. 2014, 2017 
 * © 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 tablemodel;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import ilog.views.chart.IlvChart;
import ilog.views.chart.IlvChartInteractor;
import ilog.views.chart.IlvChartLayout;
import ilog.views.chart.IlvChartRenderer;
import ilog.views.chart.IlvColor;
import ilog.views.chart.IlvDisplayPoint;
import ilog.views.chart.IlvLegend;
import ilog.views.chart.IlvStyle;
import ilog.views.chart.data.IlvSwingTableDataSource;
import ilog.views.chart.event.ChartHighlightInteractionEvent;
import ilog.views.chart.event.ChartInteractionEvent;
import ilog.views.chart.event.ChartInteractionListener;
import ilog.views.chart.graphic.IlvMarker;
import ilog.views.chart.graphic.IlvMarkerFactory;
import ilog.views.chart.graphic.IlvMarkerIcon;
import ilog.views.chart.interactor.IlvChartEditPointInteractor;
import ilog.views.chart.interactor.IlvChartInfoViewInteractor;
import ilog.views.chart.print.IlvChartPrintableDocument;
import ilog.views.chart.renderer.IlvPolylineChartRenderer;
import ilog.views.chart.renderer.IlvSinglePolylineRenderer;
import shared.AbstractChartExample;
import shared.PDFGenerator;

/**
 * This sample shows how to bind a <code>javax.swing.TableModel</code> to a
 * chart data source.
 */
public class TableModelDemo extends AbstractChartExample {
//  static private final Color BG_COLOR = IlvColor.lightSteelBlue;

  static private IlvMarker[] MARKERS = { IlvMarkerFactory.getCircleMarker(), IlvMarkerFactory.getDiamondMarker(),
      IlvMarkerFactory.getSquareMarker(), IlvMarkerFactory.getTriangleMarker(), IlvMarkerFactory.getCrossMarker(),
      IlvMarkerFactory.createCompoundMarker(IlvMarkerFactory.getSquareMarker(), IlvMarkerFactory.getPlusMarker()),
      IlvMarkerFactory.createCompoundMarker(IlvMarkerFactory.getCircleMarker(), IlvMarkerFactory.getCrossMarker()),
      IlvMarkerFactory.createCompoundMarker(IlvMarkerFactory.getPlusMarker(), IlvMarkerFactory.getCrossMarker()) };

  /** The EditPoint interactor used to modify the chart data model. */
  IlvChartEditPointInteractor editInter;

  /** The cell editor used to edit Date objects. */
  TableCellEditor dateEditor = new DateCellEditor(new JTextField());

  /** The table data source referencing the table model. */
  IlvSwingTableDataSource tableDs;

  /** The JTable */
  JTable table;
  /** The chart */
  IlvChart chart;

  /**
   * Initializes a new <code>TableModelDemo</code> object.
   **/
  Override
  public void init(Container container) {
    super.init(container);
    // container.setLayout(new GridLayout(0,1));
    container.setLayout(new BorderLayout());
    // Create the chart.
    chart = createChart();

    // Create the swing TableModel containing the data.
    AbstractTableModel swingModel = null;
    try {
      swingModel = createSwingTableModel();
    } catch (ParseException e) {
      swingModel = new DefaultTableModel();
    } catch (NumberFormatException e) {
      swingModel = new DefaultTableModel();
    }
    // Bind an IlvSwingTableDataSource to this swing table model. The series
    // being arranged by column, the data source is of type COLUMN_SERIES.
    // Since a specific column is used for the abscissa (the Year column) for
    // all the series, the index in the table model of the year column
    // is also specified (0).
    tableDs = new IlvSwingTableDataSource(swingModel, IlvSwingTableDataSource.COLUMN_SERIES, 0, -1);
    // At this time, the data sets corresponding to the table model series
    // have been created.

    // Connect the data source to a polyline chart renderer.
    // The IlvPolylineChartRenderer will create a renderer for each data set
    // of its data source and hold them in an internal list. These sub-
    // renderers are called child renderers and can be parsed using an
    // Iterator (see below).
    IlvPolylineChartRenderer disp = new IlvPolylineChartRenderer();
    disp.setDataSource(tableDs);
    chart.addRenderer(disp);

    // To visualize every point, we want all the chart renderers to draw
    // a square mark at each point. To do so, we associate an IlvMarker
    // with every renderer held by <code>disp</code>, each child renderer
    // being retrieved via an iterator on the <code>disp</code> children.
    IlvSinglePolylineRenderer child;
    int i = 0;
    final Icon[] markerIcons = new Icon[disp.getChildCount()];
    for (Iterator<IlvChartRenderer> ite = disp.getChildIterator(); ite.hasNext(); ++i) {
      child = (IlvSinglePolylineRenderer) ite.next();
      child.setStyle(child.getStyle().setStroke(new BasicStroke(2)));
      child.setMarker(MARKERS[i % 8]);
      child.setMarkerSize(4);
      IlvStyle style = new IlvStyle(child.getStyle().getStrokePaint(),
          IlvColor.brighter(child.getStyle().getStrokeColor()));
      child.setMarkerStyle(style);
      markerIcons[i] = new IlvMarkerIcon(MARKERS[i % 8], 4, style);
    }

    // Create the JTable associated with the same TableModel as the chart.
    table = new JTable(swingModel);
    table.getTableHeader().setReorderingAllowed(true);
    // Uses a specialized cell renderer that displays the renderers markers in
    // the header cell.
    table.getTableHeader().setDefaultRenderer(new DefaultTableCellRenderer() {
      Override
      public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
          int row, int column) {
        Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        column = table.convertColumnIndexToModel(column);
        if (column > 0)
          ((JLabel) comp).setIcon(markerIcons[column - 1]);
        else
          ((JLabel) comp).setIcon(null);
        ((JLabel) comp).setOpaque(false);
        return comp;
      }
    });
    Dimension dim = table.getPreferredScrollableViewportSize();
    dim.height = Math.min(table.getRowHeight() * table.getRowCount(), dim.width);
    table.setPreferredScrollableViewportSize(dim);
    // Use a customized cell renderer to handle Date formatting.
    table.getColumn("Year").setCellRenderer(new DefaultTableCellRenderer() {
      SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy");

      Override
      public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
          int row, int column) {
        setText(dateFormat.format((Date) value));
        return this;
      }
    });

    // Use a customized cell editor to handle Date editing.
    table.setDefaultEditor(Date.class, dateEditor);

    table.setCellSelectionEnabled(true);
    // Add interactors to the chart. The infoView interactor pops up
    // a tooltip showing information about the data point when the
    // mouse moves over it. Furthermore, we want to select the corresponding
    // table cell. To do this, we add a chart interaction listener that
    // selects or deselects the cell depending on whether the data point has
    // been highlighted.
    IlvChartInteractor inter = new IlvChartInfoViewInteractor();
    inter.addChartInteractionListener(new ChartInteractionListener() {
      Override
      public void interactionPerformed(ChartInteractionEvent evt) {
        ChartHighlightInteractionEvent hevt = (ChartHighlightInteractionEvent) evt;
        if (hevt.isHighlighted()) {
          IlvDisplayPoint dp = hevt.getDisplayPoint();
          int row = dp.getIndex();
          int col = tableDs.getDataSetIndex(dp.getDataSet()) + 1;
          ListSelectionModel rowsel = table.getSelectionModel();
          ListSelectionModel colsel = table.getColumnModel().getSelectionModel();
          col = table.convertColumnIndexToView(col);
          rowsel.setSelectionInterval(row, row);
          colsel.setSelectionInterval(col, col);
        } else {
          table.getSelectionModel().clearSelection();
          table.getColumnModel().getSelectionModel().clearSelection();
        }
      }
    });
    chart.addInteractor(inter);
    // Add an IlvChartEditPointInteractor. This interactor allows editing
    // of the points of a data set. Since we want real-time modifications
    // (the modifications are sent to the data sets when the pointer moves
    // and not when the button is released), we turn on the opaqueEdit mode.
    editInter = new IlvChartEditPointInteractor();
    editInter.setOpaqueEdit(true);
    chart.addInteractor(editInter);

    JScrollPane jsp = new JScrollPane(table);
    jsp.setColumnHeaderView(table.getTableHeader());

    // Connect an IlvLegend to the chart in the top-left corner.
    IlvLegend legend = new IlvLegend();
    legend.setAntiAliasing(true);
    chart.addLegend(legend, IlvChartLayout.NORTH_WEST);

    JLabel label = new JLabel("Click on a point to edit", JLabel.CENTER);
    label.setOpaque(true);
    label.setBackground(Color.white);
    label.setBorder(BorderFactory.createLineBorder(label.getForeground()));
    JPanel panel = new JPanel(new BorderLayout());
    panel.add(label, BorderLayout.NORTH);
    panel.add(chart, BorderLayout.CENTER);
    container.add(panel, BorderLayout.CENTER);

    label = new JLabel("Click in a cell to change its value", JLabel.CENTER);
    label.setOpaque(true);
    label.setBackground(Color.white);
    label.setBorder(BorderFactory.createLineBorder(label.getForeground()));
    panel = new JPanel(new BorderLayout());
    panel.add(jsp, BorderLayout.CENTER);
    panel.add(label, BorderLayout.SOUTH);
    container.add(panel, BorderLayout.SOUTH);

    JToolBar toolbar = createToolBar();
    container.add(toolbar, BorderLayout.NORTH);
  }

  /**
   * Creates the document to be printed.
   */
  Override
  protected IlvChartPrintableDocument createPrintableDocument() {
    // We use a custom IlvChartPrintableDocument subclass to add a title
    // and a table dump on the printed page.
    return new TablePrintableDocument("TableModelDemo", table, chart);
  }

  /**
   * Creates the XSL-FO element to be shown in the PDF.
   */
  Override
  protected Element createFOElement(Document document, PDFGenerator pdfgen) {
    return createFOElementForChart(chart, document, pdfgen);
  }

  /**
   * Populates the specified toolbar.
   */
  Override
  protected void populateToolBar(JToolBar toolbar) {
    super.populateToolBar(toolbar);
    addPrintingToToolBar(toolbar);
  }

  /**
   * Returns an initialized <code>IlvChart</code>.
   */
  protected IlvChart createChart() {
    IlvChart chart = new IlvChart();
    chart.setAntiAliasing(true);
//    Color[] colors = new Color[] { IlvColor.brighter(BG_COLOR), IlvColor.darker(BG_COLOR) };
    // chart.getChartArea().setPlotBackground(colors, true);
    chart.setBorder(BorderFactory.createEtchedBorder());
    // The x series being years, set the scale as TIME_SCALE.
    chart.getXScale().setTimeUnit(null);
    // Avoid having overlapping labels.
    chart.getXScale().setSkippingLabel(true);
    chart.getXGrid().setMajorPaint(chart.getForeground());
    chart.getYGrid(0).setMajorPaint(chart.getForeground());

    // Initialize the chart title and scale titles.
    // The header is a JComponent that can be added at the top of the chart.
    // As a convenience, the setHeaderText method offers an easy way to
    // have just a label as header: a JLabel initialized with the specified
    // string is automatically created and added to the chart.
    chart.setHeaderText("U.S. Average length of Haul for Domestic Interstate Freight");
    // Set the scales title (the y title being rotated by -90 deg).
    chart.getXScale().setTitle("Years", 0);
    chart.getYScale(0).setTitle("Ton-miles per year", -90);

    // Add a check box to enable/disable editing along the x-axis.
    // When it is selected, the x editing mode of the EditPoint interactor
    // is enabled, thereby allowing the user to modify the abscissa of a point.
    // By default, this mode is disabled.
    // This check box is added as the footer.
    JCheckBox box = new JCheckBox("X editing allowed");
    box.setHorizontalAlignment(SwingConstants.CENTER);
    box.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent evt) {
        editInter.setXEditAllowed(((JCheckBox) evt.getSource()).isSelected());
      }
    });
    chart.setFooter(box);

    return chart;
  }

  /**
   * Returns a swing table model filled with the data to bind. The series should
   * be ordered by column, the x series being the first one (index 0).
   */
  protected AbstractTableModel createSwingTableModel() throws ParseException, NumberFormatException {
    String[] names = { "Year", "Air", "Oil Pipeline", "Rail", "Intercity Truck", "Coastal", "Inland Lake", "Internal",
        "Intraport" };
    SimpleDateFormat format = new SimpleDateFormat("yyyy");
    Object[][] data = {
        { format.parse("1960"), new Double(553), new Double(233000), new Double(572309), new Double(285000),
            new Double(256000), new Double(65990), new Double(89614), new Double(1730) },
        { format.parse("1965"), new Double(1353), new Double(306393), new Double(697878), new Double(359000),
            new Double(302546), new Double(75918), new Double(109701), new Double(1638) },
        { format.parse("1970"), new Double(2189), new Double(431000), new Double(764809), new Double(412000),
            new Double(359784), new Double(79416), new Double(155816), new Double(1179) },
        { format.parse("1975"), new Double(3470), new Double(507000), new Double(754252), new Double(454000),
            new Double(315846), new Double(68517), new Double(180399), new Double(1222) },
        { format.parse("1980"), new Double(4528), new Double(588200), new Double(918958), new Double(555000),
            new Double(631149), new Double(61747), new Double(227343), new Double(1596) },
        { format.parse("1985"), new Double(5156), new Double(564300), new Double(876984), new Double(610000),
            new Double(610977), new Double(48184), new Double(232708), new Double(1102) },
        { format.parse("1990"), new Double(9064), new Double(584100), new Double(1033875), new Double(735000),
            new Double(479134), new Double(60930), new Double(292393), new Double(1087) },
        { format.parse("1991"), new Double(8858), new Double(578500), new Double(1038875), new Double(758000),
            new Double(502133), new Double(55339), new Double(289959), new Double(968) },
        { format.parse("1992"), new Double(9820), new Double(588800), new Double(1066781), new Double(815000),
            new Double(502311), new Double(55785), new Double(297639), new Double(950) },
        { format.parse("1993"), new Double(10675), new Double(592900), new Double(1109309), new Double(861000),
            new Double(448404), new Double(56438), new Double(283894), new Double(922) },
        { format.parse("1994"), new Double(11688), new Double(608000), new Double(1200701), new Double(908000),
            new Double(457601), new Double(58263), new Double(297762), new Double(1293) } };

    DefaultTableModel model = new DefaultTableModel(data, names) {

      // The getColumnClass should be overridden to specify the type
      // of our data. Since a data set only contains double values,
      // this method is used by the IlvSwingTableDataSource to
      // find the right converter to use to convert data of a given
      // column into a <code>double</code> value. By default, converters
      // exist for Date and Double. You can specify your own converter
      // by subclassing <code>IlvDataConverter</code> and registering
      // it invoking IlvSwingTableDataSource.setDefaultConverter().
      Override
      public Class<?> getColumnClass(int col) {
        return (col == 0) ? Date.class : Double.class;
      }

      Override
      public boolean isCellEditable(int rowIndex, int columnIndex) {
        return true;
      }
    };
    return model;
  }

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      Override
      public void run() {
        JFrame frame = new JFrame("Table model sample");
        TableModelDemo sample = new TableModelDemo();
        sample.init(frame.getContentPane());
        sample.setFrameGeometry(700, 540, true);
        frame.setVisible(true);
      }
    });
  }

}