/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : RemoteFile.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.io.File;
import java.net.URI;
import java.util.Calendar;
import java.util.Vector;

import ciss.phase_viewer.file.ChaseFile;
import ciss.phase_viewer.settings.PropertiesManager;
import ciss.phase_viewer.ssh.hosts.MyUserInfo;
import ciss.phase_viewer.ssh.sftp.Sftp;

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

//public class RemoteFile extends File implements Comparable {
public class RemoteFile extends File {
    private static org.apache.log4j.Logger logger = org.apache.log4j.Logger
            .getLogger(RemoteFile.class.getName());

    private ChannelSftp c;

    private Sftp sftp;

    private MyUserInfo userinfo;

    private boolean isRemote = false;

    private File file;

    private String pathname;
    private String FS = System.getProperty("file.separator");

    private boolean isRoot = false;

    private ChaseFile chaseFile;

    public RemoteFile(File parent, String child) {
        super(parent, child);
        file = new File(parent, child);
        this.pathname = parent.getAbsolutePath() + FS + child;
        RemoteFile file;
    }

    public RemoteFile(String pathname) {
        super(pathname);
        file = new File(pathname);
        this.pathname = pathname;
    }

    public RemoteFile(String parent, String child) {
        super(parent, child);
        file = new File(parent, child);
        this.pathname = parent + FS + child;
    }

    public RemoteFile(URI uri) {
        super(uri);
        file = new File(uri);
        this.pathname = uri.getPath();
    }

    public RemoteFile(File parent, String child, ChannelSftp c) {
        super(parent, child);
        file = new File(parent, child);
        this.c = c;
        if (c != null) {
            isRemote = true;
            FS = "/";
        }
        this.pathname = parent.getAbsolutePath() + FS + child;
    }

    public RemoteFile(String pathname, ChannelSftp c) {
        super(pathname);
        file = new File(pathname);
        this.c = c;
        if (c != null) {
            isRemote = true;
            FS = "/";
        }
        this.pathname = pathname;
    }

    public RemoteFile(String parent, String child, ChannelSftp c) {
        super(parent, child);
        file = new File(parent, child);
        this.c = c;
        if (c != null) {
            isRemote = true;
            FS = "/";
        }
        this.pathname = parent + FS + child;
    }

    public RemoteFile(URI uri, ChannelSftp c) {
        super(uri);
        file = new File(uri);
        this.c = c;
        if (c != null) {
            isRemote = true;
            FS = "/";
        }
        this.pathname = uri.getPath();
    }

    public void setChaseFile(ChaseFile cfile) {
        this.chaseFile = cfile;
    }

    public ChaseFile getChaseFile() {
        return this.chaseFile;
    }

    public void setUserInfo(MyUserInfo userinfo) {
        this.userinfo = userinfo;
    }

    public boolean equals(Object obj) {
        RemoteFile fi = null;
        try {
            fi = (RemoteFile) obj;
        } catch (ClassCastException cce) {
            return false;
        }
        if (fi == null) {
            return false;
        }
        return getAbsolutePath().equals(fi.getAbsolutePath());
    }

    //
    // public int compareTo(Object obj) {
    // RemoteFile comp = null;
    // try {
    // comp = (RemoteFile) obj;
    // } catch(ClassCastException cce) {
    // logger.debug("\"compareTo\" method failed");
    // return 0;
    // }
    // if ( !isRemote ) {
    // return file.compareTo(comp.getJavaIOFile());
    // }
    // String compstring = comp.getAbsolutePath();
    // String thisstring = this.getAbsolutePath();
    // return thisstring.compareTo(compstring);
    // }

    public File getJavaIOFile() {
        return file;
    }

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

    public String getParent() {
        if (!isRemote) {
            return super.getParent();
        }

        String pd;
        String pd_buff;
        try {
            pd_buff = c.pwd();
            c.cd("..");
            pd = c.pwd();
            c.cd(pd_buff);
        } catch (SftpException se) {
            logger.error("failed to change dir.");
            return pathname;
        }

        return pd;
    }

