/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : ExternalProgramExecuter.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.common;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import org.apache.log4j.Logger;

public class ExternalProgramExecuter {
    private static Logger logger = Logger
            .getLogger(ExternalProgramExecuter.class.getName());
    /** the program path string */
    private String programPath = new String();

    /** process */
    private Process process;

    public static final int NORMAL = 0; /* !< 正常終了の際に返すint値 */
    public static final int EXCEPTION = 1; /* !< 例外が発生した際に返すint値 */

    public ExternalProgramExecuter() {
    }

    /**
     * @param programPath
     *            実行すべきプログラムのパス
     */
    public ExternalProgramExecuter(String programPath) {
        this.programPath = programPath;
    }

    /**
     * プログラムパスに文字列を足す.
     * 
     * @param str
     *            足す文字列
     */
    public void addProgramPath(String str) {
        programPath = programPath + str;
    }

    /** プログラム実行用文字列を初期化する. */
    public void clearProgramPath() {
        programPath = new String();
    }

    /**
     * @return 実行すべきプログラムのパス
     */
    public String getProgramPath() {
        return this.programPath;
    }

    /**
     * このクラスが保持する, 「実行すべき文字列」を実行する.
     */
    public void execute() throws Exception {
        process = Runtime.getRuntime().exec(programPath);
        logger.info("executed: " + programPath);
        // process.waitFor();
    }

    /**
     * @return 実行したプロセスオブジェクトの参照.
     */
    public Process getProcess() {
        return this.process;
    }

    /**
     * 直接実行したいプログラムのパスを指定し, 実行するメソッド.
     * 
     * @param str
     *            実行したいプログラムのパス.
     */
    public static void execute(String str) throws Exception {
        Process p = Runtime.getRuntime().exec(str);
        logger.info("executed: " + str);
        p.waitFor();
    }

    /**
     * 直接実行したいプログラムのパスを指定し, 実行するメソッド. 標準出力からの情報も欲しい場合こちらを使用する.
     * 
     * @param str
     *            実行したいプログラムのパス.
     * @return 標準出力と標準エラー出力の情報を保持するBufferedReader; 0番目の要素が
     *         標準出力で1番目の要素が標準エラー出力に相当するが, 標準エラー出力の方は現在うまく 動作しない.
     * @param str
     *            実行したいプログラムのパス.
     * @return 実行したプログラムが標準出力・標準エラー出力に書き出す情報を保持した BufferedReader.
     */
    public static BufferedReader[] executeGetBR(String str) throws IOException {
        return executeGetBR(str, null);
    }

    /**
     * 標準出力で1番目の要素が標準エラー出力に相当するが, 標準エラー出力の方は現在うまく 動作しない.
     * 
     * @param str
     *            実行したいプログラムのパス.
     * @param dir
     *            作業ディレクトリー
     * @return 実行したプログラムが標準出力・標準エラー出力に書き出す情報を保持した BufferedReader.
     */
    public static BufferedReader[] executeGetBR(String str, File dir)
            throws IOException {

        BufferedReader[] bin = new BufferedReader[2];

        Process p = Runtime.getRuntime().exec(str, null, dir);
        InputStream ie;
        InputStream is;
        ie = p.getErrorStream();
        bin[1] = new BufferedReader(new InputStreamReader(ie));
        is = p.getInputStream();
        bin[0] = new BufferedReader(new InputStreamReader(is));
        return bin;
    }

    /**
     * 標準出力で1番目の要素が標準エラー出力に相当するが, 標準エラー出力の方は現在うまく 動作しない. 環境変数も渡せる.
     * 
     * @param str
     *            実行したいプログラムのパス.
     * @param dir
     *            作業ディレクトリー
     * @param envp
     *            環境変数の配列; Runtimeクラスを参照.
     * @return 実行したプログラムが標準出力・標準エラー出力に書き出す情報を保持した BufferedReader.
     */
    public static BufferedReader[] executeGetBR(String str, File dir,
            String[] envp) throws IOException {

        BufferedReader[] bin = new BufferedReader[2];

        Process p = Runtime.getRuntime().exec(str, envp, dir);
        InputStream ie;
        InputStream is;
        ie = p.getErrorStream();
        bin[1] = new BufferedReader(new InputStreamReader(ie));
        is = p.getInputStream();
        bin[0] = new BufferedReader(new InputStreamReader(is));
        return bin;
    }

