/*
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2005 satoshi akabane(akabane@logical-paradox.org)
 * $Id: DataRestoreCommand.java,v 1.3 2005/09/17 17:35:49 rampil Exp $
 */
package org.logical_paradox.vsan.dac.command.handler;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.logical_paradox.common.io.ExpandableBuffer;
import org.logical_paradox.common.session.IllegalSessionStateException;
import org.logical_paradox.common.session.Session;
import org.logical_paradox.common.util.ByteStreamConverter;
import org.logical_paradox.vsan.dac.DacConstant;
import org.logical_paradox.vsan.dac.NodeContext;
import org.logical_paradox.vsan.dac.Response;
import org.logical_paradox.vsan.dac.ResponseCode;
import org.logical_paradox.vsan.dac.command.exception.CommandExecutionException;
import org.logical_paradox.vsan.dac.command.exception.SessionTerminationException;
import org.logical_paradox.vsan.node.Database;
import org.logical_paradox.vsan.node.DatabaseAccessException;
import org.logical_paradox.vsan.node.dbr.DbRecord;

/**
 * データリストアコマンド
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.3 $
 */
public class DataRestoreCommand implements DacCommand {
	/** ロガー */
	private static final Log log = LogFactory.getLog(DataRestoreCommand.class);

	/**
	 * コマンドを実行する
	 * @param session 実行するセッション
	 * @return 更新されたセッション
	 * @throws CommandExecutionException コマンド実行中に発生した例外
	 * @throws SessionTerminationException 接続終了時に発生する例外
	 */
	public Session execute(Session session) throws CommandExecutionException, SessionTerminationException, DatabaseAccessException {
		try {
			NodeContext ctx = (NodeContext)session.getAttribute(DacConstant.SKEY_NODE_CONTEXT);
			Database db = ctx.getDatabase();

			// 引数の受信
			byte[] bb = new byte[8];

			InputStream in = (InputStream)session.getAttribute(DacConstant.SKEY_SOCKET_INPUT_STREAM);

			// 要求レコード数
			bb[0] = (byte)in.read();
			bb[1] = (byte)in.read();
			bb[2] = (byte)in.read();
			bb[3] = (byte)in.read();

			int retrieveRecords = ByteStreamConverter.getInt(bb);

			if(retrieveRecords <= 0) {
				log.warn("要求されたレコード数が0以下です");
				throw new CommandExecutionException();
			}

			// 問い合わせるレコード番号を受信する
			long[] recordno = new long[retrieveRecords];
			for(int i = 0; i < recordno.length; i++) {
				bb[0] = (byte)in.read();
				bb[1] = (byte)in.read();
				bb[2] = (byte)in.read();
				bb[3] = (byte)in.read();
				bb[4] = (byte)in.read();
				bb[5] = (byte)in.read();
				bb[6] = (byte)in.read();
				bb[7] = (byte)in.read();

				recordno[i] = ByteStreamConverter.getLong(bb);
			}

			// ドメインデータベースに対して問い合わせる
			DbRecord[] records = db.restoreStream(recordno);
			byte[] datastream = createDataStreamFromRecords(records);

			// 戻り値の作成
			session.setAttribute(DacConstant.SKEY_DAC_RESPONSE, new Response(ResponseCode.OK, datastream));
			return session;
		} catch(IllegalSessionStateException e) {
			// セッションが継続可能な状態でない場合，当該セッションを終了する
			log.warn("データリストアが要求されましたが、セッションが持続可能な状態ではありませんでした。");
			log.warn("セッションを破棄します。");
			throw new SessionTerminationException(e);
		} catch(IOException e) {
			log.warn("コマンド受信中にI/O例外が発生しました");
			throw new CommandExecutionException(e);
		}
	}
	/**
	 * データベースレコードをバイト列に変換する
	 * @param records データベースレコード
	 * @return 変換されたバイト列
	 */
	protected byte[] createDataStreamFromRecords(DbRecord[] records) throws IOException {
		ExpandableBuffer exb = new ExpandableBuffer();
		// レコード数
		exb.write(ByteStreamConverter.convert(records.length));
		// それぞれのレコードをバイト列に変換して登録する
		for(int i = 0; i < records.length; i++) {
			exb.write(records[i].toByteStream());
		}

		return exb.byteStream();
	}
}
