/*
 * 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;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;

import jnpad.config.Config;
import jnpad.text.IBuffer;
import jnpad.text.Scheme;
import jnpad.ui.EscapableDialog;
import jnpad.ui.icon.CompositeIcon;
import jnpad.ui.tab.ITabbedPane;
import jnpad.ui.table.JNPadTable;
import jnpad.ui.table.LabelTableCellRenderer;
import jnpad.util.Utilities;

/**
 * The Class WindowDialog.
 *
 * @version 0.3
 * @since   jNPad v0.1
 */
public class WindowDialog extends EscapableDialog {
  JPanel                        contentPane;
  JPanel                        pnBackground          = new JPanel();
  JScrollPane                   scrollPane            = new JScrollPane();
  JPanel                        pnButtons             = new JPanel();
  JPanel                        pnButtonsGroup        = new JPanel();
  JButton                       btActivate            = new JButton();
  JButton                       btSave                = new JButton();
  JButton                       btClose               = new JButton();
  JButton                       btMove                = new JButton();
  JButton                       btSort                = new JButton();
  JButton                       btOk                  = new JButton();

  private static final int      COLUMN_NAME           = 0,
                                COLUMN_PATH           = 1,
                                COLUMN_TYPE           = 2;

  private static final String[] COLUMN_NAMES = {
	JNPadBundle.getString("WindowDialog.column.name"), //$NON-NLS-1$
	JNPadBundle.getString("WindowDialog.column.path"), //$NON-NLS-1$
	JNPadBundle.getString("WindowDialog.column.type")  //$NON-NLS-1$
  };

  DefaultTableModel tableModel = new DefaultTableModel() {
    /** UID */
    private static final long serialVersionUID = -2339075884909205886L;

    /**
     * Checks if is cell editable.
     *
     * @param row the row
     * @param col the col
     * @return true, if is cell editable
     * @see javax.swing.table.DefaultTableModel#isCellEditable(int, int)
     */
    @Override
    public boolean isCellEditable(int row, int col) {
      return false;
    }
  };

  JNPadTable                    table                 = new JNPadTable(tableModel);

  private JNPadFrame            jNPad;
  private ITabbedPane           tabbedPane;
  private boolean               isEditable;
  private boolean               isValueChangedEnabled = true;

  /** Logger */
  private final static Logger   LOGGER                = Logger.getLogger(WindowDialog.class.getName());

  /** UID */
  private static final long     serialVersionUID      = -3473470406892731188L;

  /**
   * Instantiates a new window dialog.
   *
   * @param jNPad the jNPad's frame
   * @param tabbedPane the tabbed pane
   * @param isEditable is editable
   */
  public WindowDialog(JNPadFrame jNPad, ITabbedPane tabbedPane, boolean isEditable) {
    super(jNPad, JNPadBundle.getString("WindowDialog.title"), true); //$NON-NLS-1$
    try {
      this.jNPad      = jNPad;
      this.tabbedPane = tabbedPane;
      this.isEditable = isEditable;

      jbInit();
      
      pack();
      setLocationRelativeTo(jNPad);
      setVisible(true);
    }
    catch (Exception ex) {
      LOGGER.log(Level.WARNING, ex.getMessage(), ex);
    }
  }

