package nxm.sys.libg;

import java.awt.Color;
import java.awt.Component;
import java.util.EventObject;

import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.CellEditorListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

import nxm.sys.inc.InternalUseOnly;
import nxm.sys.lib.Convert;
import nxm.sys.lib.Midas;
import nxm.sys.lib.StringUtil;
import nxm.sys.lib.Table;
import nxm.sys.libg.MJPopup.HasValue;

/** Configuration panel for LIST2 templates. <b>This class is only for use by {@link MJList} and LIST2;
    and is subject to change/removal without notice.</b>
    @author  J. Czechowski IV
    @since   NeXtMidas 2.5.0
 */
@InternalUseOnly("since initial version")
@SuppressWarnings({"rawtypes","serial","unchecked"}) // <-- NOTE: JDK7 has some unchecked rawtypes & Xlint warnings that CANNOT be fixed since we still support JDK6
public class MJListConfig extends javax.swing.JPanel implements HasValue {
  /** Midas context being used (null if not available) */ private final Midas              midas;
  /** The associated MJList (required)                 */ private final MJList             mjlist;
  /** Template Table Model                             */ private final TemplateTableModel templTableModel;
  /** Global Layout Table                              */ private final Table              globalLayout;
  /** Column Layout Table                              */ private final Table              columnLayout;
  /** Template being edited                            */ private       Table              template;
  /** Saved copy of template (used when restoring)     */ private       Table              saveTemplate;

  /** The associated LIST2 (null if not available). in 3.5.4, switched to GPrimitive type
      to reduce coupling from sys.libg to sys.prim package
   */
  private final GPrimitive list2;

  /** Creates new form MJListConfig
   *  @param midas  Midas instance
   *  @param list2  The associated LIST2 (null if not available)
   *  @param mjlist The associated MJList (required)
   */
  public MJListConfig(Midas midas, GPrimitive list2, MJList mjlist) {
    Table tbl = Convert.o2t("nxm.sys.libg.MJListConfig.tbl", midas);

    this.midas           = midas;
    this.list2           = list2;
    this.mjlist          = mjlist;
    this.globalLayout    = tbl.getTable("GLOBAL");
    this.columnLayout    = tbl.getTable("COLUMN");
    this.templTableModel = new TemplateTableModel();
    this.saveTemplate    = mjlist.getTemplate();
    this.template        = mjlist.getTemplate();

    initComponents();

    // The default row height is rarely large enough for the combo box used to edit choices
    JComboBox comboBox = (new JComboBox());
    int height = Math.max(templateTable.getRowHeight(), comboBox.getPreferredSize().height);
    templateTable.setRowHeight(height);


    templateTable.setModel(templTableModel);
    templateTable.getColumnModel().getColumn(1).setCellRenderer(templTableModel);
    templateTable.getColumnModel().getColumn(1).setCellEditor(templTableModel);
    columnSelector.setModel(new DefaultComboBoxModel(template.getKeys()));
    showTemplate("_GLOBAL_");
  }

  public String getName ()  { return "MJList"; } // Implements HasValue
  public Object getValue () { return null;     } // Implements HasValue
  public int    getInfo ()  { return 0;        } // Implements HasValue

  /** Refresh the local copy of the template, discarding any changes. */
  private void refreshTemplate () {
    template = mjlist.getTemplate();
    showTemplate(columnSelector.getSelectedItem().toString());
  }

  /** Update the template editor to show a given column or "_GLOBAL_". */
  private void showTemplate (String col) {
    if (col.equals("_GLOBAL_")) {
      templTableModel.setLayout(globalLayout);
    } else {
      templTableModel.setLayout(columnLayout);
    }
    templTableModel.setValues(template.getTable(col));
    // JPH: Bug 1493 fix to make sure default choice is correct
    columnSelector.setSelectedItem(col);
  }

  /** Template Table Model */
  private class TemplateTableModel extends AbstractTableModel implements TableCellRenderer, TableCellEditor {
    private final Icon SELECTED   = new Symbol.CheckBox(true);
    private final Icon DESELECTED = new Symbol.CheckBox(false);

    /** Cell renderer                                               */ private DefaultTableCellRenderer cellRenderer;
    /** Cell editor for TOGGLE values (only one of these)           */ private DefaultCellEditor        toggleEditor;
    /** Cell editor for  TEXT  values (only one of these)           */ private DefaultCellEditor        textEditor;
    /** Current cell editor                                         */ private TableCellEditor          cellEditor;
    /** Current row names (for performance reasons, matches layout) */ private String[]                 rowNames;
    /** The layout table                                            */ private Table                    layout;
    /** The table of values                                         */ private Table                    values;

