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

import java.io.InputStream;
import java.io.OutputStream;

import javax.swing.JTextArea;

import org.apache.log4j.Logger;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.Session;

/**
 * リモートホストのコマンドを受けて得られた標準出力をJTextAreaに書き出す.
 * 
 * @author
 */
public class RemoteStdOut implements Runnable {
    private Logger logger = Logger.getLogger(RemoteStdOut.class.getName());

    private Session session;

    private JTextArea textArea;

    private String command;

    private boolean stop = false;

    private Process p;

    public static int APPEND = 0;

    public static int REPLACE = 1;

    private int mode = APPEND;

    /**
     * リモートホストのセッション, 書き出し先のtext area, 実行したいコマンドを引数にもつ セッションは, すでにつながっているものとしよう.
     * 
     * @param session
     *            リモートホストのセッション
     * @param command
     *            実行したいコマンド
     * @param textArea
     *            標準出力書き出し先
     */
    public RemoteStdOut(Session session, String command, JTextArea textArea) {
        this.session = session;
        this.textArea = textArea;
        this.command = command;
    }

    public void setMode(int mode) {
        this.mode = mode;
    }

    public void doIt() {
        stop = false;
        new Thread(this).start();
    }

    private Channel channel;

    private OutputStream out;

    private InputStream in;

    private int bufSize = 5096;

    private byte[] tmpBuf = new byte[bufSize];

    public void run() {
        if (session == null) {
            InputStream is = null;
            p = null;
            try {
                p = Runtime.getRuntime().exec(command);
                is = p.getInputStream();
                while (is.available() >= 0 && !stop) {
                    int i = is.read(tmpBuf, 0, bufSize);
                    if (i < 0)
                        break;
                    String str = new String(tmpBuf, 0, i);
                    textArea.append(str);
                    textArea.setCaretPosition(textArea.getText().length());
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            } finally {
                logger.debug("at finally");
                try {
                    is.close();
                } catch (Exception exc) {
                }
                if (p != null) {
                    p.destroy();
                }
            }
        } else {
            if (!session.isConnected()) {
                logger.error("not connected.");
                return;
            }

            try {
                if (channel == null && mode == APPEND) {
                    channel = session.openChannel("shell");
                    channel.connect();
                    out = channel.getOutputStream();
                    in = channel.getInputStream();
                } else if (mode == REPLACE) {
                    channel = session.openChannel("exec");
                    ((ChannelExec) channel).setCommand(command);
                    channel.connect();
                    in = channel.getInputStream();
                }
            } catch (Exception exc) {
                logger.error("failed to open channel.");
                return;
            }

            if (mode == APPEND) {
                try {
                    byte[] tmp = command.getBytes();
                    out.write(tmp, 0, tmp.length);
                    byte[] enter = { (byte) 0x0d };
                    out.write(enter, 0, enter.length);
                    out.flush();
                } catch (Exception exc) {
                }
            }

            try {
                logger.debug("in.available? " + in.available());
                while (in.available() >= 0 && !channel.isEOF()) {
                    if (stop) {
                        textArea.setText("");
                        break;
                    }
                    int i = in.read(tmpBuf, 0, bufSize);
                    if (i < 0)
                        break;
                    synchronized (channel) {
                        String str = new String(tmpBuf, 0, i);
                        if (mode == APPEND) {
                            textArea.append(str);
                            textArea.setCaretPosition(textArea.getText()
                                    .length());
                        } else if (mode == REPLACE) {
                            textArea.setText(str);
                        }
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                logger.debug("at 'finally'");
                if (mode == REPLACE) {
                    try {
                        in.close();
                    } catch (Exception exc) {
                    }
                    channel.disconnect();
                }
            }
        }
    }

    /**
     * いつまでも続く出力の場合, このメソッドを呼べばtext areaへの書き出しを 止めることができる.
     */
    public void terminate() {
        stop = true;
        logger.debug("terminating...");
        if (p != null) {
            p.destroy();
        }
        // if(channel!=null)
        // channel.disconnect();
    }

    protected void finalize() throws Throwable {
        terminate();
    }

}
