/**
 * LOGICAL-PARADOX.ORG b-tree library
 * Copyright (C)2004 satoshi akabane(akabane@logical-paradox.org)
 *
 * Release 1.0
 * ・初期パージョン
 */
package org.logical_paradox.common.btree.simulation.bts;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;

import org.logical_paradox.common.btree.Btree;
import org.logical_paradox.common.btree.BtreeException;
import org.logical_paradox.common.btree.BtreeKey;
import org.logical_paradox.common.btree.BtreePage;
import org.logical_paradox.common.btree.BtreeSession;

/**
 * B-treeの図面情報を管理するクラス
 * @author satoshi akabane@logical-paradox.org
 * @version 1.0
 */
public class Diagram {
	private final static int POINTER_WIDTH = 4;
	private final BtreeSimulatorMainPanel mainPanel;
	private BtreeSession session;

	/**
	 * コンストラクタ
	 * @param panel メインパネル
	 */
	public Diagram(BtreeSimulatorMainPanel panel) throws Exception {
		mainPanel= panel;

		// 新しいB-tree構造を生成し，セッションを生成する
		session = Btree.getInstance(new DrawableBtreeFactory()).getSession();
		session.open("");
	}
	/**
	 * 新しいキーをツリーに挿入する
	 * @param key キー
	 * @throws BtreeException 操作に失敗した
	 */
	public void insert(String key) throws Exception {
		session.insert(new DrawableBtreeKey(key));
		repaint();
	}
	/**
	 * 指定されたキーを，新しいキーで更新する
	 * @param src 元のキー 
	 * @param dest 更新後のキー
	 * @throws BtreeException 操作に失敗した
	 */
	public void update(String src, String dest) throws Exception {
		repaint();
	}
	/**
	 * 指定されたキーをツリーから除去する
	 * @param key
	 * @throws BtreeException
	 */
	public void delete(String key) throws Exception {
		repaint();
	}
	
	/**
	 * このアプリケーションと関連付けされているビューに対して，ツリー図の再描画を指示する
	 */
	public void repaint() {
		paint(mainPanel.getGraphics());
	}
	/**
	 * この図面を描画する．
	 * メインパネル再描画のタイミングで，(1)キャンバス (2)このメソッド の順で呼び出される
	 * @param g 描画対象のグラフィックリージョン
	 * @see BtreeSimulatorMainPanel
	 */
	public void paint(Graphics g) {
		g.setColor(Color.BLACK);
		// ルートのページから描画を開始する
		try {
			drawPage(g, session.getPage(), 0, 20);
		} catch (BtreeException e) {
			// 何かエラーがでたのでログ出力
			System.out.println("ページ描画中に何らかの例外がでた");
			e.printStackTrace();
		}
	}
	/**
	 * 指定されたページをキャンバスに描画する
	 * このメソッドはツリーを効果しながら全体を描画する
	 * @param g 描画先のビュー
	 * @param level 現在のノードの深さ
	 * @param btp 描画するページ
	 */
	protected int drawPage(Graphics g, BtreePage btp, int level, int xoffset) {
		if(btp == null) {
			return 0;
		}
		// 全ての子ノードについてループする
		BtreeKey[] keys = btp.getKeys();
		BtreePage childNode = null;

		int startx = xoffset;
		int childrenWidth = 0;
		for(int i = 0; i < btp.keyCount() +1; i++) {
			try {
				long childpageno = btp.getChildNodeAt(i);
				if(childpageno > 0) {
					// 子ノードのページ番号を取得して描画
					childNode = session.getPage(childpageno);
					int pagewidth = drawPage(g, childNode, level+1, startx + childrenWidth);	// ページ間は20px空ける
					childrenWidth += pagewidth + 10;
				}
			} catch(Exception be) {
				be.printStackTrace();
			}
		}
		int pageoffset = (childrenWidth - calcPageWidth(btp)) /2;
		if(childrenWidth == 0) {
			pageoffset = 0;
		}
System.out.println("childr=" + childrenWidth);
System.out.println("pagewd=" + calcPageWidth(btp));
System.out.println("startx=" + startx);
System.out.println("poffst=" + pageoffset);

		return drawKeys(g, btp, level, startx + pageoffset);
	}
	/**
	 * ページの描画サイズを計算して返す
	 * @param btp 描画対象のページ
	 * @return ページのサイズ(px)
	 */
	protected int calcPageWidth(BtreePage btp) {
		BtreeKey[] keys = btp.getKeys();

		// 描画に必要なサイズの計算
		int width = 0;
		int height = 0;

		for(int i = 0; i < btp.keyCount(); i++) {
			DrawableBtreeKey drawable = (DrawableBtreeKey)keys[i];
			Dimension d = drawable.getSize(); 
			width += d.width;
			if(height < d.height) {
				height = d.height;
			}

			// 子ノードのポインタのサイズを追加する
			// POINTER_WIDTHpxとする
			width += POINTER_WIDTH;
		}
		if(keys.length > 0) {
			width += POINTER_WIDTH;		// 最大キーの右のノード表示分
		}

		return width;
	}
	/**
	 * ページの内容を全て描画する
	 * @param g 描画先のビュー
	 * @param level 現在のノードの深さ
	 * @param btp 描画するページ
	 */
	protected int drawKeys(Graphics g, BtreePage btp, int level, int xoffset) {
		BtreeKey[] keys = btp.getKeys();
System.out.println("xoffset=" + xoffset);
		// 描画に必要なサイズの計算
		int width = calcPageWidth(btp);
		int height = 0;

		if(keys[0] != null) {
			height = ((DrawableBtreeKey)keys[0]).getSize().height;
		}

		// 描画を開始する中心位置を計算する
		int startx = xoffset; //(mainPanel.getCanvasSize().width - width) /2;
		int starty = 48 + level * (height + 16);

		// 全てのキーとポインタを描画する
		for(int cnt = 0; cnt < btp.keyCount(); cnt++) {
			// ポインタの描画
			g.drawRect(startx, starty, POINTER_WIDTH, height);
			startx += POINTER_WIDTH;

			// キーの描画
			DrawableBtreeKey dbk = (DrawableBtreeKey)keys[cnt];
			dbk.draw(g, new Point(startx, starty));
			startx += dbk.getSize().width;
		}

		// 一番右のポインタを描画
		g.drawRect(startx, starty, POINTER_WIDTH, height);
		startx += POINTER_WIDTH;

		return width;
	}
}
