/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/05/26, 11:51
!  AUTHOR(S): KOGA, Junichiro
!  File : RemoteFileSystemView.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */

package ciss.phase_viewer.ssh.filechooser;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileSystemView;
import javax.swing.filechooser.FileView;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpException;

/**
 * @author KOGA, Junichiro
 */
public class RemoteFileSystemView extends FileSystemView {
    private static org.apache.log4j.Logger logger = org.apache.log4j.Logger
            .getLogger(RemoteFileSystemView.class.getName());
    private boolean isRemote = false;
    private ChannelSftp c;
    private String FS = System.getProperty("file.separator");

    /** Creates a new instance of RemoteFileSystemView */
    public RemoteFileSystemView() {
    }

    /**
     * @param isRemote
     *            : true if remote file system.
     */
    public RemoteFileSystemView(boolean isRemote) {
        this.isRemote = isRemote;
        if (isRemote) {
            FS = "/";
        }
    }

    public RemoteFileSystemView(ChannelSftp c) {
        this.c = c;
        if (c != null) {
            isRemote = true;
            FS = "/";
        }
    }

    /**
     * Returns a File object constructed from the given path string.
     */
    public java.io.File createFileObject(String path) {
        logger.debug("createFileObject");
        java.io.File retValue = null;
        if (!isRemote) {
            retValue = super.createFileObject(path);
        } else {
            logger.debug("path: " + path);
            retValue = new RemoteFile(path, c);
        }

        return retValue;
    }

    /**
     * Gets the list of shown (i.e. not hidden) files.
     */
    public java.io.File[] getFiles(java.io.File dir, boolean useFileHiding) {

        java.io.File[] retValue;
        if (!isRemote) {
            retValue = super.getFiles(dir, useFileHiding);
        } else {
            logger.debug("getFiles: " + dir.getAbsolutePath());
            RemoteFile rfile = new RemoteFile(dir.getAbsolutePath(), c);
            retValue = rfile.listFiles();
        }
        return retValue;
    }

    /**
     * 
     * @param parent
     *            a <code>File</code> object repesenting a directory or special
     *            folder
     * @param fileName
     *            a name of a file or folder which exists in <code>parent</code>
     * @return a File object. This is normally constructed with <code>new
     * File(parent, fileName)</code> except when parent and child are both
     *         special folders, in which case the <code>File</code> is a wrapper
     *         containing a <code>ShellFolder</code> object.
     */
    public java.io.File getChild(java.io.File parent, String fileName) {
        logger.debug("getChild");
        java.io.File retValue;
        if (!isRemote) {
            retValue = super.getChild(parent, fileName);
        } else {
            retValue = new RemoteFile(parent.getAbsolutePath(), fileName, c);
        }
        return retValue;
    }

    /**
     * Returns a File object constructed in dir from the given filename.
     */
    public java.io.File createFileObject(java.io.File dir, String filename) {
        logger.debug("createFileObject");
        java.io.File retValue;
        if (!isRemote) {
            retValue = super.createFileObject(dir, filename);
        } else {
            retValue = new RemoteFile(dir.getAbsolutePath(), filename, c);
        }
        return retValue;
    }

    /**
     * Returns true if the file (directory) can be visited. Returns false if the
     * directory cannot be traversed.
     * 
     * @param f
     *            the <code>File</code>
     * @return <code>true</code> if the file/directory can be traversed,
     *         otherwise <code>false</code>
     * @see JFileChooser#isTraversable
     * @see FileView#isTraversable
     */
    public Boolean isTraversable(java.io.File f) {

        Boolean retValue;
        if (!isRemote) {
            retValue = super.isTraversable(f);
        } else {
            RemoteFile rfile = new RemoteFile(f.getAbsolutePath(), c);
            retValue = new Boolean(rfile.canRead());
        }
        return retValue;
    }

