/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/06/20, 17:58
!  AUTHOR(S): KOGA, Junichiro
!  File : BatchFileTransferer.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.remotehostexecuter;

import java.io.File;
import java.io.Serializable;
import java.util.Vector;

import ciss.phase_viewer.file.ChaseFile;
import ciss.phase_viewer.file.ChaseFileManager;
import ciss.phase_viewer.projectbrowser.ProjectInfo;
import ciss.phase_viewer.ssh.filechooser.RemoteFile;
import ciss.phase_viewer.ssh.hosts.HostInfo;
import ciss.phase_viewer.ssh.sftp.Sftp;
import ciss.phase_viewer.ssh.sftp.SftpEvent;
import ciss.phase_viewer.ssh.sftp.SftpListener;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.Session;

/**
 * used to upload/download from/to remote/local host in batch mode.
 * 
 * @author KOGA, Junichiro
 */

public class BatchFileTransferer implements SftpListener, Serializable {
    private static org.apache.log4j.Logger logger = org.apache.log4j.Logger
            .getLogger(BatchFileTransferer.class.getName());
    private HostInfo hostinfo;

    private String targetdir;
    private String localdir;
    private Sftp sftp;
    private ChannelSftp c;

    private Vector regfiles = new Vector();
    private Vector listeners = new Vector();
    private ProjectInfo projectInfo;

    public BatchFileTransferer(ProjectInfo projectInfo, HostInfo hostinfo) {
        this.localdir = projectInfo.getProjectDirectory();
        this.projectInfo = projectInfo;
        this.hostinfo = hostinfo;
        init();
    }

    public void disconnect() {
        if (sftp != null)
            sftp.disconnect();
    }

    public void registerFiles(String path) {
        regfiles.add(path);
    }

    public void uploadFinished(SftpEvent se) {
        logger.info("finished upload.");
        for (int i = 0; i < listeners.size(); i++) {
            ((SftpListener) listeners.elementAt(i)).uploadFinished(se);
        }
        sftp.disconnect();
    }

    public void downloadFinished(SftpEvent se) {
        logger.info("finished download.");
        sftp.disconnect();
    }

    public void addSftpListener(SftpListener list) {
        listeners.addElement(list);
    }

    private ChaseFile[] chaseFiles;

    private void init() {
        logger.debug("initializing....");
        targetdir = projectInfo.getTargetDirectory();
        ChaseFileManager cfmanager = projectInfo.getChaseFileManager();
        if (cfmanager == null) {
            logger.error("invalid ChaseFileManager.");
            return;
        }
        chaseFiles = cfmanager.getFiles();
        logger.debug("initialization finished...");
    }

    public String getTargetDir() {
        return this.targetdir;
    }

    public Session getSession() {
        if (sftp != null) {
            return sftp.getSession();
        }
        return null;
    }

    public boolean upload(File file) {
        RemoteFile[] rfs = new RemoteFile[1];
        rfs[0] = new RemoteFile(file.getAbsolutePath());
        sftp = new Sftp(hostinfo);
        sftp.addSftpListener(this);
        sftp.connect();
        c = sftp.getChannelSftp();
        sftp.upload(rfs, new RemoteFile(targetdir, c),
                projectInfo.getProjectDirectory());
        return true;
    }

    public boolean upload() {
        logger.debug("uploading .... host: " + hostinfo.getName());
        if (hostinfo == null) {
            logger.error("invalid hostinfo.");
            return false;
        }

        if (targetdir == null || targetdir.trim().length() == 0) {
            targetdir = "";
        }

        if (targetdir.startsWith("$HOME/")) {
            targetdir = targetdir.substring(6, targetdir.length());
        }

        logger.debug("uploading file to " + hostinfo.getName()
                + ", target dir: " + targetdir);
        RemoteFile[] rfs = chaseFile2RemoteFile("upload");

        if (rfs == null) {
            logger.debug("found no valid files...");
            return false;
        }

        for (int i = 0; i < rfs.length; i++) {
            logger.debug("file no. " + i + ": " + rfs[i].toString());
        }

        sftp.upload(rfs, new RemoteFile(targetdir, c),
                projectInfo.getProjectDirectory());
        return true;
    }

    public boolean download() {
        if (hostinfo == null) {
            return false;
        }

        if (targetdir == null || targetdir.trim().length() == 0) {
            targetdir = "";
        }

        if (targetdir.startsWith("$HOME/")) {
            targetdir = targetdir.substring(6, targetdir.length());
        }

        logger.debug("downloading file to " + hostinfo.getName()
                + ", target dir: " + targetdir);
        RemoteFile[] rfs = chaseFile2RemoteFile("download");
        if (rfs == null) {
            return false;
        }
        sftp.download(rfs, new RemoteFile(projectInfo.getProjectDirectory()),
                false);
        return true;
    }

    private String replaceHOME(String dir) {
        if (dir.startsWith("$HOME/"))
            return dir.substring(6, dir.length());
        else
            return dir;
    }