    /** Creates a new instance with null layout and values. */
    public TemplateTableModel () {
      this(null,null);
    }

    /** Creates a new instance.
        @param layout The layout table.
        @param values The table of values.
     */
    public TemplateTableModel (Table layout, Table values) {
      JCheckBox checkBox = new JCheckBox();
      checkBox.setSelectedIcon(SELECTED);
      checkBox.setDisabledIcon(DESELECTED);

      this.cellRenderer = new DefaultTableCellRenderer();
      this.toggleEditor = new DefaultCellEditor(checkBox);
      this.textEditor   = new DefaultCellEditor(new JTextField());
      this.cellEditor   = toggleEditor;

      setLayout(layout);
      setValues(values);
    }

    /** Gets the layout table. */
    public Table getLayout () {
      return layout;
    }

    /** Sets the layout table. */
    public void setLayout (Table layout) {
      if (layout == null) { layout = new Table(); }
      if (this.layout != layout) {
        this.layout   = layout;
        this.rowNames = layout.getKeys();
        fireTableDataChanged();
      }
    }

    /** Gets the table of values. */
    public Table getValues () {
      return values;
    }

    /** Sets the table of values. */
    public void setValues (Table values) {
      if (this.values != values) {
        this.values = values;
        fireTableDataChanged();
      }
    }

    private Table rowLayout (int row) {
      Table rowLayout = layout.getTable(rowNames[row]);
      return (rowLayout == null) ? new Table() : rowLayout;
    }

    public int     getRowCount ()        { return rowNames.length; }
    public int     getColumnCount ()     { return 2; }
    public String  getColumnName (int c) { return (c == 0) ? "TAG" : "VALUE"; }

    public boolean isCellEditable (int row, int col) {
      return (col == 1) && rowLayout(row).getState("EDITABLE",true);
    }

    public Object getValueAt (int row, int col) {
      Object val = null;

      if (col == 0) {
        val = rowLayout(row).getO("LABEL");
        if (val == null) {
          val = rowNames[row];
        }
      } else {
        if (values != null) {
          val = values.getO(rowNames[row]);
        }
        if (val == null) {
          val = rowLayout(row).getO("DEFVALUE");
        }
      }
      return val;
    }

    public void setValueAt (Object val, int row, int col) {
      if (col == 0) {
        // Not editable
      } else {
        if (values == null) { values = new Table(); }
        values.put(rowNames[row],val);
        fireTableCellUpdated(row,col);
      }
    }

    public Component getTableCellRendererComponent (JTable table, Object value, boolean isSelected,
                                                    boolean hasFocus, int row, int col) {
      Component comp = cellRenderer.getTableCellRendererComponent(table,value,isSelected,hasFocus,row,col);

      if (comp instanceof DefaultTableCellRenderer) { // Should always be true
        DefaultTableCellRenderer dtcr = (DefaultTableCellRenderer)comp;
        String type = rowLayout(row).getS("TYPE");

        dtcr.setIcon(null);
        dtcr.setBackground(table.getBackground());
        dtcr.setForeground(table.getForeground());

        if (type == null) {
          // do nothing
        } else if (type.equals("TOGGLE")) {
          if ((value != null) && StringUtil.isTrue(value.toString())) {
            dtcr.setIcon(SELECTED);
          } else {
            dtcr.setIcon(DESELECTED);
          }
        } else if (type.equals("COLOR") && (value != null) && (value.toString().length() > 0)) {
          Color color = MColor.getColor(value);
          dtcr.setBackground(color);
          dtcr.setForeground(new Color(~color.getRGB(),false));
        }
      }
      return comp;
    }

    public Object  getCellEditorValue ()                        { return cellEditor.getCellEditorValue();  }
    public boolean isCellEditable (EventObject e)               { return true; }
    public boolean shouldSelectCell (EventObject e)             { return true; }
    public boolean stopCellEditing ()                           { return cellEditor.stopCellEditing(); }
    public void cancelCellEditing ()                            { cellEditor.cancelCellEditing(); }
    public void addCellEditorListener (CellEditorListener l)    { cellEditor.addCellEditorListener(l); }
    public void removeCellEditorListener (CellEditorListener l) { cellEditor.removeCellEditorListener(l); }