    /**
     * Determines if the given file is a root in the navigatable tree(s).
     * Examples: Windows 98 has one root, the Desktop folder. DOS has one root
     * per drive letter, <code>C:\</code>, <code>D:\</code>, etc. Unix has one
     * root, the <code>"/"</code> directory.
     * 
     * The default implementation gets information from the
     * <code>ShellFolder</code> class.
     * 
     * @param f
     *            a <code>File</code> object representing a directory
     * @return <code>true</code> if <code>f</code> is a root in the navigatable
     *         tree.
     * @see #isFileSystemRoot
     */
    public boolean isRoot(java.io.File f) {
        logger.debug("isRoot");
        boolean retValue;
        if (!isRemote) {
            retValue = super.isRoot(f);
        } else {
            String fs = f.getAbsolutePath();
            retValue = fs.equals("/");
        }
        return retValue;
    }

    /**
     * Used by UI classes to decide whether to display a special icon for a
     * floppy disk. Implies isDrive(dir).
     * 
     * The default implementation has no way of knowing, so always returns
     * false.
     * 
     * @param dir
     *            a directory
     * @return <code>false</code> always
     */
    public boolean isFloppyDrive(java.io.File dir) {

        boolean retValue;
        if (!isRemote) {
            retValue = super.isFloppyDrive(dir);
        } else {
            retValue = false;
        }
        return retValue;
    }

    /**
     * Returns whether a file is hidden or not.
     */
    public boolean isHiddenFile(java.io.File f) {

        boolean retValue;

        if (!isRemote) {
            retValue = super.isHiddenFile(f);
        } else {
            retValue = false;
        }
        return retValue;
    }

    /**
     * Is dir the root of a tree in the file system, such as a drive or
     * partition. Example: Returns true for "C:\" on Windows 98.
     * 
     * @param f
     *            a <code>File</code> object representing a directory
     * @return <code>true</code> if <code>f</code> is a root of a filesystem
     * @see #isRoot
     */
    public boolean isFileSystemRoot(java.io.File dir) {
        logger.debug("isFileSystemRoot");
        boolean retValue;
        if (!isRemote) {
            retValue = super.isFileSystemRoot(dir);
        } else {
            retValue = isRoot(dir);
        }
        return retValue;
    }

    /**
     * Checks if <code>f</code> represents a real directory or file as opposed
     * to a special folder such as <code>"Desktop"</code>. Used by UI classes to
     * decide if a folder is selectable when doing directory choosing.
     * 
     * @param f
     *            a <code>File</code> object
     * @return <code>true</code> if <code>f</code> is a real file or directory.
     */
    public boolean isFileSystem(java.io.File f) {
        logger.debug("isFileSystem");
        boolean retValue;
        if (!isRemote) {
            retValue = super.isFileSystem(f);
        } else {
            retValue = true;
        }
        return retValue;
    }

    /**
     * Used by UI classes to decide whether to display a special icon for drives
     * or partitions, e.g. a "hard disk" icon.
     * 
     * The default implementation has no way of knowing, so always returns
     * false.
     * 
     * @param dir
     *            a directory
     * @return <code>false</code> always
     */
    public boolean isDrive(java.io.File dir) {
        logger.debug("isDrive");
        boolean retValue;
        if (!isRemote) {
            retValue = super.isDrive(dir);
        } else {
            retValue = false;
        }
        return retValue;
    }

    /**
     * Used by UI classes to decide whether to display a special icon for a
     * computer node, e.g. "My Computer" or a network server.
     * 
     * The default implementation has no way of knowing, so always returns
     * false.
     * 
     * @param dir
     *            a directory
     * @return <code>false</code> always
     */
    public boolean isComputerNode(java.io.File dir) {

        boolean retValue;
        if (!isRemote) {
            retValue = super.isComputerNode(dir);
        } else {
            retValue = true;
        }
        return retValue;
    }

    /**
     * Creates a new <code>File</code> object for <code>f</code> with correct
     * behavior for a file system root directory.
     * 
     * @param f
     *            a <code>File</code> object representing a file system root
     *            directory, for example "/" on Unix or "C:\" on Windows.
     * @return a new <code>File</code> object
     */
    protected java.io.File createFileSystemRoot(java.io.File f) {
        logger.debug("createFileSystemRoot");
        java.io.File retValue;
        if (!isRemote) {
            retValue = super.createFileSystemRoot(f);
        } else {
            retValue = new RemoteFile("/", c);
        }
        return retValue;
    }

