/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/06/21, 19:05
!  AUTHOR(S): KOGA, Junichiro
!  File : JobExecuter.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.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Vector;

import org.jdom.Document;
import org.jdom.Element;

import ciss.phase_viewer.common.MyThread;
import ciss.phase_viewer.common.Utils;
import ciss.phase_viewer.jdom.MyElement;
import ciss.phase_viewer.jobcontrol.JobController;
import ciss.phase_viewer.projectbrowser.ProjectInfo;
import ciss.phase_viewer.scripting.ScriptWrapper;
import ciss.phase_viewer.scripting.scriptreplace.ScriptKeywordsReplacer;
import ciss.phase_viewer.settings.PropertiesManager;
import ciss.phase_viewer.ssh.Execute;
import ciss.phase_viewer.ssh.SessionCreator;
import ciss.phase_viewer.ssh.hosts.HostInfo;
import ciss.phase_viewer.ssh.sftp.SftpEvent;
import ciss.phase_viewer.ssh.sftp.SftpListener;

import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

/**
 * @author KOGA, Junichiro
 */
public class JobExecuter implements SftpListener, Serializable {
    private transient org.apache.log4j.Logger logger = org.apache.log4j.Logger
            .getLogger(JobExecuter.class.getName());

    private String projdir;

    private HostInfo hostinfo;

    private boolean islocalhost = false;

    private String script;

    private String targetdir;

    private BatchFileTransferer batchTransfer;

    private String subfile;

    private Document jobControlDocument;

    private ProjectInfo projectInfo;

    private String scriptType;

    private boolean isValid = true;

    private ScriptWrapper scriptExecScript;

    private String scriptExecScriptExecString;

    private boolean repbymail = false;

    /** Creates a new instance of RemoteHostExecuter */
    public JobExecuter(ProjectInfo projectInfo, HostInfo hostinfo,
            BatchFileTransferer batchTransfer) {
        this.projectInfo = projectInfo;
        this.projdir = projectInfo.getProjectDirectory();
        this.hostinfo = hostinfo;
        this.batchTransfer = batchTransfer;
        this.jobControlDocument = projectInfo.getProjectPropertyDocument();
        if (!parseJobControlDocument()) {
            logger.error("invalid job-control document.");
            isValid = false;
        }

        if (!preProcessScript()) {
            logger.error("failed to preprocess script.");
            isValid = false;
        }

        batchTransfer.addSftpListener(this);
        targetdir = projectInfo.getTargetDirectory();
    }

    private Vector execScripts = new Vector();

    private Vector otherScripts = new Vector();

    private boolean parseJobControlDocument() {
        if (jobControlDocument == null) {
            logger.error("jobcontrol document is null.");
            return false;
        }

        if (!jobControlDocument.hasRootElement()) {
            logger.error("jobcontrol document does not have a root element.");
            return false;
        }

        Element jobControlElement = jobControlDocument.getRootElement()
                .getChild(JobController.JOBCONTROL);
        if (jobControlElement == null) {
            logger.error("could not find a job-control element");
            return false;
        }

        Element scriptElement = jobControlElement.getChild("script");
        if (scriptElement == null) {
            logger.error("no script element");
            return false;
        }

        scriptType = MyElement.decode(scriptElement
                .getChildTextTrim("script_type"));

        Element execElement = scriptElement.getChild("exec");
        if (execElement == null || execElement.getContentSize() == 0) {
            logger.error("no exec scripts.");
            return false;
        }

        java.util.List execList = execElement.getChildren("exec_script");
        for (int i = 0; i < execList.size(); i++) {
            Element exe = (Element) execList.get(i);
            String type = MyElement.decode(exe.getChildTextTrim("script_type"));
            if (type.equals(scriptType)) {
                execScripts
                        .addElement(new ScriptWrapper(projectInfo
                                .getProjectDirectory()
                                + System.getProperty("file.separator")
                                + MyElement.decode(exe
                                        .getChildTextTrim("script_path")), type));
            }
        }

        return true;
    }