    /**
     * プログラムを直接起動できるメソッド. このメソッドは, waitForパラメターが真の場合実行した外部プログラム が終了するまで次に進まない.
     * 
     * @param str
     *            実行すべきプログラムのパス.
     * @param waitFor
     *            真のとき実行した外部プログラムが終了するまで待つ.
     * @return 実行したプログラムが標準出力・標準エラー出力に書き出す情報を保持した BufferedReader.
     */
    public static BufferedReader[] executeGetBR(String str, boolean waitFor)
            throws Exception {

        BufferedReader[] bin = new BufferedReader[2];

        Process p = Runtime.getRuntime().exec(str);
        BufferedInputStream ie;
        BufferedInputStream is;
        try {
            ie = (BufferedInputStream) p.getErrorStream();
            bin[1] = new BufferedReader(new InputStreamReader(ie));
        } catch (Exception e) {
            // System.out.println("no error out (?)");
        }
        try {
            is = (BufferedInputStream) p.getInputStream();
            bin[0] = new BufferedReader(new InputStreamReader(is));
        } catch (Exception ex) {
            logger.error("no output");
        }

        if (waitFor) {
            p.waitFor();
        }
        return bin;
    }

    /**
     * テキストデータのファイルをコピーするメソッド. OSに依存しない. また, コピー元とコピー先が同じファイルだった場合何もしない.
     * 
     * @param str1
     *            コピー元
     * @param str2
     *            コピー先
     */
    public static void copy(String str1, String str2) throws IOException {
        if (str1.trim().equalsIgnoreCase(str2.trim())) {
            logger.error("can't copy ... same file!");
            return;
        }
        String strBuff;
        BufferedReader brInFile = new BufferedReader(new FileReader(str1));
        PrintWriter pwOutFile = new PrintWriter(new BufferedWriter(
                new FileWriter(str2)));

        while ((strBuff = brInFile.readLine()) != null) {
            pwOutFile.println(strBuff);
        }
        brInFile.close();
        pwOutFile.close();
    }

    /**
     * バイナリーデータのファイルをコピーするメソッド. OSに依存しない. また, コピー元とコピー先が同じファイルだった場合何もしない.
     * 
     * @param binaryFile1
     *            コピー元
     * @param binaryFile2
     *            コピー先
     */
    public static void copyBinary(String binaryFile1, String binaryFile2)
            throws IOException {
        if (binaryFile1.trim().equalsIgnoreCase(binaryFile2.trim())) {
            logger.debug("can't copy ... same file!");
            return;
        }
        BufferedInputStream bistream = new BufferedInputStream(
                new FileInputStream(binaryFile1));
        BufferedOutputStream bostream = new BufferedOutputStream(
                new FileOutputStream(binaryFile2));

        int indata;

        while ((indata = bistream.read()) != -1) {
            bostream.write(indata);
        }

        bostream.flush();
        bistream.close();
        bostream.close();
    }

    /**
     * ファイル削除用メソッド.
     * 
     * @param fileName
     *            削除したいファイル.
     */
    public static void remove(String fileName) {
        File file = new File(fileName);
        if (file.exists()) {
            file.delete();
        }
        return;
    }

    /**
     * ファイル移動用のメソッド. 移動元と移動先が同一の場合なにもしない. Javaのリネーム機能を利用する.
     * 
     * @param fileName1
     *            移動元
     * @param fileName2
     *            移動先
     * @return ファイル移動が成功したら真.
     */
    public static boolean move(String fileName1, String fileName2) {
        logger.info("moving file " + fileName1 + " to " + fileName2);
        boolean bret = false;
        if (fileName1.trim().equalsIgnoreCase(fileName2.trim())) {
            logger.error("can't move ... same file!");
            return false;
        }
        File file1 = new File(fileName1);
        File file2 = new File(fileName2);
        bret = file1.renameTo(file2);
        return bret;
    }

}
