/*

Copyright (C) 2009 NTT DATA INTELLILINK CORPORATION

This program is free software; you can redistribute it and/or
Modify it under the terms of the GNU General Public License 
as published by the Free Software Foundation, version 2.

This program is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied 
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
PURPOSE.  See the GNU General Public License for more details.

 */

package jp.co.intellilink.hinemos.export.history;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.rmi.RemoteException;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import jp.co.intellilink.hinemos.export.conf.performance.CollectorItemCodeFactory;
import jp.co.intellilink.hinemos.util.EjbConnectionManager;
import jp.co.intellilink.hinemos.util.Messages;

import com.clustercontrol.performance.bean.CollectedDataInfo;
import com.clustercontrol.performance.bean.CollectedDataSet;
import com.clustercontrol.performance.bean.CollectorItemInfo;
import com.clustercontrol.performance.bean.CollectorProperty;
import com.clustercontrol.performance.ejb.bmp.RecordCollectorData;
import com.clustercontrol.performance.ejb.session.CollectorController;

/**
 * 性能管理機能の実績収集結果をCSVファイルに出力するクラス<br>
 * 
 * @version 1.0.0
 * @since 1.0.0
 */
public class ExportRecordData {

	private CollectorProperty property;

	private String facilityID;

	private File file;

	private FileOutputStream fos;

	private OutputStreamWriter osw;

	private BufferedWriter bw;

	private Date lastDate = null;

	private boolean header = false;

	private int getSize = 144; // マネージャから一度に取得するデータ数（時間軸方向）

	private long collectStartTime;

	private long collectStopTime;

	private HashMap<String, Date> checkResultMap;

	/**
	 * コンストラクタ
	 */
	public ExportRecordData(CollectorProperty property,
			String facilityID, 
			Date startTime, 
			Date endTime) {
		this.property = property;
		this.facilityID = facilityID;
		this.checkResultMap = new HashMap<String, Date>();
		this.collectStartTime = startTime.getTime();
		this.collectStopTime = endTime.getTime();
	}

	/**
	 * 上書きモードでファイルを開きます。
	 * 
	 * @throws FileNotFoundException
	 * @throws UnsupportedEncodingException
	 */
	private void openFile() throws FileNotFoundException {
		try {
			fos = new FileOutputStream(this.file); // 上書きモードで開く
			osw = new OutputStreamWriter(fos);
			bw = new BufferedWriter(osw);
		} catch (FileNotFoundException e) {
			throw e;
		} finally {
		}
	}

	/**
	 * ファイルを閉じます。
	 * 
	 * @throws IOException
	 */
	private void closeFile() throws IOException {
		try {
			bw.close();
			osw.close();
			fos.close();
		} catch (IOException e) {
			throw e;
		} finally {
		}
	}