    public Component getTableCellEditorComponent (JTable table, Object value, boolean isSelected,
                                                  int row, int col) {
      String type = rowLayout(row).getS("TYPE");
      if ((type == null) || type.equals("TEXT")) {
        cellEditor = textEditor;
      } else if (type.equals("TOGGLE")) {
        cellEditor = toggleEditor;
      } else if (type.startsWith("CHOICE")) {
        String[] choices = rowLayout(row).getS("CHOICES","").split("[,|]");

        JComboBox comboBox = new JComboBox(choices);

        if (type.indexOf("/E") > 0) {
          comboBox.setEditable(true);
        }
        cellEditor = new DefaultCellEditor(comboBox);
      } else {
        cellEditor = textEditor;
      }

      return cellEditor.getTableCellEditorComponent(table,value,isSelected,row,col);
    }
  }

  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    jPanel2 = new javax.swing.JPanel();
    jLabel1 = new javax.swing.JLabel();
    columnSelector = new javax.swing.JComboBox();
    jScrollPane1 = new javax.swing.JScrollPane();
    templateTable = new javax.swing.JTable();
    applyChanges = new javax.swing.JButton();
    resetChanges = new javax.swing.JButton();
    jPanel3 = new javax.swing.JPanel();

    jLabel1.setText("Show Template For:");

    columnSelector.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
    columnSelector.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        columnSelectorActionPerformed(evt);
      }
    });

    templateTable.setModel(new javax.swing.table.DefaultTableModel(
      new Object [][] {
        {null, null, null, null},
        {null, null, null, null},
        {null, null, null, null},
        {null, null, null, null}
      },
      new String [] {
        "Title 1", "Title 2", "Title 3", "Title 4"
      }
    ));
    templateTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_ALL_COLUMNS);
    templateTable.setRowSelectionAllowed(false);
    jScrollPane1.setViewportView(templateTable);

    applyChanges.setText("Apply");
    applyChanges.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        applyChangesActionPerformed(evt);
      }
    });

    resetChanges.setText("Reset");
    resetChanges.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        resetChangesActionPerformed(evt);
      }
    });

    jPanel3.setLayout(new java.awt.GridLayout(1, 0, 4, 0));

    javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
    jPanel2.setLayout(jPanel2Layout);
    jPanel2Layout.setHorizontalGroup(
      jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
          .addGroup(jPanel2Layout.createSequentialGroup()
            .addGap(24, 24, 24)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 379, Short.MAX_VALUE))
          .addGroup(jPanel2Layout.createSequentialGroup()
            .addComponent(resetChanges)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(applyChanges))
          .addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel2Layout.createSequentialGroup()
            .addComponent(jLabel1)
            .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addGroup(jPanel2Layout.createSequentialGroup()
                .addGap(172, 172, 172)
                .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, 105, Short.MAX_VALUE))
              .addGroup(jPanel2Layout.createSequentialGroup()
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(columnSelector, 0, 265, Short.MAX_VALUE)))))
        .addContainerGap())
    );
    jPanel2Layout.setVerticalGroup(
      jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(jPanel2Layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
            .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addComponent(columnSelector, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 262, Short.MAX_VALUE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(applyChanges)
          .addComponent(resetChanges))
        .addContainerGap())
    );

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
    this.setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    );
  }// </editor-fold>//GEN-END:initComponents

  private void resetChangesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resetChangesActionPerformed
    mjlist.setTemplate(saveTemplate);
    refreshTemplate();
  }//GEN-LAST:event_resetChangesActionPerformed

  private void applyChangesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_applyChangesActionPerformed
    mjlist.setTemplate(template);
    refreshTemplate();
  }//GEN-LAST:event_applyChangesActionPerformed

  private void columnSelectorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_columnSelectorActionPerformed
    showTemplate(columnSelector.getSelectedItem().toString());
  }//GEN-LAST:event_columnSelectorActionPerformed


  // Variables declaration - do not modify//GEN-BEGIN:variables
  /** apply changes button */  private javax.swing.JButton applyChanges;
  /** column selector combo */ private javax.swing.JComboBox columnSelector;
  /** label */                 private javax.swing.JLabel jLabel1;
  /** outer panel */           private javax.swing.JPanel jPanel2;
  /** inner panel */           private javax.swing.JPanel jPanel3;
  /** the scroll pane */       private javax.swing.JScrollPane jScrollPane1;
  /** rest changes button */   private javax.swing.JButton resetChanges;
  /** template table */        private javax.swing.JTable templateTable;
  // End of variables declaration//GEN-END:variables

}