    public File getParentFile() {
        logger.debug("getting parent file ...");
        int maxdirdepth = 100;
        String d = PropertiesManager.getGlobalProperties(
                PropertiesManager.PROPERTIES_PVIEWER).getProperty(
                "sftp_client_max_dir_depth");
        if (d != null) {
            try {
                maxdirdepth = Integer.parseInt(d);
            } catch (NumberFormatException nfe) {
                maxdirdepth = 100;
            }
        }

        if (!isRemote) {
            File parent = file.getParentFile();
            if (parent != null) {
                return (File) new RemoteFile(parent.getAbsolutePath());
            }
            return null;
        }

        String pd = new String();
        String pd_buff = "";

        // int dircount = 0;
        // String wd = c.pwd();
        // if ( wd.trim().equals(FS) ) {
        // logger.debug("caught root...");
        // return null;
        // }
        //
        // logger.debug("wd: "+wd);

        String[] pd_buffs = pathname.split(FS);
        int len = 0;
        if (isDirectory()) {
            len = pd_buffs.length;
        } else {
            len = pd_buffs.length - 1;
        }
        logger.debug("num. parent dir: " + len);
        if (len == 0) {
            return null;
        }

        for (int i = 0; i < len; i++) {
            pd_buff += pd_buffs[i];
            if (i != len - 1) {
                pd_buff += FS;
            }
        }

        logger.debug("parent file: " + pd_buff);

        if (pd_buff.equals(FS)) {
            return null;
        }

        // Sftp sftp = ((ChannelSftpWrapper) c).getSftp();
        // // Session session = sftp.getSession();
        // // Channel channel = null;
        // // logger.debug("getting channel...");
        // // try {
        // // channel = session.openChannel("sftp");
        // // channel.connect();
        // // logger.debug("connected!");
        // // } catch(JSchException je) {
        // //
        // logger.debug("failed to obtain a 'channel' instance... returning.");
        // // return null;
        // // }
        // //
        // // ChannelSftp cs = (ChannelSftp) sftp.getChannel().connect();
        // ChannelSftpWrapper csw = new ChannelSftpWrapper(c, sftp);

        logger.debug("cd'ing dir to: " + pd_buff);
        try {
            c.cd(pd_buff);
            if (isDirectory()) {
                c.cd("..");
                pd_buff = c.pwd();
            }
        } catch (SftpException se) {
        }

        logger.debug("returning file: " + pd_buff);
        return (File) new RemoteFile(pd_buff, c);
    }

    public ChannelSftp getChannelSftp() {
        return c;
    }

    public boolean exists() {
        if (!isRemote) {
            return super.exists();
        }

        boolean ret = true;
        try {
            SftpATTRS attrs = c.stat(pathname);
            // logger.debug(attrs.toString());
        } catch (SftpException se) {
            ret = false;
        }

        return ret;
    }

    // public static File [] listRoots() {
    // どうもstaticにできない. ペンディング.
    // if ( !isRemote ) {
    // return File.listRoots();
    // } else {
    // RemoteFile [] rfs = {new RemoteFile("/",c)};
    // return (File[]) rfs;
    // }
    // }

    public String getSeparator() {
        return FS;
    }

    public boolean isRemote() {
        return isRemote;
    }

    public File[] listFiles() {

        RemoteFile[] rfiles = null;
        if (!isRemote) {
            File[] files = super.listFiles();
            if (files != null) {
                rfiles = new RemoteFile[files.length];
                for (int i = 0; i < files.length; i++) {
                    rfiles[i] = new RemoteFile(files[i].getAbsolutePath());
                }
            }
            return (File[]) rfiles;
        }

        if (!isDirectory()) {
            return null;
        }

        try {
            Vector lsres = c.ls(pathname);
            if (lsres != null) {
                rfiles = new RemoteFile[lsres.size()];
                for (int i = 0; i < lsres.size(); i++) {
                    logger.debug(lsres.elementAt(i));
                    ChannelSftp.LsEntry lsentry = (ChannelSftp.LsEntry) lsres
                            .elementAt(i);
                    String lsstr = lsentry.getLongname();
                    // String lsstr = (String) lsres.elementAt(i);
                    String[] elems = lsstr.split("[\\s]+");
                    if (elems.length < 9) {
                        return null;
                    }
                    String name = pathname;
                    if (!name.trim().endsWith(FS)) {
                        name += FS;
                    }
                    for (int j = 0; j < elems.length - 8; j++) {
                        name += elems[j + 8];
                    }
                    rfiles[i] = new RemoteFile(name, c);
                }
            }
        } catch (SftpException se) {
            logger.debug("failed to perform the command \"ls\"" + " for path "
                    + pathname);
        }

        return (File[]) rfiles;
    }

    public String getName() {
        if (!isRemote) {
            return super.getName();
        }

        String[] pathelems = pathname.split(FS);
        String ret = "";
        if (pathelems != null && pathelems.length != 0) {
            ret = pathelems[pathelems.length - 1];
        }
        return ret;
    }

    public boolean isDirectory() {
        if (!isRemote) {
            return super.isDirectory();
        }

        boolean ret = false;
        try {
            SftpATTRS attrs = c.stat(pathname);
            ret = attrs.isDir();
        } catch (SftpException se) {
            logger.debug("couldn't stat path: " + pathname);
        }
        return ret;
    }

    public String getAbsolutePath() {
        if (!isRemote) {
            return super.getAbsolutePath();
        }

        return pathname;
    }