  /**
   * Component initialization.
   *
   * @throws Exception the exception
   */
  private void jbInit() throws Exception {
    contentPane = (JPanel)this.getContentPane();
    contentPane.setLayout(new BorderLayout());
    contentPane.add(pnBackground, BorderLayout.CENTER);
    contentPane.add(pnButtons, BorderLayout.EAST);

    ActionListener actionHandler = new ActionHandler();

    pnButtons.setLayout(new BorderLayout());
    pnButtonsGroup.setLayout(new GridLayout(0, 1, 0, 5));
    pnButtons.setBorder(GUIUtilities.createEmptyBorder(5));
    pnBackground.setBorder(BorderFactory.createEmptyBorder(5, 5, 20, 5));
    pnBackground.setLayout(new BorderLayout());
    pnButtons.add(pnButtonsGroup, BorderLayout.NORTH);
    pnButtonsGroup.add(btActivate, null);
    pnButtonsGroup.add(btSave, null);
    pnButtonsGroup.add(btClose, null);
    pnButtonsGroup.add(btMove, null);
    pnButtonsGroup.add(btSort, null);
    pnButtons.add(btOk, BorderLayout.SOUTH);

    GUIUtilities.setLocalizedText(btOk      , JNPadBundle.getOkButtonText());
    GUIUtilities.setLocalizedText(btActivate, JNPadBundle.getActivateButtonText());
    GUIUtilities.setLocalizedText(btSave    , JNPadBundle.getSaveButtonText());
    GUIUtilities.setLocalizedText(btClose   , JNPadBundle.getCloseButtonText());
    GUIUtilities.setLocalizedText(btMove    , JNPadBundle.getMoveButtonText());
    GUIUtilities.setLocalizedText(btSort    , JNPadBundle.getSortButtonText());

    btActivate.addActionListener(actionHandler);
    btSave.addActionListener(actionHandler);
    btClose.addActionListener(actionHandler);
    btMove.addActionListener(actionHandler);
    btSort.addActionListener(actionHandler);
    btOk.addActionListener(actionHandler);

    pnBackground.add(scrollPane, BorderLayout.CENTER);
    scrollPane.getViewport().add(table, null);

    table.setSortingEnabled(false);
    table.setRowHeaderEnabled(true);
    table.setRowHeaderSelected(tabbedPane.getSelectedIndex());

    table.getSelectionModel().addListSelectionListener(new ListSelectionHandler());

    //table.getTableHeader().setDefaultRenderer(new JNPadDefaultHeaderTableCellRenderer());

    table.getTableHeader().setReorderingAllowed(false);

    table.setPreferredScrollableViewportSize(new Dimension(600, 300));
    //table.setFont(Config.TEXT_FONT.getValue());
    table.setGridColor(Config.TEXT_RIGHT_MARGIN_LINE_COLOR.getValue());
    table.setForeground(Config.TEXT_FOREGROUND.getValue());
    table.setBackground(Config.TEXT_BACKGROUND.getValue());
    table.setSelectionForeground(Config.TEXT_SELECTION_FOREGROUND.getValue());
    table.setSelectionBackground(Config.TEXT_SELECTION_BACKGROUND.getValue());

    int tabCount = tabbedPane.getTabCount();

    Object[][] data = new Object[tabCount][COLUMN_NAMES.length];
    for (int i = 0; i < tabCount; i++) {
      Component c = tabbedPane.getComponentAt(i);
      data[i][COLUMN_NAME] = getName(c, i);
      data[i][COLUMN_PATH] = getPath(c);
      data[i][COLUMN_TYPE] = getType(c);
    }
    tableModel.setDataVector(data, COLUMN_NAMES);
    tableModel.getDataVector();

    table.getColumnModel().getColumn(COLUMN_NAME).setCellRenderer(new NameTableCellRenderer());
    table.getColumnModel().getColumn(COLUMN_NAME).setPreferredWidth(200);
    table.getColumnModel().getColumn(COLUMN_PATH).setCellRenderer(new LabelTableCellRenderer(true));
    table.getColumnModel().getColumn(COLUMN_PATH).setPreferredWidth(300);
    table.getColumnModel().getColumn(COLUMN_TYPE).setCellRenderer(new LabelTableCellRenderer(false));
    table.getColumnModel().getColumn(COLUMN_TYPE).setPreferredWidth(100);

    int selectedIndex = tabbedPane.getSelectedIndex();
    table.setRowSelectionInterval(selectedIndex, selectedIndex);

    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
  }

  /**
   * Escape pressed.
   *
   * @see jnpad.ui.EscapableDialog#escapePressed()
   */
  @Override
  protected void escapePressed() {
    dispose();
  }
  
  /**
   * Reload.
   */
  private void reload() {
    isValueChangedEnabled = false;

    tableModel.getDataVector().clear();

    for (int i = 0; i < tabbedPane.getTabCount(); i++) {
      Component c = tabbedPane.getComponentAt(i);
      Object[] rowData = new Object[COLUMN_NAMES.length];
      rowData[COLUMN_NAME] = getName(c, i);
      rowData[COLUMN_PATH] = getPath(c);
      rowData[COLUMN_TYPE] = getType(c);
      tableModel.addRow(rowData);
    }
    tableModel.fireTableDataChanged();

    isValueChangedEnabled = true;
  }

  /**
   * Do post close.
   */
  private void doPostClose() {
    int[] selectedRows = table.getSelectedRows();

    reload();

    int rowCount = table.getRowCount();

    if (selectedRows.length > 0) {
      ListSelectionModel selectionModel = table.getSelectionModel();

      int selectedRow = selectedRows[0];
      if (selectedRow < rowCount) {
        selectionModel.setSelectionInterval(selectedRow, selectedRow);
      }
      else if (rowCount > 0) {
        selectionModel.setSelectionInterval(rowCount - 1, rowCount - 1);
      }
    }

    if (rowCount == 0) {
      dispose();
    }
  }

