/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/08/12, 15:34
!  AUTHOR(S): KOGA, Junichiro
!  File : JobControlPanel.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.plugins.projectmanipulator.phase.jobcontrolpanel;

import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;

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

import ciss.phase_viewer.common.Utils;
import ciss.phase_viewer.file.ChaseFileManager;
import ciss.phase_viewer.file.FileTransferPanel;
import ciss.phase_viewer.jdom.MyElement;
import ciss.phase_viewer.jdom.XMLUtils;
import ciss.phase_viewer.jobcontrol.MonitorPanelPanel;
import ciss.phase_viewer.plugins.projectmanipulator.BasicReportGenerator;
import ciss.phase_viewer.projectbrowser.ProjectBrowserUtils;
import ciss.phase_viewer.projectbrowser.ProjectInfo;
import ciss.phase_viewer.projectbrowser.ProjectInfoChangeEvent;
import ciss.phase_viewer.projectbrowser.ProjectInfoListener;
import ciss.phase_viewer.projectbrowser.ProjectManipulator;
import ciss.phase_viewer.scripting.scriptmanipulator.ScriptManipulator;
import ciss.phase_viewer.settings.PropertiesManager;
import ciss.phase_viewer.ssh.filechooser.RemoteFile;
import ciss.phase_viewer.ssh.hosts.HostInfo;
import ciss.phase_viewer.ssh.hosts.HostSelectorCombo;
import ciss.phase_viewer.ssh.sftp.Sftp;
import ciss.phase_viewer.ssh.sftp.SftpListener;

import com.jcraft.jsch.ChannelSftp;

/**
 * @author
 */