    public boolean mkdir() {
        if (!isRemote) {
            return super.mkdir();
        }

        try {
            c.mkdir(pathname);
            return true;
        } catch (SftpException se) {
            return false;
        }
    }

    public boolean mkdirs() {
        int maxdirdepth = 500;
        int dircount = 0;

        if (!isRemote || c == null) {
            return super.mkdirs();
        }

        String tmpstring = pathname.toString();
        String trimmeddir = new String();
        Vector trimmeddirs = new Vector();

        while (!exists(tmpstring) || dircount < maxdirdepth) {
            String[] rets = parentPath(tmpstring);
            tmpstring = rets[0];
            trimmeddir = rets[1];
            logger.debug("tmpstring, trimmed string: " + tmpstring + " "
                    + trimmeddir);
            trimmeddirs.addElement(trimmeddir.toString());
            if (exists(tmpstring)) {
                break;
            }
            dircount++;
        }

        logger.debug("tmpstring: " + tmpstring);
        String newdir = tmpstring;
        for (int i = trimmeddirs.size() - 1; i >= 0; i--) {
            logger.debug("trimmed dir: " + trimmeddirs.elementAt(i));
            RemoteFile nrf = new RemoteFile(newdir, c);
            // if ( !nrf.canWrite() ) {
            // logger.error("no permission to create dir: "+nrf.getAbsolutePath());
            // return false;
            // }
            newdir += (String) trimmeddirs.elementAt(i) + "/";
            nrf = new RemoteFile(newdir, c);
            if (!nrf.mkdir()) {
                // logger.error("no permission to create dir: "+nrf.getAbsolutePath());
                return false;
            }
        }

        return true;
    }

    private boolean exists(String str) {
        RemoteFile rf = new RemoteFile(str, c);
        return rf.exists();
    }

    private String[] parentPath(String str) {
        String[] rets = new String[2];
        String ret = "";
        String trim = "";

        String[] paths = str.split("/");
        if (paths == null || paths.length == 0) {
            ret = "/";
            trim = "";
        } else {
            for (int i = 0; i < paths.length - 1; i++) {
                ret += paths[i] + "/";
            }
            trim = paths[paths.length - 1].toString();
        }
        rets[0] = ret;
        rets[1] = trim;
        return rets;
    }

    public String toString() {
        if (!isRemote) {
            return super.toString();
        }
        return pathname;
    }

    public boolean canRead() {
        if (!isRemote) {
            return super.canRead();
        }

        try {
            Vector lsres = c.ls(pathname);
            if (lsres != null) {
                for (int i = 0; i < lsres.size(); i++) {
                    // String lsstr = (String) lsres.elementAt(i);
                    String lsstr = ((ChannelSftp.LsEntry) lsres.elementAt(i))
                            .getLongname();
                    String[] elems = lsstr.split("[\\s]+");
                    if (elems.length < 9) {
                        logger.warn("invalid output from 'ls -l' command");
                        return false;
                    }

                    String permission = elems[0];
                    char[] per = permission.toCharArray();
                    if (per != null && per.length > 4) {
                        return (new Character(per[1]))
                                .equals(new Character('r'));
                    }
                }
            }
        } catch (SftpException se) {
            logger.debug("failed to perform the command \"ls\"" + " for path "
                    + pathname);
        }

        return false;
    }

    public boolean canWrite() {
        if (!isRemote) {
            return super.canWrite();
        }

        try {
            Vector lsres = c.ls(pathname);
            if (lsres != null) {
                for (int i = 0; i < lsres.size(); i++) {
                    // String lsstr = (String) lsres.elementAt(i);
                    String lsstr = ((ChannelSftp.LsEntry) lsres.elementAt(i))
                            .getLongname();
                    String[] elems = lsstr.split("[\\s]+");
                    if (elems.length < 9) {
                        logger.warn("invalid output from 'ls -l' command");
                        return false;
                    }

                    String permission = elems[0];
                    char[] per = permission.toCharArray();
                    if (per != null && per.length > 4) {
                        return (new Character(per[1]))
                                .equals(new Character('w'));
                    }
                }
            }
        } catch (SftpException se) {
            logger.debug("failed to perform the command \"ls\"" + " for path "
                    + pathname);
        }

        return false;
    }