    private boolean preProcessScript() {
        String dir = projectInfo.getProjectDirectory();
        if (!dir.endsWith(System.getProperty("file.separator"))) {
            dir += System.getProperty("file.separator");
        }
        ScriptKeywordsReplacer replacer = new ScriptKeywordsReplacer();

        for (int i = 0; i < execScripts.size(); i++) {
            ScriptWrapper script = (ScriptWrapper) execScripts.get(i);
            replacer.replace(script, jobControlDocument);
            File file = new File(script.getPath());
            String replacedFile = file.getParent()
                    + System.getProperty("file.separator")
                    + ScriptKeywordsReplacer.REPLACED_FILE_PREFIX
                    + file.getName();
            batchTransfer.registerFiles(replacedFile);
        }

        ScriptWrapper[] foo = new ScriptWrapper[execScripts.size()];
        execScripts.copyInto(foo);
        String foobar = ciss.phase_viewer.scripting.scriptexecscripts.ExecScriptCreator
                .getScriptExecScript(scriptType, foo, hostinfo);
        ScriptWrapper tmpScriptExecScript = new ScriptWrapper(foobar,
                ScriptWrapper.getTypeFromFile(foobar));
        replacer.replace(tmpScriptExecScript, jobControlDocument, true);
        String scriptExecPath = dir
                + new File(tmpScriptExecScript.getPath()).getName();
        scriptExecScript = new ScriptWrapper(scriptExecPath,
                ScriptWrapper.getTypeFromFile(scriptExecPath));
        // scriptExecScript = tmpScriptExecScript;
        try {
            new File(scriptExecScript.getPath()).delete();
            ciss.phase_viewer.common.ExternalProgramExecuter.copyBinary(
                    tmpScriptExecScript.getPath(), scriptExecScript.getPath());
        } catch (Exception exc) {
            logger.error("failed to copy script-exec script.");
        }

        if (new File(dir + "report.bsh").exists()
                && new File(dir + "mail.xml").exists()) {
            repbymail = true;

            PrintWriter writer = Utils.getPrintWriter(
                    scriptExecScript.getPath(), true);
            writer.print("\"__JAVA__\" -Dpviewer.home=\"__CHASEDIR__\" -jar \"__CHASEJAR__\" interpreter_mode report.bsh\n");
            if (!hostinfo.isLocalHost()) {
                writer.println("rm -f report.bsh");
                writer.println("rm -f mail.xml");
            }
            new File(dir + "report.bsh").deleteOnExit();
            new File(dir + "mail.xml").deleteOnExit();

            writer.flush();
            writer.close();

            replacer.replace(scriptExecScript, jobControlDocument, true);
            batchTransfer.registerFiles(dir + "report.bsh");
            batchTransfer.registerFiles(dir + "mail.xml");
        }

        scriptExecScriptExecString = ciss.phase_viewer.scripting.scriptexecscripts.ExecScriptCreator
                .getScriptExecScriptExecString(scriptType,
                        scriptExecScript.getPath(), hostinfo);
        batchTransfer.registerFiles(scriptExecScript.getPath());

        return true;
    }

    public void uploadFinished(SftpEvent se) {
        submit();
    }

    public void downloadFinished(SftpEvent se) {
    }

    public void setScriptText(String script) {
        this.script = script;
    }

    public boolean execute() {
        if (!isValid) {
            return false;
        }
        transferFiles();
        return true;
    }

    private void transferFiles() {
        if (!hostinfo.isLocalHost()) {
            batchTransfer.upload();
        } else {
            uploadFinished(null);
        }
    }

    private Session session;