  /**
   * Do post save.
   */
  private void doPostSave() {
    int[] selectedRows = table.getSelectedRows();

    reload();

    if (selectedRows.length > 0) {
      ListSelectionModel selectionModel = table.getSelectionModel();
      for (int selectedRow : selectedRows) {
        selectionModel.addSelectionInterval(selectedRow, selectedRow);
      }
    }
  }

  /**
   * Do post move.
   */
  private void doPostMove() {
    reload();
    int selectedIndex = tabbedPane.getSelectedIndex();
    table.setRowSelectionInterval(selectedIndex, selectedIndex);
  }
  
  /**
   * Gets the name.
   *
   * @param c the component
   * @param index the index
   * @return the name
   */
  private NameElem getName(Component c, int index) {
    String name;
    Icon icon;
    boolean save;
    boolean selected = tabbedPane.getSelectedIndex() == index;

    if (c instanceof IBuffer) {
      name = tabbedPane.getTitleAt(index);
      if ( ( (IBuffer) c).isDirty()) {
        name += "*"; //$NON-NLS-1$
      }
      icon = tabbedPane.getIconAt(index);
      if (icon instanceof CompositeIcon && ( (CompositeIcon) icon).hasMainIcon()) {
        icon = ( (CompositeIcon) icon).getMainIcon();
      }
      save = isEditable && ! ( (IBuffer) c).isReadOnly() && ( (IBuffer) c).isDirty();
    }
    else {
      name = Utilities.EMPTY_STRING;
      icon = null;
      save = isEditable;
    }
    return new NameElem(name, icon, save, selected);
  }

  /**
   * Gets the path.
   *
   * @param c the component
   * @return the path
   */
  private String getPath(Component c) {
    if (c instanceof IBuffer) {
      return ( (IBuffer) c).getFilePath();
    }
    return Utilities.EMPTY_STRING;
  }

  /**
   * Gets the type.
   *
   * @param c the component
   * @return the type
   */
  private String getType(Component c) {
    if (c instanceof IBuffer) {
      return Scheme.getDisplayName(( (IBuffer) c).getContentType());
    }
    return JNPadBundle.getString("WindowDialog.text"); //$NON-NLS-1$
  }

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class ListSelectionHandler.
   */
  private class ListSelectionHandler implements ListSelectionListener {
    
    /**
     * Value changed.
     *
     * @param e the <code>ListSelectionEvent</code>
     * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
     */
    @Override
    public void valueChanged(ListSelectionEvent e) {
      if (!isValueChangedEnabled) {
        return;
      }

      if (e.getSource() == table.getSelectionModel() && e.getFirstIndex() >= 0) {
        int[] selectedRows = table.getSelectedRows();

        switch (selectedRows.length) {
          case 0:
            btActivate.setEnabled(false);
            btClose.setEnabled(false);
            btSave.setEnabled(false);
            btMove.setEnabled(false);
            btSort.setEnabled(true);
            break;

          case 1:
            btClose.setEnabled(isEditable);
            NameElem nameElem = (NameElem) table.getValueAt(selectedRows[0], COLUMN_NAME);
            btSave.setEnabled(nameElem.save);
            btActivate.setEnabled(!nameElem.selected);
            btMove.setEnabled(true);
            btSort.setEnabled(false);
            break;

          default:
            btActivate.setEnabled(false);
            btMove.setEnabled(false);
            btSort.setEnabled(selectedRows.length == tabbedPane.getTabCount());
            btClose.setEnabled(isEditable);
            boolean b = false;
            for (int selectedRow : selectedRows) {
              NameElem nElem = (NameElem) table.getValueAt(selectedRow, COLUMN_NAME);
              if (nElem.save) {
                b = true;
                break;
              }
            }
            btSave.setEnabled(b);
            break;
        }
      }
    }
  }
  //////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class ActionHandler.
   */
  private class ActionHandler implements ActionListener {
    
