/*
 * jNPad v0.3 - jNPad's an Simple Text Editor written in Java
 *
 * Copyright (C) 2014-2017  rgs
 *
 * Require JDK 1.6 (or later)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 *
 * Info, Questions, Suggestions & Bugs Report to rgsevero@gmail.com
 */

package jnpad.ui.table;

import java.util.Enumeration;
import java.util.Vector;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

import jnpad.ui.ColorUtilities;
import jnpad.ui.plaf.LAFUtils;

/**
 * The Class JNPadTable.
 *
 * @version 0.3
 * @since   jNPad v0.1
 */
public class JNPadTable extends JTable {
  private int               sortedColumnIndex       = -1;
  private boolean           isSortedColumnAscending = true;
  private boolean           isSortingEnabled        = true;
  private boolean           isEditable              = true;
  private boolean           isRowHeaderEnabled      = false;
  private DefaultRowHeader  rowHeader;
  private int               rowHeaderWidth          = 20;
  private int               rowHeaderSelected       = -1;

  /** UID */
  private static final long serialVersionUID        = 1803896768020595693L;

  /**
   * Instantiates a new jNPad table.
   */
  public JNPadTable() {
    this(new JNPadDefaultTableModel(), false, SwingConstants.CENTER);
  }

  /**
   * Instantiates a new jNPad table.
   *
   * @param numRows int
   * @param numColumns int
   */
  public JNPadTable(int numRows, int numColumns) {
    this(new JNPadDefaultTableModel(numRows, numColumns), false, SwingConstants.CENTER);
  }

  /**
   * Instantiates a new jNPad table.
   *
   * @param rowData Object[][]
   * @param columnNames Object[]
   */
  public JNPadTable(Object[][] rowData, Object[] columnNames) {
    this(new JNPadDefaultTableModel(rowData, columnNames), false, SwingConstants.CENTER);
  }

  /**
   * Instantiates a new jNPad table.
   *
   * @param rowData Vector
   * @param columnNames Vector
   */
  public JNPadTable(Vector<?> rowData, Vector<?> columnNames) {
    this(new JNPadDefaultTableModel(rowData, columnNames), false, SwingConstants.CENTER);
  }

  /**
   * Instantiates a new jNPad table.
   *
   * @param tm TableModel
   */
  public JNPadTable(TableModel tm) {
    this(tm, false, SwingConstants.CENTER);
  }

  /**
   * Instantiates a new jNPad table.
   *
   * @param tm TableModel
   * @param multiLineHeader boolean
   */
  public JNPadTable(TableModel tm, boolean multiLineHeader) {
    super(tm);
    if (multiLineHeader)
      init(true, MultiLineHeaderTableCellRenderer.MIDDLE);
    else
      init(false, SwingConstants.CENTER);
  }

  /**
   * Instantiates a new jNPad table.
   *
   * @param tm TableModel
   * @param multiLineHeader boolean
   * @param align int
   */
  public JNPadTable(TableModel tm, boolean multiLineHeader, int align) {
    super(tm);
    init(multiLineHeader, align);
  }

  /**
   * Instantiates a new jNPad table.
   *
   * @param tm TableModel
   * @param tcm TableColumnModel
   */
  public JNPadTable(TableModel tm, TableColumnModel tcm) {
    super(tm, tcm);
    init(false, SwingConstants.CENTER);
  }

  /**
   * Instantiates a new jNPad table.
   *
   * @param tm TableModel
   * @param tcm TableColumnModel
   * @param lsm ListSelectionModel
   */
  public JNPadTable(TableModel tm, TableColumnModel tcm, ListSelectionModel lsm) {
    super(tm, tcm, lsm);
    init(false, SwingConstants.CENTER);
  }

  /**
   * Inits the.
   *
   * @param multiLineHeader boolean
   * @param align int
   */
  private void init(boolean multiLineHeader, int align) {
    TableCellRenderer renderer;
    if(multiLineHeader)
      renderer = new MultiLineHeaderTableCellRenderer(align);
    else
      renderer = new LineHeaderTableCellRenderer(align);

    tableHeader.setDefaultRenderer(renderer);
    for (Enumeration<TableColumn> e = columnModel.getColumns(); e.hasMoreElements(); ) {
      TableColumn tableColumn = e.nextElement();
      tableColumn.setHeaderRenderer(renderer);
    }
    tableHeader.addMouseListener(new MouseHandler());
  }

  /**
   * Sets the sorting enabled.
   *
   * @param b the new sorting enabled
   */
  public void setSortingEnabled(boolean b) {
    isSortingEnabled = b;
  }

  /**
   * Checks if is sorting enabled.
   *
   * @return true, if is sorting enabled
   */
  public boolean isSortingEnabled() {return isSortingEnabled;}

  /**
   * Gets the sorted column index.
   *
   * @return the sorted column index
   */
  public int getSortedColumnIndex() {return sortedColumnIndex;}

  /**
   * Sets the sorted column index.
   *
   * @param column the new sorted column index
   */
  public void setSortedColumnIndex(int column) {
    sortedColumnIndex = column;
  }

  /**
   * Checks if is sorted column ascending.
   *
   * @return true, if is sorted column ascending
   */
  public boolean isSortedColumnAscending() {return isSortedColumnAscending;}

  /**
   * Sets the sorted column ascending.
   *
   * @param b the new sorted column ascending
   */
  public void setSortedColumnAscending(boolean b) {
    isSortedColumnAscending = b;
  }

  /**
   * Sets the editable.
   *
   * @param b the new editable
   */
  public void setEditable(boolean b) {
    this.isEditable = b;
  }

