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

import java.awt.AWTEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.Arrays;

import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;

import jnpad.config.Config;
import jnpad.config.Configurable;
import jnpad.util.Utilities;

/**
 * The Class FileList.
 *
 * @version 0.3
 * @since   jNPad v0.1
 */
public class FileList extends JList implements Configurable {
  private DefaultListModel  listModel         = new DefaultListModel();
  private File[]            files             = new File[0];
  private File[]            directories       = new File[0];
  private File              _file;

  static final File[]       FILE_SYSTEM_ROOTS = Utilities.file_system_roots();
  private static File[]     _fileSystemRoots;

  private FileBrowser       browser;

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

  static {
    _fileSystemRoots = FILE_SYSTEM_ROOTS.clone();
  }

  /**
   * Instantiates a new file list.
   *
   * @param browser FileBrowser
   */
  public FileList(FileBrowser browser) {
    enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);

    this.browser = browser;

    configure(CFG_COLOR);

    setModel(listModel);
    setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    setCellRenderer(new FileListCellRenderer());
  }

  /**
   * Configure.
   *
   * @param cfg int
   * @see jnpad.config.Configurable#configure(int)
   */
  @Override
  public void configure(final int cfg) {
    if ((cfg & CFG_COLOR) != 0) {
      setFont(Config.BROWSER_NORMAL_FONT.getValue());
      setBackground(Config.TEXT_BACKGROUND.getValue());
      setForeground(Config.TEXT_FOREGROUND.getValue());
      setSelectionBackground(Config.TEXT_SELECTION_BACKGROUND.getValue());
      setSelectionForeground(Config.TEXT_SELECTION_FOREGROUND.getValue());
      FileBrowserObject.updateDirColors();
    }
  }

  /**
   * Sobreescriturar de processKeyEvent(KeyEvent )
   * 
   * @param e KeyEvent
   * @see javax.swing.JComponent#processKeyEvent(java.awt.event.KeyEvent)
   */
  @Override
  protected void processKeyEvent(KeyEvent e) {
    super.processKeyEvent(e);
    
    // =====================================================================
    // - KEY_PRESSED.
    // Se le llama después de que el usario pulse una tecla mientras el
    // componente escuchado tiene el foco.
    // =====================================================================
    if (e.getID() == KeyEvent.KEY_PRESSED) {
      final int keyCode = e.getKeyCode();
      final int modifiers = e.getModifiers();

      // VK_ENTER | VK_SPACE
      if (keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_SPACE) {
        if (modifiers == InputEvent.CTRL_MASK)
          refresh(getSelectedValue().toString());
        else
          action();
      }
      // VK_BACK_SPACE 
      else if (keyCode == KeyEvent.VK_BACK_SPACE) {
        backSpace();
      }
      // VK_HOME 
      else if (keyCode == KeyEvent.VK_HOME) {
        if (modifiers == InputEvent.CTRL_MASK)
          browser.goToHomeDir();
        else if (modifiers == InputEvent.ALT_MASK)
          browser.setHomeDir(getFile().getPath());
      }
      // C+NUMPAD1 | C+1
      else if (modifiers == InputEvent.CTRL_MASK &&
               (keyCode == KeyEvent.VK_NUMPAD1 || keyCode == KeyEvent.VK_1)) {
        browser.setPositionFrameOnScreen(0, 1);
      }
      // C+NUMPAD2 | C+2
      else if (modifiers == InputEvent.CTRL_MASK &&
               (keyCode == KeyEvent.VK_NUMPAD2 || keyCode == KeyEvent.VK_2)) {
        browser.setPositionFrameOnScreen(0.5, 1);
      }
      // C+NUMPAD3 | C+3
      else if (modifiers == InputEvent.CTRL_MASK &&
               (keyCode == KeyEvent.VK_NUMPAD3 || keyCode == KeyEvent.VK_3)) {
        browser.setPositionFrameOnScreen(1, 1);
      }
      // C+NUMPAD4 | C+4
      else if (modifiers == InputEvent.CTRL_MASK &&
               (keyCode == KeyEvent.VK_NUMPAD4 || keyCode == KeyEvent.VK_4)) {
        browser.setPositionFrameOnScreen(0, 0.5);
      }
      // C+NUMPAD5 | C+5
      else if (modifiers == InputEvent.CTRL_MASK &&
               (keyCode == KeyEvent.VK_NUMPAD5 || keyCode == KeyEvent.VK_5)) {
        browser.setPositionFrameOnScreen(0.5, 0.5);
      }
      // C+NUMPAD6 | C+6
      else if (modifiers == InputEvent.CTRL_MASK &&
               (keyCode == KeyEvent.VK_NUMPAD6 || keyCode == KeyEvent.VK_6)) {
        browser.setPositionFrameOnScreen(1, 0.5);
      }
      // C+NUMPAD7 | C+7
      else if (modifiers == InputEvent.CTRL_MASK &&
               (keyCode == KeyEvent.VK_NUMPAD7 || keyCode == KeyEvent.VK_7)) {
        browser.setPositionFrameOnScreen(0, 0);
      }
      // C+NUMPAD8 | C+8
      else if (modifiers == InputEvent.CTRL_MASK &&
               (keyCode == KeyEvent.VK_NUMPAD8 || keyCode == KeyEvent.VK_8)) {
        browser.setPositionFrameOnScreen(0.5, 0);
      }
      // C+NUMPAD9 | C+9
      else if (modifiers == InputEvent.CTRL_MASK &&
               (keyCode == KeyEvent.VK_NUMPAD9 || keyCode == KeyEvent.VK_9)) {
        browser.setPositionFrameOnScreen(1, 0);
      }
      // F1: Info
      else if (keyCode == KeyEvent.VK_F1) {
        browser.toggleInfoVisible();
      }
      // F2: Color
      else if (keyCode == KeyEvent.VK_F2) {
        browser.toggleColor();
      }
      // F3: Human-readable
      else if (keyCode == KeyEvent.VK_F3) {
        browser.toggleHumanReadable();
      }
      // F4: Classify
      else if (keyCode == KeyEvent.VK_F4) {
        browser.toggleClassify();
      }
    }
  }

  /**
   * Process mouse event.
   *
   * @param e MouseEvent
   * @see javax.swing.JComponent#processMouseEvent(java.awt.event.MouseEvent)
   */
  @Override
  protected void processMouseEvent(MouseEvent e) {
    super.processMouseEvent(e);

    // --------------
    // MOUSE RELEASED
    // --------------
    if (e.getID() == MouseEvent.MOUSE_RELEASED &&
             e.getClickCount() > 1 &&
             SwingUtilities.isLeftMouseButton(e)) {
      action();
    }
  }
  
  /**
   * Refresh.
   *
   * @param obj Object
   */
  public void refresh(Object obj) {
    refresh();
    if (listModel.contains(obj)) {
      setSelectedValue(obj, true);
    }
    else {
      FileBrowserObject fileBrowserObject = new FileBrowserObject(new File(_file, obj.toString()));
      setSelectedValue(fileBrowserObject, true);
    }
  }  
  
  /**
   * Refresh.
   */
  public void refresh() {
    final int selectedIndex = getSelectedIndex();
    Object obj = getSelectedValue();
    listFile(_file);
    if (listModel.indexOf(obj) > 0)
      setSelectedValue(obj, true);
    else if (selectedIndex < listModel.getSize())
      setSelectedValue(listModel.getElementAt(selectedIndex), true);
  }  
  
  /**
   * List file.
   *
   * @param file File
   * @return boolean
   */
  public boolean listFile(File file) {
    _file = file;
    try {
      browser.setStatus(_file.getAbsolutePath());
      files = _file.listFiles(browser.fileFilter);
      directories = _file.listFiles(browser.directoryFilter);

      Arrays.sort(files);
      Arrays.sort(directories);

      listModel.removeAllElements();
      removeAll();
      
      final String dotdot = Config.BROWSER_CLASSIFY.getValue() ? "../" : ".."; //$NON-NLS-1$ //$NON-NLS-2$
      
      listModel.addElement(dotdot);
      for (File directory : directories)
        listModel.addElement(new FileBrowserObject(directory));
      for (File file1 : files)
        listModel.addElement(new FileBrowserObject(file1));

      setSelectedValue(dotdot, true);

      for (int k = 0; k < FILE_SYSTEM_ROOTS.length; k++) {
        listModel.addElement(new FileBrowserObject(FILE_SYSTEM_ROOTS[k], true));
      }

      return true;
    }
    catch (Exception ex) {
      if (files == null)
        files = new File[0];
      if (directories == null)
        directories = new File[0];
      return false;
    }
  }

  /**
   * Back space.
   */
  public void backSpace() {
    File file = _file;
    File file1 = _file.getParentFile();
    if (file1 != null) {
      listFile(file1);
      setSelectedValue(new FileBrowserObject(file), true);
    }
  }

  /**
   * Gets the file.
   *
   * @return File
   */
  public File getFile() {
    return _file;
  }

  /**
   * Gets the path.
   *
   * @return String
   */
  public String getPath() {
    String s = Utilities.EMPTY_STRING;
    if (!Utilities.isFileSystemRoot(_file))
      s = Utilities.DIR_SEPARATOR_STRING;
    return _file.getAbsolutePath() + s + getSelectedName();
  }

  /**
   * Gets the selected name.
   *
   * @return String
   */
  public String getSelectedName() {
    String s = Utilities.EMPTY_STRING;
    int i;
    if ((i = getSelectedIndex()) > 0)
      if (i - 1 < directories.length)
        s = directories[i - 1].getName();
      else if (i - 1 < directories.length + files.length)
        s = files[i - directories.length - 1].getName();
    return s;
  }  

  /**
   * Action.
   */
  private void action() {
    int selectedIndex = getSelectedIndex();
    if (selectedIndex >= 0) {
      if (selectedIndex == 0) {
        backSpace();
      }
      else if (selectedIndex <= directories.length) {
        File file = new File(_file, directories[selectedIndex - 1].getName());
        File file1 = _file;
        if (file.exists() && !listFile(file)) {
          listFile(file1);
          browser.setStatus(FileBrowserBundle.getString("FileList.action.error", file.getAbsolutePath())); //$NON-NLS-1$
        }
      }
      else if (selectedIndex <= directories.length + files.length)
        browser.openFile();
      else
        driveChanged();
    }
  }
  
  /**
   * Drive changed.
   */
  private void driveChanged() {
    File file = _file.getAbsoluteFile();
    
    final int selectedIndex = getSelectedIndex();
    
    File file1 = _file;
    for (File file2 = _file.getParentFile(); file2 != null; file2 = file2.getParentFile())
      file1 = file2;

    for (int j = 0; j < _fileSystemRoots.length; j++)
      if (FILE_SYSTEM_ROOTS[j].equals(file1)) {
        _fileSystemRoots[j] = _file.getAbsoluteFile();
        j = _fileSystemRoots.length;
      }

    int k = selectedIndex - 1 - (directories.length + files.length);
    if (!listFile(_fileSystemRoots[k])) {
      _file = file;
      if (!listFile(FILE_SYSTEM_ROOTS[k])) {
        listFile(file);
        setSelectedValue(listModel.getElementAt(selectedIndex), true);
        browser.setStatus(FileBrowserBundle.getString("FileList.driveChanged.error", FILE_SYSTEM_ROOTS[k])); //$NON-NLS-1$
      }
    }
  }  
  
}