public class JobControlPanel extends ProjectManipulator implements
        ProjectInfoListener {
    /*
     * (non-Javadoc)
     * 
     * @see
     * ciss.phase_viewer.projectbrowser.ProjectInfoListener#projectInfoChanged
     * (ciss.phase_viewer.projectbrowser.ProjectInfoChangeEvent)
     */
    public void projectInfoChanged(ProjectInfoChangeEvent ev) {
        if (fileTransferPanel != null)
            fileTransferPanel
                    .setSelectedHost(ev.getProjectInfo().getHostInfo());

        setDefaultEmailSelected();
    }

    private org.apache.log4j.Logger logger = org.apache.log4j.Logger
            .getLogger(JobControlPanel.class.getName());

    private JobControlSubPanel subPanel;
    private FileTransferPanel fileTransferPanel;

    /** Creates new form JobControlPanel */
    public JobControlPanel(ProjectInfo projectInfo) {
        super(projectInfo);
        // initComponents();
        // ProjectBrowserUtils.setHtmlToEditorPane(jEditorPane1,getClass().getResource("/ciss/phase_viewer/plugins/projectmanipulator/phase/jobcontrolpanel/jobcontrol.html"));
    }

    private String reportGeneratorPath;
    private MonitorPanelPanel monpanel;

    public void init() {
        projectInfo.addProjectInfoListener(this);
        reportGeneratorPath = BasicReportGenerator
                .getReportGeneratorPath(projectInfo);

        JTabbedPane tabbedPane = new JTabbedPane();
        subPanel = new JobControlSubPanel();
        browser = new ScriptManipulator(projectInfo);

        // ftpanel.addActionListenerToHostSelector(browser);
        fileTransferPanel.addActionListenerToHostSelector(browser);
        // tabbedPane.addTab("job control",subPanel);
        // tabbedPane.addTab("manipulate script",browser);
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        setBorder(new javax.swing.border.TitledBorder(null,
                "Job control panel",
                javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
                javax.swing.border.TitledBorder.DEFAULT_POSITION,
                new java.awt.Font("MS Sans Serif", 0, 11), new java.awt.Color(
                        0, 51, 153)));
        add(subPanel);
        monpanel = new MonitorPanelPanel(projectInfo,
                fileTransferPanel.getSelectedHost());
        fileTransferPanel.addActionListenerToHostSelector(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                if (monpanel != null)
                    monpanel.setTargetHost(fileTransferPanel.getSelectedHost());
            }
        });

        subPanel.add(monpanel);
        revalidate();

        setDefaultEmailSelected();

    }

    private ScriptManipulator browser;

    public void postInit() {
        browser.adjust();
    }

    public void initializeProject() {
    }

    public boolean postProcess() {
        subPanel.save();
        return true;
    }

    private String REPORT_GEN_PATH = "__REPORT_GEN_PATH__";
    private String reportFileName = "report.bsh";
    private String reportbsh = "import ciss.phase_viewer.main.*;\n"
            + "import ciss.phase_viewer.jobcontrol.*;\n" + "reportGenPath=\""
            + REPORT_GEN_PATH + "\";\n"
            + "generator=PluginLoader.instantiate(reportGenPath,null);\n"
            + "reportByMail(generator);";

    private String[] emailTags = { "mail_smtphost", "mail_from_address",
            "mail_from_user", "mail_to_address" };

    private JCheckBox email;

    private void toggleReport() {
        File reportFile = new File(projectInfo.getProjectDirectory()
                + System.getProperty("file.separator") + reportFileName);
        File mailxml = new File(projectInfo.getProjectDirectory()
                + System.getProperty("file.separator") + "mail.xml");
        if (email.isSelected()) {
            String str = reportbsh.replaceAll(REPORT_GEN_PATH,
                    reportGeneratorPath);
            PrintWriter writer = Utils.getPrintWriter(reportFile
                    .getAbsolutePath());
            if (writer != null) {
                writer.print(str);
                writer.flush();
                writer.close();
            }

            Document document = new Document();
            Element rootElement = new Element("root");
            HostInfo hostInfo = projectInfo.getHostInfo();
            for (int i = 0; i < emailTags.length; i++)
                rootElement.addContent(new Element(emailTags[i])
                        .setText(MyElement.encode(hostInfo
                                .getProperty(emailTags[i]))));
            document.setRootElement(rootElement);
            XMLUtils.saveDocumentTo(document, mailxml);
        } else {
            reportFile.delete();
            mailxml.delete();
        }
    }

    private void setDefaultEmailSelected() {
        if (email == null)
            return;

        boolean valid = true;
        for (int i = 0; i < emailTags.length; i++)
            if (projectInfo.getHostInfo().getProperty(emailTags[i]) == null
                    || projectInfo.getHostInfo().getProperty(emailTags[i])
                            .trim().length() == 0)
                valid = false;

        if (!valid) {
            email.setSelected(false);
            email.setEnabled(false);
            return;
        }

        email.setEnabled(true);

        if (new Boolean(projectInfo.getHostInfo().getProperty("mail_default"))
                .booleanValue()) {
            email.setSelected(true);
            toggleReport();
        } else {
            email.setSelected(false);
            toggleReport();
        }
    }

    private JPanel pp;

    private String MPI_PARAMS = "MPI_Params";

    class JobControlSubPanel extends JPanel {
        private org.apache.log4j.Logger logger = org.apache.log4j.Logger
                .getLogger(JobControlSubPanel.class.getName());
        private HostSelectorCombo hosts;
        private String baseDir;
        private ChaseFileManager cfManager;

        private JTextField ne;
        private JTextField nk;
        private JTextField np;

        private JButton bexe;
        private JButton bedit;
        private JButton bstop;

        protected JobControlSubPanel() {
            super();
            setLayout(new java.awt.GridLayout(2, 0));

            this.baseDir = projectInfo.getProjectDirectory()
                    + System.getProperty("file.separator");
            this.cfManager = projectInfo.getChaseFileManager();

            // ftpanel = new PhaseFileTransferPanel(projectInfo);
            /*
             * fileTransferPanel = (FileTransferPanel)
             * projectInfo.getGUI(FileTransferPanel.class); if (
             * fileTransferPanel == null ) { fileTransferPanel = new
             * FileTransferPanel(projectInfo);
             * projectInfo.registerGUI(fileTransferPanel); }
             */
            fileTransferPanel = new FileTransferPanel(projectInfo);
            pp = new JPanel();
            pp.setLayout(new BoxLayout(pp, BoxLayout.Y_AXIS));
            pp.add(fileTransferPanel);
            pp.add(createMPIPanel());
            pp.add(createExePanel());
            JScrollPane pane = new JScrollPane(pp);
            add(pane);
            // add(new HTMLPanel());
        }

        class HTMLPanel extends JPanel {
            HTMLPanel() {
                setLayout(new GridLayout());
                initComponents();
            }

            private void initComponents() {
            }

        }

        private JPanel createMPIPanel() {

            String snp = "1";
            String sne = "1";
            String snk = "1";

            Element root = projectInfo.getProjectPropertyDocument()
                    .getRootElement();
            Element mpiparams = root.getChild(MPI_PARAMS);
            if (mpiparams != null) {
                snp = mpiparams.getChildTextTrim("np");
                sne = mpiparams.getChildTextTrim("ne");
                snk = mpiparams.getChildTextTrim("nk");
                try {
                    Integer.parseInt(snp);
                    Integer.parseInt(sne);
                    Integer.parseInt(snk);
                } catch (NumberFormatException nfe) {
                    snp = "1";
                    sne = "1";
                    snk = "1";
                }
            }
            boolean enableMPI = new Boolean(PropertiesManager
                    .getGlobalProperties(PropertiesManager.PROPERTIES_PVIEWER)
                    .getProperty("mpienable")).booleanValue();
            JPanel ret = new JPanel();
            ret.setLayout(new BoxLayout(ret, BoxLayout.X_AXIS));
            ret.setBorder(new TitledBorder(
                    "MPI parameters (note: np = ne x nk)"));

            JPanel pnp = new JPanel();
            pnp.setBorder(new TitledBorder("np"));
            np = new JTextField(snp, 10);
            pnp.add(np);

            JPanel pnk = new JPanel();
            pnk.setBorder(new TitledBorder("nk"));
            nk = new JTextField(snk, 10);
            pnk.add(nk);

            JPanel pne = new JPanel();
            pne.setBorder(new TitledBorder("ne"));
            ne = new JTextField(sne, 10);
            pne.add(ne);

            np.setEnabled(enableMPI);
            nk.setEnabled(enableMPI);
            ne.setEditable(enableMPI);
            np.setBackground(Color.WHITE);
            nk.setBackground(Color.WHITE);
            ne.setBackground(Color.WHITE);

            ret.add(pnp);
            ret.add(pnk);
            ret.add(pne);

            // add caret listeners which forces a valid input for np, nk, and ne
            // ...
            // pending ...
            np.addCaretListener(new CaretListener() {
                public void caretUpdate(CaretEvent ce) {
                    save();
                }
            });

            nk.addCaretListener(new CaretListener() {
                public void caretUpdate(CaretEvent ce) {
                    save();
                    try {
                        int ink = Integer.parseInt(nk.getText().trim());
                        int ine = Integer.parseInt(ne.getText().trim());
                        int inp = Integer.parseInt(np.getText().trim());
                        if (ink * ine != inp)
                            np.setText(String.valueOf(ink * ine));
                    } catch (Exception ex) {
                    }
                }
            });

            ne.addCaretListener(new CaretListener() {
                public void caretUpdate(CaretEvent ce) {
                    save();
                    try {
                        int ink = Integer.parseInt(nk.getText().trim());
                        int ine = Integer.parseInt(ne.getText().trim());
                        int inp = Integer.parseInt(np.getText().trim());
                        if (ink * ine != inp)
                            np.setText(String.valueOf(ink * ine));
                    } catch (Exception ex) {
                    }
                }
            });

            return ret;
        }

        private JButton bmon;


        private JPanel createExePanel() {
            JPanel ret = new JPanel();
            ret.setBorder(new TitledBorder("execution control"));

            bexe = new JButton("execute");
            ret.add(bexe);
            bmon = new JButton("monitor");
            // ret.add(bmon);

            String cluster=System.getProperty("cluster_support");
            if(cluster!=null&&cluster.equals("true")) {
                bedit = new JButton("edit job script");
                ret.add(bedit);
            }

            bstop = new JButton("terminate");
            ret.add(bstop);

            email = new JCheckBox("notify end of job by email");
            // ret.add(email);

            fileTransferPanel
                    .addActionListenerToHostSelector(new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
//                            boolean loc_and_win = fileTransferPanel
//                                    .getSelectedHost().isLocalHost()
//                                    && Utils.isWindows();
                            // bmon.setEnabled(!loc_and_win);
                        }
                    });

            bexe.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (!ciss.phase_viewer.jobcontrol.JobController.execute(
                            projectInfo, fileTransferPanel.getSelectedHost())) {
                        logger.error("failed execution");
                    } else {
                        monpanel.reset();
                        monpanel.run();
                    }
                }
            });

            if(cluster!=null&&cluster.equals("true"))
                bedit.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent arg0) {
                        String pdir = projectInfo.getProjectDirectory()
                                + System.getProperty("file.separator");
                        String submit = "submit.sh";
                        if (projectInfo.getHostInfo().getName().equals("localhost")
                                && Utils.isWindows())
                            submit = "submit.bat";
                        ProjectBrowserUtils.bootTextViewer(pdir + submit);
                    }
                });

            bstop.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    terminate();
                }
            });

            email.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    toggleReport();
                }
            });
            return ret;
        }

        private void terminate() {
            new Thread(new NFSTOP()).start();
        }

        class NFSTOP implements Runnable, SftpListener {
            public void run() {
                Element root = projectInfo.getProjectPropertyDocument()
                        .getRootElement();
                Element jobcontrol = root.getChild("jobcontrol");
                Element script = null;
                if (jobcontrol != null) {
                    try {
                        script = jobcontrol.getChild("script").getChild(
                                "script_type");
                    } catch (Exception e) {
                    }
                }

                String nfstopname = "nfstop.data";
                boolean bshscr = false;
                if (script != null
                        && script.getTextTrim().equalsIgnoreCase("bsh")) {
                    bshscr = true;
                    nfstopname = "bshstop.data";
                }

                String dir = projectInfo.getProjectDirectory();

                if (bshscr) {
                    logger.debug("stopping bsh script");
                    File filestop = new File(dir
                            + System.getProperty("file.separator") + nfstopname);
                    try {
                        filestop.createNewFile();
                    } catch (IOException ioe) {
                    }
                    if (!projectInfo.getHostInfo().isLocalHost()) {
                        Sftp sftp = new Sftp(projectInfo.getHostInfo());
                        sftp.addSftpListener(this);
                        sftp.connect();
                        ChannelSftp c = sftp.getChannelSftp();
                        RemoteFile[] rfs = new RemoteFile[1];
                        rfs[0] = new RemoteFile(filestop.getAbsolutePath());
                        String targetdir = projectInfo.getTargetDirectory();
                        sftp.upload(rfs, new RemoteFile(targetdir, c), dir);
                    } else {
                        logger.info("submitted bshstop.data");
                    }
                    return;
                }

                String nfstop = dir + System.getProperty("file.separator")
                        + nfstopname;
                PrintWriter writer = null;
                try {
                    writer = new PrintWriter(new BufferedWriter(new FileWriter(
                            nfstop)));
                    writer.println("1");
                    writer.flush();
                } catch (Exception exc) {
                } finally {
                    try {
                        writer.close();
                    } catch (Exception exc) {
                    }
                }

                HostInfo hinfo = projectInfo.getHostInfo();
                if (hinfo.isLocalHost()) {
                    logger.info("submitted nfstop.data");
                } else {
                    Sftp sftp = new Sftp(hinfo);
                    sftp.addSftpListener(this);
                    sftp.connect();
                    ChannelSftp c = sftp.getChannelSftp();
                    RemoteFile[] rfs = new RemoteFile[1];
                    rfs[0] = new RemoteFile(nfstop);
                    String targetdir = projectInfo.getTargetDirectory();
                    sftp.upload(rfs, new RemoteFile(targetdir, c), dir);
                }
            }

            public void uploadFinished(ciss.phase_viewer.ssh.sftp.SftpEvent e) {
                logger.info("submitted nfstop.data to "
                        + projectInfo.getHostInfo().getName() + ":"
                        + projectInfo.getTargetDirectory());
                String dir = projectInfo.getProjectDirectory();
                String nfstop = dir + System.getProperty("file.separator")
                        + "nfstop.data";
                new File(nfstop).delete();
            }

            public void downloadFinished(ciss.phase_viewer.ssh.sftp.SftpEvent e) {
            }

        }

        /**
         * mpiのパラメターに整合性があるかどうかチェック... まあいずれちゃんとやろう.
         */
        private void checkMPIParams(String mode) {
            String snp = np.getText();
            String snk = nk.getText();
            String sne = ne.getText();

            int inp;
            boolean pvalid = true;
            int ink;
            boolean kvalid = true;
            int ine;
            boolean evalid = true;

            try {
                inp = Integer.parseInt(snp);
            } catch (NumberFormatException nfe) {
                inp = 1;
                pvalid = false;
            }

            try {
                ink = Integer.parseInt(snk);
            } catch (NumberFormatException nfe) {
                ink = 1;
                kvalid = false;
            }

            try {
                ine = Integer.parseInt(sne);
            } catch (NumberFormatException nfe) {
                ine = 1;
                evalid = false;
            }

            if (inp != ink * ine) {
                if (mode.equals("np")) {
                }
            }
        }

        protected void save() {
            String snp = np.getText();
            String snk = nk.getText();
            String sne = ne.getText();
            try {
                Integer.parseInt(snp);
                Integer.parseInt(snk);
                Integer.parseInt(sne);
            } catch (NumberFormatException nfe) {
                snp = "1";
                snk = "1";
                sne = "1";
            }

            Element root = projectInfo.getProjectPropertyDocument()
                    .getRootElement();
            Element mpiparams = root.getChild(MPI_PARAMS);
            if (mpiparams != null) {
                mpiparams.getChild("np").setText(snp);
                mpiparams.getChild("nk").setText(snk);
                mpiparams.getChild("ne").setText(sne);
            } else {
                Element mpi_params = new Element(MPI_PARAMS);
                root.addContent(mpi_params);
                mpi_params.addContent(new Element("np").setText(snp));
                mpi_params.addContent(new Element("nk").setText(snk));
                mpi_params.addContent(new Element("ne").setText(sne));
            }
            projectInfo.saveProjectPropertyDocument();
        }

    }

}