  /**
   * Checks if is editable.
   *
   * @return true, if is editable
   */
  public boolean isEditable() {return isEditable;}

  /**
   * Checks if is cell editable.
   *
   * @param row the row
   * @param column the column
   * @return true, if is cell editable
   * @see javax.swing.JTable#isCellEditable(int, int)
   */
  @Override
  public boolean isCellEditable(int row, int column) {
    return isEditable && super.isCellEditable(row, column);
  }

  /**
   * Sets the column preferred width.
   *
   * @param width the new column preferred width
   */
  public void setColumnPreferredWidth(int width) {
    int max = columnModel.getColumnCount();
    for (int i = 0; i < max; i++) {
      TableColumn tableColumn = columnModel.getColumn(i);
      tableColumn.setPreferredWidth(width);
    }
  }

  /**
   * Sets the row header selected.
   *
   * @param row the new row header selected
   */
  public void setRowHeaderSelected(int row) {
    if (row != rowHeaderSelected) {
      rowHeaderSelected = row;
      if (rowHeader != null)
        rowHeader.repaint();
    }
  }

  /**
   * Gets the row header selected.
   *
   * @return int
   */
  public int getRowHeaderSelected() {return rowHeaderSelected;}

  /**
   * Sets the row header enabled.
   *
   * @param b boolean
   */
  public void setRowHeaderEnabled(boolean b) {
    if (b != isRowHeaderEnabled) {
      isRowHeaderEnabled = b;
      setRowHeaderView();
    }
  }

  /**
   * Gets the row header enabled.
   *
   * @return boolean
   */
  public boolean getRowHeaderEnabled() {return isRowHeaderEnabled;}

  /**
   * Gets the row header.
   *
   * @return Component
   */
  public Component getRowHeader() {
    if (!isRowHeaderEnabled)
      return null;
    if (rowHeader == null)
      setRowHeaderView();
    return rowHeader;
  }

  /**
   * Sets the row header width.
   *
   * @param width int
   */
  public void setRowHeaderWidth(int width) {
    rowHeaderWidth = width;
    if (rowHeader != null)
      rowHeader.setFixedCellWidth(width);
  }

  /**
   * Gets the row header width.
   *
   * @return int
   */
  public int getRowHeaderWidth() {return rowHeaderWidth;}


  /**
   * Sets the row height.
   *
   * @param rowHeight the new row height
   * @see javax.swing.JTable#setRowHeight(int)
   */
  @Override
  public void setRowHeight(int rowHeight) {
    super.setRowHeight(rowHeight);
    if (rowHeader != null) {
      rowHeader.setFixedCellHeight(getRowHeight()
      //                             + getRowMargin()
      //                             + getIntercellSpacing().height
                                   );
    }
  }

  /**
   * Configure enclosing scroll pane.
   *
   * @see javax.swing.JTable#configureEnclosingScrollPane()
   */
  @Override
  protected void configureEnclosingScrollPane() {
    super.configureEnclosingScrollPane();
    setRowHeaderView();
  }

  /**
   * Sets the row header view.
   */
  private void setRowHeaderView() {
    Container c1 = getParent();
    if (! (c1 instanceof JViewport))
      return;
    Container c2 = c1.getParent();
    if (! (c2 instanceof JScrollPane))
      return;
    JScrollPane scrollPane = (JScrollPane) c2;
    JViewport viewport = scrollPane.getViewport();
    if (viewport == null || viewport.getView() != this)
      return;
    if (isRowHeaderEnabled) {
      if (rowHeader == null) {
        rowHeader = new DefaultRowHeader(this);
        Color bg = scrollPane.getBackground();
        if (LAFUtils.isNimbusLAF()) // 4Nimbus
          bg = ColorUtilities.createPureColor(bg);
        rowHeader.setBackground(bg);
        scrollPane.setRowHeaderView(rowHeader);
      }
      scrollPane.setRowHeaderView(rowHeader);
    }
    else if (rowHeader != null) {
      scrollPane.setRowHeaderView(null);
    }
  }

  /**
   * Table changed.
   *
   * @param e the TableModelEvent
   * @see javax.swing.JTable#tableChanged(javax.swing.event.TableModelEvent)
   */
  @Override
  public void tableChanged(TableModelEvent e) {
    super.tableChanged(e);
    if (rowHeader != null)
      rowHeader.repaint();
  }

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class MouseHandler.
   */
  private class MouseHandler extends MouseAdapter {
    /**
     * Mouse released.
     *
     * @param e the MouseEvent
     * @see java.awt.event.MouseAdapter#mouseReleased(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseReleased(MouseEvent e) {
      try {
        if (!isSortingEnabled)
          return;
        TableModel tableModel = getModel();
        if (! (tableModel instanceof JNPadTableModel))
          return;

        TableColumnModel tableColumnModel = getColumnModel();
        int vColumnIndex = tableColumnModel.getColumnIndexAtX(e.getX());
        int mColumnIndex = tableColumnModel.getColumn(vColumnIndex).getModelIndex();

        if (! ( (JNPadTableModel) tableModel).isSortable(mColumnIndex))
          return;

        if (sortedColumnIndex == vColumnIndex) {
          isSortedColumnAscending = !isSortedColumnAscending;
        }
        sortedColumnIndex = vColumnIndex;
        ( (JNPadTableModel) tableModel).setSortByColumn(mColumnIndex, isSortedColumnAscending);

        tableHeader.repaint(); // va?
      }
      catch (Exception ex) {
        //ignored
      }
    }
  }
  //////////////////////////////////////////////////////////////////////////////

} // fin de JNPadTable