    /**
     * Creates a new folder with a default folder name.
     */
    public java.io.File createNewFolder(java.io.File containingDir)
            throws java.io.IOException {
        java.io.File file;
        String filestring = containingDir.getAbsolutePath() + FS + "NewFolder";

        if (!isRemote) {
            file = new java.io.File(filestring);
            file.mkdir();
        } else {
            file = new RemoteFile(filestring, c);
            file.mkdir();
        }
        return file;
    }

    /**
     * Returns the parent directory of <code>dir</code>.
     * 
     * @param dir
     *            the <code>File</code> being queried
     * @return the parent directory of <code>dir</code>, or <code>null</code> if
     *         <code>dir</code> is <code>null</code>
     */
    public java.io.File getParentDirectory(java.io.File dir) {
        logger.debug("getParentDirectory");
        java.io.File retValue;
        if (!isRemote) {
            retValue = super.getParentDirectory(dir);
        } else {
            logger.debug("dir: " + dir);
            if (dir == null) {
                retValue = new RemoteFile("/", c);
            } else {
                RemoteFile rfile = new RemoteFile(dir.getAbsolutePath(), c);
                retValue = rfile.getParentFile();
            }
        }
        return retValue;
    }

    /**
     * Name of a file, directory, or folder as it would be displayed in a system
     * file browser. Example from Windows: the "M:\" directory displays as
     * "CD-ROM (M:)"
     * 
     * The default implementation gets information from the ShellFolder class.
     * 
     * @param f
     *            a <code>File</code> object
     * @return the file name as it would be displayed by a native file chooser
     * @see JFileChooser#getName
     */
    public String getSystemDisplayName(java.io.File f) {

        String retValue;
        if (!isRemote) {
            retValue = super.getSystemDisplayName(f);
        } else {
            retValue = f.getName();
        }
        return retValue;
    }

    /**
     * Icon for a file, directory, or folder as it would be displayed in a
     * system file browser. Example from Windows: the "M:\" directory displays a
     * CD-ROM icon.
     * 
     * The default implementation gets information from the ShellFolder class.
     * 
     * @param f
     *            a <code>File</code> object
     * @return an icon as it would be displayed by a native file chooser
     * @see JFileChooser#getIcon
     */
    public javax.swing.Icon getSystemIcon(java.io.File f) {

        javax.swing.Icon retValue;

        String folicon = System.getProperty("pviewer.home")
                + System.getProperty("file.separator") + "lib"
                + System.getProperty("file.separator") + "resource"
                + System.getProperty("file.separator") + "folder_white.png";
        BufferedImage image = null;
        try {
            image = ImageIO.read(new File(folicon));
        } catch (IOException ioe) {
        }

        ImageIcon ic = new ImageIcon(image);
        logger.debug("got image icon ...");
        retValue = super.getSystemIcon(f);
        if (isRemote) {
            retValue = ic;
        }
        return retValue;
    }

    /**
     * Type description for a file, directory, or folder as it would be
     * displayed in a system file browser. Example from Windows: the "Desktop"
     * folder is desribed as "Desktop".
     * 
     * Override for platforms with native ShellFolder implementations.
     * 
     * @param f
     *            a <code>File</code> object
     * @return the file type description as it would be displayed by a native
     *         file chooser or null if no native information is available.
     * @see JFileChooser#getTypeDescription
     */
    public String getSystemTypeDescription(java.io.File f) {

        String retValue;
        if (!isRemote) {
            retValue = super.getSystemTypeDescription(f);
        } else {
            retValue = null;
        }
        return retValue;
    }

    /**
     * Returns a string representation of the object. In general, the
     * <code>toString</code> method returns a string that "textually represents"
     * this object. The result should be a concise but informative
     * representation that is easy for a person to read. It is recommended that
     * all subclasses override this method.
     * <p>
     * The <code>toString</code> method for class <code>Object</code> returns a
     * string consisting of the name of the class of which the object is an
     * instance, the at-sign character `<code>@</code>', and the unsigned
     * hexadecimal representation of the hash code of the object. In other
     * words, this method returns a string equal to the value of: <blockquote>
     * 
     * <pre>
     * getClass().getName() + '@' + Integer.toHexString(hashCode())
     * </pre>
     * 
     * </blockquote>
     * 
     * @return a string representation of the object.
     */
    public String toString() {

        String retValue;
        retValue = super.toString();
        return retValue;
    }