    private RemoteFile[] chaseFile2RemoteFile(String mode) {
        if (chaseFiles == null) {
            logger.debug("invalid chaseFiles");
            return null;
        }
        int numfiles = chaseFiles.length;
        logger.debug("at chaseFile2RemoteFile, mode: " + mode
                + ", num. files: " + numfiles);
        sftp = new Sftp(hostinfo);
        sftp.addSftpListener(this);

        sftp.connect();
        c = sftp.getChannelSftp();
        if (c == null) {
            return null;
        }

        Vector filevector = new Vector();
        ChannelSftp ctmp = null;
        if (mode.equals("download")) {
            ctmp = c;
        }

        Vector remfiles = new Vector();
        for (int i = 0; i < numfiles; i++) {
            ChaseFile file = chaseFiles[i];
            logger.debug("processing file: " + file);
            if (!file.upload_or_download(mode)) {
                continue;
            }
            if (file == null) {
                logger.error("element is null!");
                return null;
            }

            String directory = "";
            String filesep = System.getProperty("file.separator");
            if (mode.equals("upload")) {
                directory = replaceHOME(projectInfo.getProjectDirectory());
                if (!directory.endsWith(System.getProperty("file.separator"))) {
                    directory += System.getProperty("file.separator");
                }
            } else if (mode.equals("download")) {
                filesep = "/";
                directory = replaceHOME(projectInfo.getTargetDirectory());

                if (!directory.endsWith(filesep)) {
                    directory += filesep;
                }
            }

            String transfer = file.getTransfer();
            Vector fnamevec = new Vector();
            logger.debug("directory, transfer: " + directory + ", " + transfer);
            if (!file.getDefaultName().hasRegExp()) {
                fnamevec.add(file.getFileName());
            } else {
                if (transfer.equals(ChaseFile.ALL)) {
                    String rname = file.getDefaultName().getValue();
                    fnamevec = getFileNamesFromRegExpEntry(directory, rname,
                            ctmp);
                } else {
                    String rname = file.getDefaultName().getValue();
                    String foo = getFileNameFromRegExpEntry(directory, rname,
                            ctmp);
                    fnamevec.addElement(foo);
                }
            }

            if (fnamevec.size() == 0) {
                continue;
            }

            for (int j = 0; j < fnamevec.size(); j++) {
                String filepath = directory + file.getRelativePath() + filesep
                        + (String) fnamevec.get(j);
                RemoteFile rf = new RemoteFile(filepath, ctmp);
                if (rf.exists()) {
                    rf.setChaseFile(file);
                    if (file.getDuplicateFile().equals(ChaseFile.OVERWRITE)) {
                        rf.overwrite(true);
                    } else {
                        rf.overwrite(false);
                    }
                    remfiles.addElement(rf);
                }
            }
            logger.debug("ok done with " + file.getFileName());
        }

        /*
         * その他のファイルも足しておく
         */
        for (int i = 0; i < regfiles.size(); i++) {
            remfiles.add(new RemoteFile((String) regfiles.elementAt(i), ctmp));
        }

        if (remfiles.size() == 0) {
            return null;
        }

        RemoteFile[] ret = new RemoteFile[remfiles.size()];
        remfiles.copyInto(ret);
        return ret;
    }

    private String getFileNameFromRegExpEntry(String target, String name,
            ChannelSftp c) {
        String ret = "";
        logger.debug("targetdir: " + target);
        RemoteFile rf = new RemoteFile(target, c);
        RemoteFile[] rfs = (RemoteFile[]) rf.listFiles();

        if (rfs == null || rfs.length == 0) {
            logger.debug("remote file list was null.");
            return ret;
        }

        long newest = 0L;
        int inew = 0;
        for (int i = 0; i < rfs.length; i++) {
            if (rfs[i].getName().matches(name)) {
                logger.debug("caught: " + rfs[i].getName());
                long last = rfs[i].lastModified();
                if (last > newest) {
                    ret = rfs[i].getName();
                }
            } else {
                logger.debug(rfs[i].getName() + " does not match " + name + ".");
            }
        }

        logger.debug("newest file: " + ret);
        return ret;
    }

    private Vector getFileNamesFromRegExpEntry(String target, String name,
            ChannelSftp c) {
        logger.debug("targetdir: " + target);
        Vector ret = new Vector();
        RemoteFile rf = new RemoteFile(target, c);
        RemoteFile[] rfs = (RemoteFile[]) rf.listFiles();

        if (rfs == null || rfs.length == 0) {
            logger.debug("remote file list was null.");
            return ret;
        }

        for (int i = 0; i < rfs.length; i++) {
            if (rfs[i].getName().matches(name)) {
                logger.debug("caught: " + rfs[i].getName());
                ret.addElement(rfs[i].getName());
            } else {
                logger.debug(rfs[i].getName() + " does not match " + name + ".");
            }
        }
        return ret;
    }

}