    private void submit() {
        logger.info("submitting " + scriptExecScript.getPath() + " to host : "
                + hostinfo.getName());
        if (!hostinfo.isLocalHost()) {
            // session = batchTransfer.getSession();
            String filename = new File(scriptExecScript.getPath()).getName();
            String chmod = "chmod +x " + targetdir + "/" + filename;
            Session sess = null;
            try {
                sess = SessionCreator.getSession(hostinfo);
            } catch (JSchException e) {
                e.printStackTrace();
            }
            Execute exe = new Execute(sess);
            String ret = exe.command(chmod);
            exe.disconnect();
            if (ret != null && ret.length() != 0) {
                logger.info("info from 'chmod' command:");
                logger.info("\n" + ret);
            }
            for (int i = 0; i < execScripts.size(); i++) {
                String name = new File(
                        ((ScriptWrapper) execScripts.get(i)).getPath())
                        .getName();
                new File(projdir + System.getProperty("file.separator")
                        + ScriptKeywordsReplacer.REPLACED_FILE_PREFIX + name)
                        .delete();
            }
            new File(scriptExecScript.getPath()).delete();
            new Thread(new RemoteExecuterThread()).start();
        } else {
            boolean execDirectly = new Boolean(PropertiesManager
                    .getGlobalProperties(PropertiesManager.PROPERTIES_PVIEWER)
                    .getProperty("exec_bin_directly")).booleanValue()
                    && !scriptType.equals("bsh") && !repbymail;
            if (!execDirectly) {
                try {
                    if (!System.getProperty("os.name").toLowerCase()
                            .startsWith("windows")) {
                        try {
                            Process p = Runtime.getRuntime().exec(
                                    "chmod 755 " + scriptExecScript.getPath());
                            p.waitFor();
                        } catch (Exception exc) {
                            logger.error("failed to chmod");
                            exc.printStackTrace();
                        }
                    }
                    // String phase =
                    // System.getProperty("pviewer.home")+System.getProperty("file.separator")+"bin"+System.getProperty("file.separator")+"ekcal.exe";
                    // new MyThread(phase,"done",new
                    // File(projectInfo.getProjectDirectory()));
                    MyThread thread = new MyThread(scriptExecScriptExecString);
                    logger.info("submitted job to: " + hostinfo.getName());
                    // new Thread(new LocalExecuterThread()).start();
                } catch (Exception exc) {
                    logger.error("failed execution of script: "
                            + scriptExecScript.getPath());
                }
            } else {
                for (int i = 0; i < execScripts.size(); i++) {
                    ScriptWrapper sw = (ScriptWrapper) execScripts.get(i);
                    String newfile = projectInfo.getProjectDirectory()
                            + ScriptKeywordsReplacer.REPLACED_FILE_PREFIX
                            + new File(sw.getPath()).getName();
                    BufferedReader rd = Utils.getReader(newfile);
                    String line = "";
                    try {
                        while ((line = rd.readLine()) != null) {
                            new MyThread(line.trim(), "", new File(
                                    projectInfo.getProjectDirectory()));
                        }
                    } catch (IOException e) {
                    } finally {
                        try {
                            rd.close();
                        } catch (IOException e) {
                        }
                    }
                }
            }

        }
    }

    class LocalExecuterThread implements Runnable {
        public void run() {
            // try{
            // if (
            // !System.getProperty("os.name").toLowerCase().startsWith("windows")
            // ) {
            // try{
            // Process p = Runtime.getRuntime().exec("chmod 755
            // "+scriptExecScript.getPath());
            // p.waitFor();
            // } catch(Exception exc) {
            // logger.error("failed to chmod");
            // exc.printStackTrace();
            // }
            // }
            // logger.info("executing: "+scriptExecScriptExecString);
            // Process p = Runtime.getRuntime().exec(new
            // String[]{scriptExecScriptExecString});
            // BufferedReader bin = null;
            // try {
            // BufferedInputStream is = (BufferedInputStream)
            // p.getInputStream();
            // bin = new BufferedReader(new InputStreamReader(is));
            // while( bin.readLine() != null ) {
            // }
            // } catch(Exception ex) {
            // logger.error("no output");
            // }
            // p.waitFor();
            // // logger.info("executed: "+str);
            // // ExternalProgramExecuter.execute(scriptExecScriptExecString);
            // } catch(Exception exc) {
            // exc.printStackTrace();
            // } finally {
            // }
        }
    }

    class RemoteExecuterThread implements Runnable {
        private org.apache.log4j.Logger logger = org.apache.log4j.Logger
                .getLogger(RemoteExecuterThread.class.getName());

        private Execute exe;

        public void run() {
            Session sess = null;
            try {
                sess = SessionCreator.getSession(hostinfo);
            } catch (JSchException e) {
                e.printStackTrace();
            }
            String filename = new File(scriptExecScript.getPath()).getName();
            Execute exe = new Execute(sess);
            exe.isVerbose(true);
            exe.command(targetdir + "/" + filename);
            // new Thread(new Execute(sess,targetdir+"/"+filename)).start();
            logger.info("submitted job to: " + hostinfo.getName());
            sess.disconnect();
        }
    }

}
