/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2006/06/22, 18:25
!  AUTHOR(S): KOGA, Junichiro
!  File : MonitorPanel.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.jobcontrol;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyVetoException;
import java.util.HashMap;
import java.util.Set;
import java.util.Vector;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToggleButton;
import javax.swing.border.TitledBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;

import org.jfree.chart.event.ChartProgressEvent;
import org.jfree.chart.event.ChartProgressListener;

import ciss.phase_viewer.common.Resources;
import ciss.phase_viewer.file.ChaseFile;
import ciss.phase_viewer.file.Monitorable;
import ciss.phase_viewer.main.PluginLoader;
import ciss.phase_viewer.mainpanel.InternalFrameChase;
import ciss.phase_viewer.projectbrowser.ProjectInfo;

import com.jcraft.jsch.Session;

/**
 * ジョブ監視のためのパネル
 * 
 * @author
 */
public class MonitorPanel extends InternalFrameChase implements
        ChartProgressListener, MonitorPanelInterface {
    private boolean drawingChart = false;

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.jfree.chart.event.ChartProgressListener#chartProgress(org.jfree.chart
     * .event.ChartProgressEvent)
     */
    public void chartProgress(ChartProgressEvent arg0) {
        if (arg0.getType() == ChartProgressEvent.DRAWING_STARTED)
            drawingChart = true;
        else if (arg0.getType() == ChartProgressEvent.DRAWING_FINISHED)
            drawingChart = false;
    }

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

    private ProjectInfo projectInfo;

    private ChaseFile[] chaseFile;

    private Session session;

    /**
     * ProjectInfoは特に注意はいらない. Sessionについては, すでにコネクションが確立しているものと 仮定する.
     * nullの場合はlocalhost.
     * 
     * @param projectInfo
     *            モニターしたいプロジェクトの情報
     * @param session
     *            対象ホストのセッション; nullの場合localhost.
     */
    public MonitorPanel(ProjectInfo projectInfo, Session session) {
        super("host: " + projectInfo.getHostInfo().getName() + " dir: "
                + projectInfo.getTargetDirectory(), true, true, true, true,
                new Dimension(640, 500));
        this.projectInfo = projectInfo;
        this.session = session;
        init();
    }

    private void init() {
        ChaseFile[] cfs = projectInfo.getChaseFileManager().getFiles();
        if (cfs == null || cfs.length == 0) {
            logger.error("invalid files!");
            dispose();
            return;
        }

        Vector validFiles = new Vector();
        for (int i = 0; i < cfs.length; i++) {
            logger.debug("cfs: " + cfs[i]);
            if (cfs[i].isMonitorable()) {
                validFiles.addElement(cfs[i]);
            }
        }

        if (validFiles.size() == 0) {
            logger.error("no monitorable files.");
            dispose();
            return;
        }

        chaseFile = new ChaseFile[validFiles.size()];
        validFiles.copyInto(chaseFile);

        JPanel fileMonitor = getFileMonitorPanel();
        addInternalFrameListener(new InternalFrameAdapter() {
            public void internalFrameClosed(
                    javax.swing.event.InternalFrameEvent e) {
                stopAll();
                if (session != null) {
                    session.disconnect();
                }
            }

            public void internalFrameClosing(
                    javax.swing.event.InternalFrameEvent e) {
                stopAll();
                if (session != null) {
                    session.disconnect();
                }
            }

        });

        JButton btnclose = new JButton("close");
        Container container = getContentPane();
        container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
        container.add(fileMonitor);
        container.add(btnclose);
        btnclose.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                dispose();
            }
        });

        addInternalFrameListener(new InternalFrameListener() {

            public void internalFrameActivated(InternalFrameEvent e) {
            }

            public void internalFrameClosed(InternalFrameEvent e) {
                if (session != null)
                    session.disconnect();
            }

            public void internalFrameClosing(InternalFrameEvent e) {
            }

            public void internalFrameDeactivated(InternalFrameEvent e) {
            }

            public void internalFrameDeiconified(InternalFrameEvent e) {
            }

            public void internalFrameIconified(InternalFrameEvent e) {
            }

            public void internalFrameOpened(InternalFrameEvent e) {
            }

        });
    }

    private JComboBox fileSelector;

    private JTextArea monitorTextArea;

    private Monitorable monitorable;

    private JToggleButton monitorButton;

    private JToggleButton graphButton;

    /**
     * グラフ描画ボタンのsetSelectedメソッドを呼ぶ.
     * 
     * @param sel
     *            選択したい場合true.
     */
    public void setGraphButtonSelected(boolean sel) {
        if (graphButton != null) {
            logger.debug("calling graphButton.setSelected(" + sel + ")");
            ActionListener[] al = graphButton.getActionListeners();
            if (al != null)
                for (int i = 0; i < al.length; i++)
                    graphButton.removeActionListener(al[i]);

            graphButton.setSelected(sel);

            if (al != null)
                for (int i = 0; i < al.length; i++)
                    graphButton.addActionListener(al[i]);
        }
    }

    private JPanel getFileMonitorPanel() {
        JPanel p = new JPanel();
        p.setLayout(new BorderLayout());
        JPanel pbtns = new JPanel();
        pbtns.setLayout(new BoxLayout(pbtns, BoxLayout.X_AXIS));

        String[] idents = new String[chaseFile.length];
        for (int i = 0; i < idents.length; i++) {
            idents[i] = chaseFile[i].getIdentifier().getValue();
        }
        fileSelector = new JComboBox(idents);
        monitorButton = new JToggleButton(
                Resources.getIcon(Resources.VCRPlay20));
        monitorButton.setToolTipText("toggle monitor");

        graphButton = new JToggleButton(
                Resources.getIcon(Resources.LINEGRAPH20));
        graphButton.setEnabled(false);

        JPanel psele = new JPanel();
        psele.setBorder(new TitledBorder("select file to monitor"));
        psele.setLayout(new BoxLayout(psele, BoxLayout.X_AXIS));
        psele.add(fileSelector);
        pbtns.add(psele);
        pbtns.add(monitorButton);
        pbtns.add(graphButton);
        p.add(pbtns, BorderLayout.NORTH);

        monitorTextArea = new JTextArea();
        monitorTextArea.setEditable(false);
        JScrollPane scrpane = new JScrollPane(monitorTextArea);
        p.add(scrpane, BorderLayout.CENTER);

        fileSelector.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                fileSelected();
            }
        });

        monitorButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                if (monitorable != null && monitorButton.isSelected()) {
                    monitorable.monitor(session, monitorTextArea);
                    graphButton.setEnabled(monitorable
                            .isGraphCreatable(monitorTextArea));
                } else if (monitorable != null && !monitorButton.isSelected()) {
                    monitorable.stopMonitor();
                    graphButton.setEnabled(false);
                    graphButton.setSelected(false);
                    monitorable.destroyGraph();
                    try {
                        setSelected(true);
                    } catch (PropertyVetoException pve) {
                    }
                }
            }
        });

        graphButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                if (monitorable != null) {
                    if (graphButton.isSelected()) {
                        monitorable.createGraphFrom(monitorTextArea);
                    } else {
                        logger.debug("destroying graph");
                        monitorable.destroyGraph();
                        try {
                            setSelected(true);
                        } catch (PropertyVetoException pve) {
                        }
                    }
                }
            }
        });

        monitorTextArea.addCaretListener(new CaretListener() {
            public void caretUpdate(CaretEvent ce) {
                if (monitorable != null)
                    graphButton.setEnabled(monitorable
                            .isGraphCreatable(monitorTextArea));
                else
                    graphButton.setEnabled(false);
            }
        });

        fileSelector.setSelectedIndex(0);

        return p;
    }

    private HashMap monitorCache = new HashMap();

    private void fileSelected() {
        monitorButton.setSelected(false);
        setGraphButtonSelected(false);
        graphButton.setEnabled(false);
        if (monitorable != null) {
            monitorable.stopMonitor();
            monitorable.destroyGraph();
        }

        monitorTextArea.setText("");
        ChaseFile selectedFile = chaseFile[fileSelector.getSelectedIndex()];
        Object obj = monitorCache.get(selectedFile.getIdentifier().getValue());
        if (obj == null) {
            String path = selectedFile.getMonitorPath();
            monitorable = (Monitorable) PluginLoader.instantiate(path,
                    new Object[] { projectInfo });
            monitorable.setMonitorPanel(this);
            monitorCache.put(selectedFile.getIdentifier().getValue(),
                    monitorable);
        } else {
            monitorable = (Monitorable) obj;
        }

        try {
            setSelected(true);
        } catch (PropertyVetoException pve) {
        }
    }

    private void stopAll() {
        Set keySet = monitorCache.keySet();
        if (keySet == null) {
            return;
        }
        Object[] foo = keySet.toArray();
        if (foo == null || foo.length == 0) {
            return;
        }
        for (int i = 0; i < foo.length; i++) {
            Monitorable mon = (Monitorable) monitorCache.get(foo[i]);
            if (mon != null) {
                mon.stopMonitor();
            }
        }
    }

    protected void finalize() throws Throwable {
        stopAll();
        if (session != null) {
            session.disconnect();
        }
    }

}
