/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/11/07, 18:07
!  AUTHOR(S): KOGA, Junichiro
!  File : OutputParser.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.outputinterface;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.Serializable;
import java.util.Vector;

import org.apache.log4j.Logger;

import ciss.phase_viewer.mainpanel.ChaseProgressMonitor;
import ciss.phase_viewer.mainpanel.ProgressInfo;

/**
 * 出力データを, ファイルからパースするクラス. このクラスは各々のデータについて 色々実装する必要があるが, こればかりは仕方がない.
 * 
 * @author
 */
public abstract class OutputParser implements Serializable, ProgressInfo {
    private Logger logger = Logger.getLogger(OutputParser.class.getName());

    protected Vector fileNames;
    protected String fileName;
    protected Vector voutputData = new Vector();

    protected LineNumberReader reader;
    protected LineNumberReader[] readers;

    private OutputInterface parent;

    protected int numLines;
    protected boolean isDone = false;

    private Vector parserListeners = new Vector();

    /**
     * Creates a new instance of OutputParser
     * 
     * @param fileName
     *            このクラスがパースすべきファイルのファイル名
     */
    public OutputParser(String fileName) {
        this.fileName = fileName;
        fileNames = new Vector();
    }

    /**
     * このパーサーが読み込むファイル名への参照を返す
     * 
     * @return ファイル名
     */
    public String getFileName() {
        return fileName;
    }

    public String[] getFileNames() {
        String[] boo = new String[fileNames.size()];
        fileNames.copyInto(boo);
        return boo;
    }

    public void addFile(String file) {
        fileNames.add(file);
    }

    public void resetFiles() {
        fileNames = new Vector();
    }

    /**
     * listenerを登録
     * 
     * @param listener
     *            このパーサーに関連付けられたlistener
     */
    public void addParserListener(OutputParserListener listener) {
        for (int i = 0; i < parserListeners.size(); i++) {
            if (listener == parserListeners.get(i)) { // 同じリスナーは再登録しない!
                return;
            }
        }
        parserListeners.addElement(listener);
    }

    /**
     * parse済みかどうかを判定
     * 
     * @return parse済みならtrue
     */
    public boolean getParsed() {
        return this.parsed;
    }

    private boolean parsed = false;

    /**
     * 一度doParseメソッドが呼ばれたらその後はdoParseメソッドを実行しても何も起こらない.
     * 再度doParseメソッドを有効にするにはここでfalseをセットする.
     * 
     * @param falseの場合
     *            , 一度parse済みであっても再parseする.
     */
    public void setParsed(boolean parsed) {
        this.parsed = parsed;
    }

    /**
     * parseメソッドを呼ぶ.
     */
    public void doParse() {
        if (parsed) {
            return;
        }

        initializeOutputDataVector();
        if (new File(fileName).exists()) {
            try {
                FileReader freader = new FileReader(fileName);
                reader = new LineNumberReader(freader);
            } catch (IOException ioe) {
                logger.error("failed to open file: " + fileName);
                ioe.printStackTrace();
            }
            numLines = ciss.phase_viewer.common.Utils.getNumLines(fileName);
            if (numLines < 0)
                numLines = 0;
        }
        for (int i = 0; i < fileNames.size(); i++) {
            numLines += ciss.phase_viewer.common.Utils.getNumLines(fileNames
                    .get(i).toString());
        }

        readers = new LineNumberReader[fileNames.size()];
        for (int i = 0; i < fileNames.size(); i++) {
            reader = readers[i];
            try {
                FileReader freader = new FileReader(fileNames.get(i).toString());
                readers[i] = new LineNumberReader(freader);
            } catch (IOException ioe) {
                logger.error("failed to open file: "
                        + fileNames.get(i).toString());
                ioe.printStackTrace();
            }
        }

        ChaseProgressMonitor monitor = ChaseProgressMonitor.getMonitor();
        monitor.setProgress(this);
        //
        // boolean ret = parse();
        // for ( int i=0 ; i<parserListeners.size() ; i++ ) {
        // ((OutputParserListener) parserListeners.get(i)).parseFinished();
        // }
        if (invokeNewThread) {
            new Thread(new Parse()).start();
        } else {
            parse();
            for (int i = 0; i < parserListeners.size(); i++) {
                ((OutputParserListener) parserListeners.get(i)).parseFinished();
            }
            isDone = false;
            monitor.stop();
        }
        parsed = true;
    }

    /**
     * ファイルを読み込み, parseする. このメソッドは必ずオーバーライドし必要にして 充分なOutputDataオブジェクトを作成する.
     * 外部から直接呼ぶことはせず, doParseメソッドを 通じて呼ぶ(別スレッドで実行したり, リスナーにparseが終了したことを伝えたりする)
     * 
     * @return 失敗した時はfalseを返す, という意図.
     */
    protected abstract boolean parse();

    /**
     * OutputDataを, voutputDataに足しこむ.
     * 
     * @param outputData足しこみたいOutputDataオブジェクト
     *            .
     */
    public void addOutputData(OutputData outputData) {
        voutputData.addElement(outputData);
    }

    /**
     * このパーサーの作成元への参照のゲッター
     * 
     * @return 作成元
     */
    public OutputInterface getParent() {
        return this.parent;
    }

    /**
     * このパーサーの作成元への参照のセッター; ロード時に勝手にセットされる.
     * 
     * @param 作成元
     */
    void setParent(OutputInterface parent) {
        this.parent = parent;
        ;
    }

    /**
     * OutputDataを格納しているVectorを初期化する.
     */
    public void initializeOutputDataVector() {
        voutputData = new Vector();
    }

    /**
     * 大抵の場合, OutputDataの作り方は同じ. このメソッドでよいならこれを使う.
     * 
     * @return OutputDataの配列.
     */
    public OutputData[] getOutputData() {
        if (voutputData == null || voutputData.size() == 0) {
            return null;
        }

        OutputData[] ret = new OutputData[voutputData.size()];
        voutputData.copyInto(ret);
        return ret;
    }

    /**
     * getOutputDataで具合が悪い場合, このメソッドをオーバーライドせよ(デフォルトではnullを返すのみ).
     * 
     * @return OutputDataの配列.
     */
    public OutputData[] getOutputDataCustom() {
        return null;
    }

    /**
     * どうしようもない時用. デフォルトでは単なる空白を返す.
     * 
     * @param key
     *            なんらかの識別用文字列
     * @return なんか結果.
     */
    public String[] getGenericData(String key) {
        return new String[] { "" };
    }

    /**
     * どうしようもない時用. デフォルトでは単なる空白を返す.
     * 
     * @param key
     *            なんらかの識別用文字列
     * @return なんか結果.
     */
    public String[][] getGenericData2D(String key) {
        return new String[][] { { "" }, { "" } };
    }

    /** 'parse'の際新しいスレッドを作成したくない場合falseにする */
    protected boolean invokeNewThread = true;

    public int getLength() {
        return numLines;
    }

    public int getCurrent() {
        if (reader == null)
            return 0;
        return reader.getLineNumber();
    }

    public boolean isDone() {
        return isDone;
    }

    public String getNameOfProgress() {
        return "reading " + new File(fileName).getName();
    }

    public void done() {
    }

    class Parse implements Runnable {
        public void run() {
            boolean ret = parse();
            for (int i = 0; i < parserListeners.size(); i++) {
                ((OutputParserListener) parserListeners.get(i)).parseFinished();
            }
            ChaseProgressMonitor.getMonitor().stop();
            isDone = false;
        }
    }
}
