/*
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2005 satoshi akabane(akabane@logical-paradox.org)
 * $Id: restore.java,v 1.2 2005/09/07 13:38:06 rampil Exp $
 */
package org.logical_paradox.vsan.tools.console.command;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.logical_paradox.shell.Argument;
import org.logical_paradox.shell.Command;
import org.logical_paradox.shell.Option;
import org.logical_paradox.shell.ResultCode;
import org.logical_paradox.shell.ShellEnv;
import org.logical_paradox.shell.exception.CommandExecutionException;
import org.logical_paradox.shell.exception.TerminationException;
import org.logical_paradox.vsan.dac.Response;
import org.logical_paradox.vsan.dac.ResponseCode;
import org.logical_paradox.vsan.dac.command.Protocol;
import org.logical_paradox.vsan.tools.console.DacConsoleConstant;
import org.logical_paradox.vsan.tools.console.util.DumpListEditor;
import org.logical_paradox.vsan.tools.console.util.SocketWriter;

/**
 * <pre>
 * ■restore
 * 
 * 【動作仕様】
 * DACからデータを受信します。
 * このコマンドは、dacコマンドによって設定された'dac'環境変数を参照します。
 * レコード番号は半角カンマ区切りで複数与えることができます。
 * 
 * 【書式】
 * restore --record=<record no.[,<record no.[..]]> [<hostname>[:<port>]]
 * 
 * </pre>
 * 
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.2 $
 */
public class restore implements Command {
	public static final String USAGE = "restore --record=<record no.[,<record no.[..]]> [<hostname>[:<port>]]";

	public String usage() {
		return USAGE;
	}

	public ResultCode execute(OutputStream o, ShellEnv env, Argument a) throws CommandExecutionException, TerminationException {
		String dac = null;

		if(a == null || a.getOptions() == null || a.getOptions().length == 0) {
			throw new CommandExecutionException();
		}

		if(a == null || a.getArguments().length == 0) {
			dac = env.getProperty(DacConsoleConstant.ENV_DAC);
		} else if(a.getArguments().length > 1) {
			throw new CommandExecutionException();
		} else {
			dac = a.getArguments()[0];
		}

		// オプションの解析
		try {
			Option[] options = a.getOptions();
			long[] records = null;

			for(int i = 0; i < options.length; i++) {
				Option option = options[i];
				if("record".equals(option.getName())) {
					records = parseRecords(option.getValue());
				} else {
					throw new CommandExecutionException();
				}
			}

			if(records == null || records.length == 0) {
				throw new CommandExecutionException();
			}

			/*
			 * 送信データの作成
			 */
			ByteBuffer bb = ByteBuffer.allocate(1 + 4 + (records.length * 8)).order(ByteOrder.BIG_ENDIAN);
			bb.put((byte)Protocol.P_RESTORE);
			bb.putInt(records.length);

			// レコードリスト
			for(int i = 0; i < records.length; i++) {
				bb.putLong(records[i]);
			}

			PrintWriter pw = new PrintWriter(o);
			pw.println("retrieving..\n");
			pw.flush();

			Response response = SocketWriter.send(dac, bb.array(), false);

			if(response.getResponseCode() == ResponseCode.OK) {
				// 実行ステータスがOKの場合
				displayDumpList(pw, response);
				pw.println("done.");
				pw.flush();
				pw.close();

				return new ResultCode(ResultCode.STATUS_OK);
			} else {
				// 実行ステータスコードがOK以外の場合
				pw.println("fatal error is caused in executing RESTORE command: code=[" + response.getResponseCode() + "]\n");
				pw.flush();
				pw.close();

				return new ResultCode(ResultCode.STATUS_ERROR);
			}
		} catch(IOException e) {
			throw new CommandExecutionException();
		}
	}
	/**
	 * DACから受信したレコードのデータをコンソールに出力する
	 * @param pw 出力先
	 * @param response DACからの回答
	 */
	protected void displayDumpList(PrintWriter pw, Response response) {
		ByteBuffer buffer = ByteBuffer.allocate(response.getData().length).order(ByteOrder.BIG_ENDIAN);
		buffer.put(response.getData());
		buffer.rewind();

		// レコード数
		int replyRecords = buffer.getInt();
		DumpListEditor dumpListEditor = new DumpListEditor();

		StringBuffer sb = new StringBuffer();
		for(int i = 0; i < replyRecords; i++) {
			// レコードを読む
			// ブロックサイズ / レコード番号 / 実データサイズ
			int replyBlockSize = buffer.getInt();
			long recordNo = buffer.getLong();
			int datasize = buffer.getInt();
			byte[] dumpTarget = new byte[datasize];

			buffer.get(dumpTarget);

			sb.append("record no: " + recordNo + ", " + replyBlockSize + " bytes/block, ");

			sb.append(" size: " + datasize + " bytes\n");
			// ダンプ出力
			sb.append("-- -- -- -- -- -- -- --\n");
			sb.append(dumpListEditor.edit(dumpTarget));
			sb.append("\n");
			sb.append("-- -- -- -- -- -- -- --\n");
		}

		// コンソールへ出力する
		pw.println(sb.toString());
	}
	/**
	 * カンマ区切りのレコード番号を解析する
	 * @param line カンマ区切りの数文字列
	 * @return レコード番号の配列
	 */
	protected long[] parseRecords(String line) {
		String[] numbers = line.split(",");
		long records[] = new long[numbers.length];

		for(int i = 0; i < records.length; i++) {
			records[i] = Long.parseLong(numbers[i]);
		}

		return records;
	}
}