	/**
	 * 指定の収集データを取得します。
	 * 
	 * @throws IOException 入出力例外
	 */
	public void write(String filename) {
		try {
			this.file = new File(filename);
			header = false;
			// ファイルを開く
			openFile();

			// 収集のプロパティを取得
			RecordCollectorData collectorData = property.getCollectorData();

			int interval = collectorData.getIntervalSec();
			int timeBlock = interval * 1000 * getSize; // 1度に取得する時間の間隔(ミリ秒単位)

			// 重複部分
			long overlap = interval * 3 * 1000; // ミリ秒
			overlap = 0; //重複なし

			long stratTime = collectStartTime;
			long endTime = 0;

			// ループを回す回数を計算(進捗計算のため)
			int loopMax = ((int) (collectStopTime - collectStartTime)
					/ timeBlock + 1);
			// ループの回数を保持する(進捗計算のため)
			int loopCount = 0;

			while (stratTime < collectStopTime) {
				endTime = stratTime + timeBlock; // 今回マネージャから取得するデータの最大の時刻

				// 指定期間の収集回数より1つ少ない要素しか取得できないため少し重複して取得
				Date startDate = new Date(stratTime - overlap); // 今回マネージャから取得するデータの最小日時
				Date endDate = new Date(endTime); // 今回マネージャから取得するデータの最大日時

				CollectorController collector = EjbConnectionManager.getConnectionManager().getCollectorController();
				// マネージャとの接続に失敗した場合はエラー
				if (collector == null) {
					throw new RemoteException("Can't connect to manager.");
				}

				// 取得した性能データを一時的に保存するためのバッファ
				CollectedDataSet dataSetBuffer = new CollectedDataSet();

				// 項目を分割して数回に分けてマネージャから性能値を取得する
				List<CollectorItemInfo> itemInfos = property.getItemList();
				for (int i = 0; i < property.getItemList().size(); i++) {
					String[] facilityIds = {facilityID};

					CollectorItemInfo[] collectorItemInfos = new CollectorItemInfo[1];
					collectorItemInfos[0] = itemInfos.get(i);

					CollectedDataSet dataSet = collector
					.getRecordCollectedData(
							facilityIds,
							collectorItemInfos,
							startDate,
							endDate);

					// 性能データの取得に失敗した場合はエラー
					if (dataSet == null) {
						throw new RemoteException(
						"Get performance data failed.");
					}

					dataSetBuffer.addCollectedDataList(dataSet);
				}

				write(dataSetBuffer);

				stratTime = endTime;
				loopCount++;
			}
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// ファイルを閉じる
			try {
				closeFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 性能値をファイルに出力します。
	 * 
	 * @throws IOException
	 */
	private void write(CollectedDataSet dataSet) throws IOException {
		try {
			List<CollectorItemInfo> itemInfoList = property.getItemList();
			List<CollectorItemInfo> targetItemInfoList = property.getItemList();

			// 初めての書き込みの場合は項目を出力
			if (!header) {
				// 収集項目名を出力
				StringBuilder msg = new StringBuilder();
				msg.append(Messages.getMsg("EXPORT_COLUMN_TIME"));
				for (int j = 0; j < targetItemInfoList.size(); j++) {
					// 収集項目リストから収集コードに一致するものを探しその収集項目名を取得
					for (int i = 0; i < itemInfoList.size(); i++) {
						CollectorItemInfo itemInfo = itemInfoList.get(i);
						if (itemInfo.getItemCode().equals(targetItemInfoList.get(j).getItemCode())
								&& itemInfo.getDeviceName().equals(targetItemInfoList.get(j).getDeviceName())) {
							String itemName = 
								CollectorItemCodeFactory.getFullItemName(itemInfo.getItemCode(), itemInfo.getDeviceName());
							String measure = 
								CollectorItemCodeFactory.getMeasure(itemInfo.getItemCode());

							msg.append("\t");
							msg.append(itemName);
							msg.append(" (");
							msg.append(measure);
							msg.append(")");
							i = itemInfoList.size(); // ループを抜ける
						}
					}
				}
				bw.write(msg.toString());
				bw.newLine(); // 改行
				header = true;
			}

			List<CollectedDataInfo>[] collectedDataListArray = 
				new List[dataSet.getDataListNum(facilityID)];

			if (collectedDataListArray.length != targetItemInfoList.size()) {
				// エラー処理
				bw.write(Messages.getMsg("INVALID_DATA_IN_DB"));
				bw.newLine(); // 改行

				return ;
			}

			for (int i = 0; i < targetItemInfoList.size(); i++) {
				// 収集項目IDの順番に収集済みデータリストを配列に置き換える
				collectedDataListArray[i] = dataSet.getCollectedDataList(
						facilityID, targetItemInfoList.get(i));
			}

			// 全ての配列で時間軸方向の要素の数は同じであるため，
			// 適当なものから収集データリストの時間軸方向のサイズを取得
			int timeSize = collectedDataListArray[0].size();

			// 全ての配列で時間軸方向の要素の数は同じであるかチェックする。
			for (int i = 1; i < targetItemInfoList.size(); i++) {
				if(timeSize != collectedDataListArray[i].size()){
					// エラー処理
					bw.write(Messages.getMsg("INVALID_DATA_IN_DB"));
					bw.newLine(); // 改行

					return ;
				}
			}

			CollectedDataInfo data = null;
			for (int t = 0; t < timeSize; t++) {
				// 最初の項目を取得
				CollectedDataInfo firstData = (CollectedDataInfo) collectedDataListArray[0].get(t);

				// 重複する部分は出力しない
				if (lastDate == null
						|| firstData.getDate().getTime() > lastDate.getTime()) {
					String msg = null;
					for (int i = 0; i < targetItemInfoList.size(); i++) {
						data = (CollectedDataInfo) collectedDataListArray[i].get(t);

						if (i == 0) { // 最初の項目だけ時刻も出力
							// 出力文字列を設定
							msg = DateFormat.getDateTimeInstance().format(data.getDate())
							+ "\t"
							+ Double.toString(data
									.getValue());
						} else {
							// 出力文字列を設定
							msg = msg
							+ "\t"
							+ Double.toString(data
									.getValue());
						}
					}
					bw.write(msg);
					bw.newLine(); // 改行
				}
			}

			//取得した収集値をチェック
			for (int i = 0; i < targetItemInfoList.size(); i++) {
				CollectorItemInfo itemInfo = targetItemInfoList.get(i);
				String itemName = CollectorItemCodeFactory.getFullItemName(
						itemInfo.getItemCode(), itemInfo.getDeviceName());

				CollectedDataInfo dataInfo = null;
				for (int t = 0; t < timeSize; t++) {
					// 最初の項目を取得
					CollectedDataInfo firstData = 
						(CollectedDataInfo) collectedDataListArray[0].get(t);

					// 重複する部分は出力しない
					if (lastDate == null
							|| firstData.getDate().getTime() > lastDate.getTime()) {

						dataInfo = (CollectedDataInfo) collectedDataListArray[i].get(t);
						if(!Double.isNaN(dataInfo.getValue()) && 
								(checkResultMap.get(itemName) == null || 
										checkResultMap.get(itemName).before(dataInfo.getDate()))){
							checkResultMap.put(itemName, dataInfo.getDate());
						}
					}
				}
			}

			// 出力された最後の日時を記録
			if (data != null) {
				lastDate = data.getDate();
			}
		} catch (IOException e) {
			throw e;
		} finally {
		}
	}
}