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

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.logical_paradox.common.util.StringUtils;

/**
 * ダンプリストを作成する
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.2 $
 */
public class DumpListEditor {
	/** フォーマット = 16進 */
	public static final int DUMP_HEX = 1;
	/** 出力単位 = 1バイト */
	public static final int UNIT_BYTE = 1;
	/** 出力単位 = 2バイト */
	public static final int UNIT_WORD = 2;
	/** 出力単位 = 4バイト */
	public static final int UNIT_LONG = 4;
	/** 出力単位 = 8バイト */
	public static final int UNIT_LONG_LONG = 8;

	/** バイトオーダー = リトルエンディアン */
	public static final int ORDER_LITTLE_ENDIAN = -1;
	/** バイトオーダー = ビッグエンディアン */
	public static final int ORDER_BIG_ENDIAN = 1;

	/** 選択されたフォーマット */
	private final int format;
	/** 選択されたユニット */
	private final int unit;
	/** 選択されたバイトオーダー */
	private final int byteOrder;
	/** 1行に出力するユニット数 */
	private final int uol;

	/**
	 * コンストラクタ
	 * 16進/バイト単位/bigendian/1行=32unitsで表示
	 */
	public DumpListEditor() {
		this(DUMP_HEX, UNIT_BYTE, ORDER_BIG_ENDIAN, 32);
	}
	/**
	 * コンストラクタ
	 * @param fmt フォーマット
	 * @param ut ユニット
	 * @param bo バイトオーダー
	 * @param u 1行に出力されるユニット数
	 */
	public DumpListEditor(int fmt, int ut, int bo, int u) {
		format = fmt;
		unit = ut;
		byteOrder = bo;
		uol = u;
	}
	/**
	 * フォーマットを返す
	 * @return フォーマット
	 */
	public int getFormat() {
		return format;
	}
	/**
	 * ユニットの単位を返す
	 * @return ユニット
	 */
	public int getUnit() {
		return unit;
	}
	/**
	 * バイトオーダーを返す
	 * @return バイトオーダー
	 */
	public int getByteOrder() {
		return byteOrder;
	}
	/**
	 * 1行に出力するユニット数を返す
	 * @return ユニット数
	 */
	public int getUnitsOfLine() {
		return uol;
	}
	/**
	 * ダンプする
	 * @param bytes ダンプ対象バイト
	 * @return 文字列表現
	 */
	public String edit(byte[] bytes) {
		int paddings = bytes.length % unit;
		int bufferlen = bytes.length + paddings;
		ByteOrder order = getByteOrder() == ORDER_BIG_ENDIAN ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
		ByteBuffer buffer = ByteBuffer.allocate(bufferlen).order(order);
		buffer.put(bytes);
		// 足りない分は補う
		if(paddings > 0) {
			buffer.put(new byte[paddings]);
		}
		buffer.rewind();

		String dd = null;

		switch(format) {
			case DUMP_HEX:						// 16進
			default:
				dd = dumpHex(buffer, bufferlen);
				break;
		}

		return dd;
	}
	/**
	 * ダンプする
	 * @param buffer ダンプ対象のデータ
	 * @param bufferlen バッファのサイズ(パディングされた状態)
	 * @return ダンプ文字列
	 */
	protected String dumpHex(ByteBuffer buffer, int bufferlen) {
		StringBuffer sb = new StringBuffer();
		// ユニット単位でループする
		int loopcnt = bufferlen / unit;

		for(int i = 0; i < loopcnt; i++) {
			if(i % uol > 0) {
				sb.append(" ");
			}
			switch(unit) {
				case UNIT_BYTE:				// バイト(8bit)
					sb.append(formatColumns(Integer.toHexString(buffer.get()), 2));
					break;
				case UNIT_WORD:				// ワード(16bit)
					sb.append(formatColumns(Integer.toHexString(buffer.getInt()), 4));
					break;
				case UNIT_LONG:				// ロングワード(32bit)
					sb.append(formatColumns(Integer.toHexString(buffer.getInt()), 8));
					break;
				case UNIT_LONG_LONG:			// ロングロングワード(64bit)
					sb.append(formatColumns(Long.toHexString(buffer.getLong()), 16));
					break;
			}
			if((i+1) % uol == 0) {
				// 改行
				sb.append("\n");
			}
		}
		return sb.toString();
	}
	/**
	 * 桁数をそろえる
	 * 足りない分については、'0'が付与される
	 * @param value 出力したい値
	 * @param clmns 桁数
	 * @return
	 */
	protected String formatColumns(String value, int clmns) {
		// 指定された値が整形前に既に十分長い場合、何もしないでそのまま返す
		if(value.length() >= clmns) {
			return value.substring(value.length() -2);
		}
		String str = StringUtils.sequence('0', clmns) + value;
		return str.substring(str.length() - clmns);
	}
}