    /**
     * Action performed.
     *
     * @param e the <code>ActionEvent</code>
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
     */
    @Override
    public void actionPerformed(ActionEvent e) {
      Object obj = e.getSource();
      if (obj == btOk) {
        dispose();
      }
      else if (obj == btActivate) {
        int selectedRow = table.getSelectedRow();
        if (selectedRow >= 0 && selectedRow < tabbedPane.getTabCount()) {
          tabbedPane.setSelectedIndex(selectedRow);
          dispose();
        }
      }
      else if (obj == btSave && isEditable) {
        int[] selectedRows = table.getSelectedRows();
        boolean b = false;
        for (int selectedRow : selectedRows) {
          String path = (String) table.getValueAt(selectedRow, COLUMN_PATH);
          b = jNPad.saveFile(path) || b;
        }
        if (b) {
          doPostSave();
        }
      }
      else if (obj == btClose && isEditable) {
        int[] selectedRows = table.getSelectedRows();
        boolean b = false;
        for (int selectedRow : selectedRows) {
          String path = (String) table.getValueAt(selectedRow, COLUMN_PATH);
          b = jNPad.closeFile(path, true) || b;
        }
        if (b) {
          doPostClose();
        }
      }
      else if (obj == btMove) {
        int selectedRow = table.getSelectedRow();
        if (selectedRow >= 0 && selectedRow < tabbedPane.getTabCount()) {
          String response = JOptionPane.showInputDialog(WindowDialog.this,
                                                        JNPadBundle.getString("WindowDialog.move.message", tabbedPane.getTabCount()), //$NON-NLS-1$
                                                        JNPadBundle.getString("WindowDialog.move.title"), //$NON-NLS-1$
                                                        JOptionPane.QUESTION_MESSAGE);

          if (Utilities.isBlankString(response)) {
            return;
          }
          
          int tab;
          try {
            tab = Integer.parseInt(response) - 1;
          }
          catch (Exception ex) {
            JOptionPane.showConfirmDialog(WindowDialog.this,
                                          JNPadBundle.getString("WindowDialog.move.error.message", response), //$NON-NLS-1$
                                          JNPadBundle.getString("WindowDialog.move.error.title"), //$NON-NLS-1$
                                          JOptionPane.DEFAULT_OPTION,
                                          JOptionPane.ERROR_MESSAGE);
            return;
          }

          try {
            tabbedPane.setSelectedIndex(selectedRow);
            tabbedPane.moveSelectedTabTo(tab);
            doPostMove();
          }
          catch (Exception ex) {
            LOGGER.log(Level.WARNING, ex.getMessage(), ex);
          }
        }
      }
      else if (obj == btSort) {
        try {
          ArrayList<String> l_titles = new ArrayList<String>();
          ArrayList<Component> l_comps = new ArrayList<Component>();
          for (int i = 0; i < tabbedPane.getTabCount(); i++) {
            l_titles.add(tabbedPane.getTitleAt(i));
            l_comps.add(tabbedPane.getComponentAt(i));
          }

          ArrayList<String> l_aux = new ArrayList<String>(l_titles);

          Collections.sort(l_titles);

          if (!l_aux.equals(l_titles)) {
            Component selectedComponent = tabbedPane.getSelectedComponent();
            for (int i = 0; i < l_comps.size(); i++) {
              for (Component c : l_comps) {
                tabbedPane.setSuppressStateChangedEvents(true);
                tabbedPane.setSelectedComponent(c);
                tabbedPane.setSuppressStateChangedEvents(false);
                tabbedPane.moveSelectedTabTo(l_titles.indexOf(tabbedPane.getTitleAt(tabbedPane.getSelectedIndex())));
              }
            }
            tabbedPane.setSelectedComponent(selectedComponent);
          }
          doPostMove();
        }
        catch (Exception ex) {
          LOGGER.log(Level.WARNING, ex.getMessage(), ex);
        }
      }
    }
  }
  //////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class NameElem.
   */
  static class NameElem {
    private String  name;
    private Icon    icon;
    private boolean save;
    private boolean selected;

    /**
     * Instantiates a new name elem.
     *
     * @param name the name
     * @param icon the icon
     * @param save the save
     * @param selected the selected
     */
    NameElem(String name, Icon icon, boolean save, boolean selected) {
      this.name = name;
      this.icon = icon;
      this.save = save;
      this.selected = selected;
    }

    /**
     * To string.
     *
     * @return the string
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {return name;}
  }
  //////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class NameTableCellRenderer.
   */
  static class NameTableCellRenderer extends LabelTableCellRenderer {
    /** UID */
    private static final long serialVersionUID = -3676231760961001044L;

    /**
     * Instantiates a new name table cell renderer.
     */
    NameTableCellRenderer() {
      super(false);
    }

    /**
     * Sets the state.
     *
     * @param table the table
     * @param value the value
     * @param isSelected is selected
     * @param hasFocus has focus
     * @param row the row
     * @param column the column
     * @see jnpad.ui.table.LabelTableCellRenderer#setState(javax.swing.JTable, java.lang.Object, boolean, boolean, int, int)
     */
    @Override
    protected void setState(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
      if (value instanceof NameElem) {
        setIcon( ( (NameElem) value).icon);
      }
      else {
        setIcon(null);
      }
    }
  }
  //////////////////////////////////////////////////////////////////////////////
}