    /**
     * On Windows, a file can appear in multiple folders, other than its parent
     * directory in the filesystem. Folder could for example be the "Desktop"
     * folder which is not the same as file.getParentFile().
     * 
     * @param folder
     *            a <code>File</code> object repesenting a directory or special
     *            folder
     * @param file
     *            a <code>File</code> object
     * @return <code>true</code> if <code>folder</code> is a directory or
     *         special folder and contains <code>file</code>.
     */
    public boolean isParent(java.io.File folder, java.io.File file) {

        boolean retValue;
        if (!isRemote) {
            retValue = super.isParent(folder, file);
        } else {
            retValue = false;
        }
        return retValue;
    }

    /**
     * Returns a hash code value for the object. This method is supported for
     * the benefit of hashtables such as those provided by
     * <code>java.util.Hashtable</code>.
     * <p>
     * The general contract of <code>hashCode</code> is:
     * <ul>
     * <li>Whenever it is invoked on the same object more than once during an
     * execution of a Java application, the <tt>hashCode</tt> method must
     * consistently return the same integer, provided no information used in
     * <tt>equals</tt> comparisons on the object is modified. This integer need
     * not remain consistent from one execution of an application to another
     * execution of the same application.
     * <li>If two objects are equal according to the <tt>equals(Object)</tt>
     * method, then calling the <code>hashCode</code> method on each of the two
     * objects must produce the same integer result.
     * <li>It is <em>not</em> required that if two objects are unequal according
     * to the {@link java.lang.Object#equals(java.lang.Object)} method, then
     * calling the <tt>hashCode</tt> method on each of the two objects must
     * produce distinct integer results. However, the programmer should be aware
     * that producing distinct integer results for unequal objects may improve
     * the performance of hashtables.
     * </ul>
     * <p>
     * As much as is reasonably practical, the hashCode method defined by class
     * <tt>Object</tt> does return distinct integers for distinct objects. (This
     * is typically implemented by converting the internal address of the object
     * into an integer, but this implementation technique is not required by the
     * Java<font size="-2"><sup>TM</sup></font> programming language.)
     * 
     * @return a hash code value for this object.
     * @see java.lang.Object#equals(java.lang.Object)
     * @see java.util.Hashtable
     */
    public int hashCode() {

        int retValue;

        retValue = super.hashCode();
        return retValue;
    }

    /**
     * Return the user's default starting directory for the file chooser.
     * 
     * @return a <code>File</code> object representing the default starting
     *         folder
     */
    public java.io.File getDefaultDirectory() {
        logger.debug("getDefaultDirectory");
        java.io.File retValue = null;
        if (!isRemote) {
            retValue = super.getDefaultDirectory();
        } else {
            String curdir="";
            try {
                curdir = c.pwd();
            } catch (Exception e) {
                e.printStackTrace();
            }
            logger.debug("curdir: " + curdir);
            retValue = new RemoteFile(curdir, c);
        }
        return retValue;
    }

    public java.io.File getHomeDirectory() {
        logger.debug("getHomeDirectory");
        java.io.File retValue = null;
        if (!isRemote) {
            retValue = super.getHomeDirectory();
        } else {
            String curdir="";
            try {
                curdir = c.pwd();
            } catch (Exception e) {
                e.printStackTrace();
            }
            logger.debug("curdir: " + curdir);
            retValue = new RemoteFile(curdir, c);
        }
        return retValue;
    }

    /**
     * Returns all root partitions on this system. For example, on Windows, this
     * would be the "Desktop" folder, while on DOS this would be the A: through
     * Z: drives.
     */
    public java.io.File[] getRoots() {
        logger.debug("getRoots");
        java.io.File[] retValue;
        if (!isRemote) {
            retValue = super.getRoots();
        } else {
            retValue = new File[1];
            retValue[0] = new RemoteFile("/", c);
        }
        return retValue;
    }

}