    public long lastModified() {
        boolean dir = false;
        long lastmodified = 0L;

        if (!isRemote) {
            return super.lastModified();
        }
        int mtime = 0;
        try {
            Vector lsres = c.ls(pathname);
            if (isDirectory()) {
                dir = true;
            }
            if (lsres != null) {
                for (int i = 0; i < lsres.size(); i++) {
                    // String lsstr = (String) lsres.elementAt(i);
                    ChannelSftp.LsEntry lsentry = (ChannelSftp.LsEntry) lsres
                            .elementAt(i);
                    mtime = lsentry.getAttrs().getMTime();
                    // String lsstr = lsentry.getLongname();
                    // String [] elems = lsstr.split("[\\s]+");
                    // if ( elems.length < 9 ) {
                    // logger.warn("invalid output from 'ls -l' command");
                    // return 0L;
                    // }
                    // if ( dir && elems[8].equals(".") || !dir ) {
                    // lastmodified = computeLastModified(elems);
                    // }
                }
            }
        } catch (SftpException se) {
            logger.debug("failed to perform the command \"ls\"" + " for path "
                    + pathname);
        }
        // return lastmodified;
        return (long) mtime;
    }

    private long computeLastModified(String[] elems) {
        Calendar calendar = Calendar.getInstance();
        String month = elems[5];
        month = month.trim().toLowerCase();
        String day = elems[6];
        String time = elems[7];

        int iyear = 0;
        int imonth = 1;
        int iday = 1;
        int ihour = 0;
        int iminute = 0;
        int isec = 0;

        if (month.startsWith("jan")) {
            imonth = 1;
        } else if (month.startsWith("feb")) {
            imonth = 2;
        } else if (month.startsWith("mar")) {
            imonth = 3;
        } else if (month.startsWith("apr")) {
            imonth = 4;
        } else if (month.startsWith("may")) {
            imonth = 5;
        } else if (month.startsWith("jun")) {
            imonth = 6;
        } else if (month.startsWith("jul")) {
            imonth = 7;
        } else if (month.startsWith("aug")) {
            imonth = 8;
        } else if (month.startsWith("sep")) {
            imonth = 9;
        } else if (month.startsWith("oct")) {
            imonth = 10;
        } else if (month.startsWith("nov")) {
            imonth = 11;
        } else if (month.startsWith("dec")) {
            imonth = 12;
        }

        try {
            iday = Integer.parseInt(day);
        } catch (NumberFormatException nfe) {
            logger.warn("date-info not valid for " + pathname);
        }

        String[] times = time.split(":");
        if (times.length == 2) {
            iyear = calendar.get(Calendar.YEAR);
            try {
                ihour = Integer.parseInt(times[0]);
                iminute = Integer.parseInt(times[1]);
            } catch (NumberFormatException nfe) {
                logger.warn("time-info not valid for " + pathname);
            }
        } else {
            try {
                iyear = Integer.parseInt(time);
                ihour = 0;
                iminute = 0;
            } catch (NumberFormatException nfe) {
                logger.warn("year-info not valid for " + pathname);
            }
        }

        imonth--;

        calendar.set(iyear, imonth, iday, ihour, iminute);
        return calendar.getTimeInMillis();
    }

    public long length() {
        if (!isRemote) {
            return super.length();
        }

        long ret = 0L;
        try {
            Vector lsres = c.ls(pathname);
            if (lsres != null) {
                for (int i = 0; i < lsres.size(); i++) {
                    // String lsstr = (String) lsres.elementAt(i);
                    String lsstr = ((ChannelSftp.LsEntry) lsres.elementAt(i))
                            .getLongname();
                    String[] elems = lsstr.split("[\\s]+");
                    if (elems.length < 9) {
                        logger.warn("invalid output from 'ls -l' command");
                        return 0L;
                    }
                    String size = elems[4];
                    try {
                        ret = (Long.valueOf(size)).longValue();
                    } catch (NumberFormatException nfe) {
                        ret = 0L;
                    }
                }
            }
            return ret;
        } catch (SftpException se) {
            logger.debug("failed to perform the command \"ls\"" + " for path "
                    + pathname);
        }
        return 0L;
    }

    public boolean renameTo(File rfile) {
        if (!isRemote) {
            File rrfile = new File(rfile.getAbsolutePath());
            return super.renameTo(rrfile);
        }

        String renamepath = rfile.getAbsolutePath();
        try {
            logger.debug("renaming " + pathname + " to " + renamepath);
            c.rename(pathname, renamepath);
            return true;
        } catch (SftpException se) {
            logger.warn("failed rename.");
            return false;
        }

    }

    public boolean delete() {
        if (!isRemote) {
            return super.delete();
        }

        try {
            c.rm(pathname);
            return true;
        } catch (SftpException se) {
            logger.error("failed rename.");
            return false;
        }

    }

    public boolean isRoot() {
        return isRoot;
    }

    public void isRoot(boolean isRoot) {
        this.isRoot = isRoot;
    }

    private boolean overwrite = true;

    public void overwrite(boolean overwrite) {
        this.overwrite = overwrite;
    }

    public boolean overwrite() {
        return this.overwrite;
    }

}
