/*
 * Author: Barbara Hohlt <hohltb@cs.berkeley.edu>
 * Inception Date: July 19,2000 
 *
 * This software is copyrighted by Barbara Hohlt and the Regents of
 * the University of California.  The following terms apply to all
 * files associated with the software unless explicitly disclaimed in
 * individual files.
 * 
 * The authors hereby grant permission to use this software without
 * fee or royalty for any non-commercial purpose.  The authors also
 * grant permission to redistribute this software, provided this
 * copyright and a copy of this license (for reference) are retained
 * in all distributed copies.
 *
 * For commercial use of this software, contact the authors.
 * 
 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
 * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
 * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
 * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
 * MODIFICATIONS.
 */

package ninja2.core.sn_btree.recordconverters;

import ninja2.core.io_core.interfaces.*;
import ninja2.core.io_core.core.*;
import ninja2.core.io_core.util.*;
import java.io.IOException;
import java.util.*;
import ninja2.core.sn_btree.*;
import ninja2.util.*;

/**
 * The BtreeRoot class converts a byte array to/from a structured
 * class instance containing richly typed fields;  a table is broken up
 * into a number of btree nodes, and each node is described
 * by one of these btree node classes.
 *
 * For simplicity the Btree node will keep almost the same
 * format as TableHashBucket with two extra fields (leaf node indicator 
 * and node/data pointer). The overflow bucket number will be
 * used as the Btree link node pointer.
 *
 * <pre>
 * BtreeRoot disk format 
 * ----------------------
 * bytes 0->3:              LSN (not used)
 * bytes 4->7:              node number 
 * bytes 8->11:             blink node num, or 0 if no blink node 
 * bytes 12->15:			leaf node indicator for btree
 * bytes 16->23:            min key in btree
 * bytes 24->27:            min ptr 
 * bytes 28->35:            max key in btree 
 * bytes 36->39:            max ptr 
 * bytes 40->43:			number of btree nodes	
 * bytes 44->47:			number of data pages	
 * bytes 48->onwards:       [slots] 
 * ....
 * BtreeEntry (BtreeNode slots) 
 * -------------
 *
 * bytes 0->1:              record size (not used for now )
 * byte  2:                 0 if slot not used, 1 if used 
 * byte  3:                 (not used)
 * byte  4->11:             record key
 * byte  12->15:			node/data ptr for btree
 * </pre>
 * 
 * @author Barbara Hohlt 
 */

public class BtreeRoot extends BtreeNode {
  
  private long			_min_key;
  private int			_min_ptr;
  private long			_max_key;
  private int			_max_ptr;
  private int 			_num_nodes;
  private int			_num_blocks;
  private String		_tablename;

  public static final int ROOT_HEADER_SIZE = 48;

  /**
   * This method takes a byte array of size Btree.table_blocksize
   * and returns a structured BtreeNode object from it.  This structured
   * object includes a hidden cache of the byte array, and it makes 
   * modifications to that byte array when the richly typed fields are
   * modified.  Warning:  this means that you can't putz with the
   * byte array that you've passed in!
   *
   * @param arr the byte array from which the object is created, and to
   *            which modifications to the object will be reflected.
   *
   * @return the richly structured object
   * @exception java.io.IOException thrown if the byte[] array is
   *            malformed or not big enough
   */
  public BtreeRoot(MemRegionIF reg)
    throws IOException {

    if (reg.getSize() != SN_Btree.table_blocksize)
      throw new IOException( "bad region size != SN_Btree.table_blocksize in Btree init");

	if (SN_Btree.debug) 
		System.out.println("starting BtreeRoot(MemRegionIF)..."+ROOT_HEADER_SIZE);
    byte_representation = reg;
    _bucket_records = new Vector();
    _bucket_record_offsets = new Vector();
	_buckets_used = 0;

    // begin parsing of bytes - will be particularly hairy
    _LSN = reg.getInt(0);	/* bytes 0-3 */
    
    _main_bucket_num = reg.getInt(4); /* bytes 4-7 */
    _overflow_bucket_num = reg.getInt(8); /* bytes 8-11 */

    _is_leaf_node = (reg.getInt(12) == 1) ? true : false ; /* bytes 12-15 */
	_min_key = reg.getLong(16);			/* bytes 16-23 */
	_min_ptr = reg.getInt(24);			/* bytes 24-27 */
	_max_key = reg.getLong(28);			/* bytes 28-35 */
	_max_ptr = reg.getInt(36);			/* bytes 36-39 */
	_num_nodes = reg.getInt(40);		/* bytes 40-43 */
	_num_blocks = reg.getInt(44);		/* bytes 44-47 */

    // parse out records
    boolean done = false;
    int     cur_offset = ROOT_HEADER_SIZE;
	int count = 0;
    short   recsize;
    byte    is_usedB;
    boolean is_used;
    long    key;
	int 	  ptr;
    BtreeEntry newthbr;
    while(!done) {
	newthbr = null;

      recsize = reg.getShort(cur_offset); /* bytes 0-1 */
      if (recsize  > 0) {
		throw new IOException("bogus recsize format in buck record");
      }
      is_usedB = reg.getByte(cur_offset+2); /* byte 2 */
      is_used = (is_usedB == 1 ? true : false);
      key = reg.getLong(cur_offset+4); /* bytes 4-11, 3 unused */
      ptr = reg.getInt(cur_offset+12); /* bytes 12-15 */ 

	  if (is_used == true) {
		newthbr = new BtreeEntry(is_used, key, ptr);
		_buckets_used++;
	  } else {
		newthbr = new BtreeEntry(is_used);
	  }

      _bucket_records.addElement(newthbr);
      _bucket_record_offsets.addElement(new Integer(cur_offset));
      cur_offset += (THBR_HEADER_SIZE);
	  count++;
	  // MARK: Remember Btree root only uses 2 slots, the rest
	  // are dummies 
	  if (cur_offset >= SN_Btree.table_blocksize - THBR_HEADER_SIZE)
		done = true;

    }
	if (SN_Btree.debug) System.out.println("	num slots: "+ count +":"+ _buckets_used);
  }

  /**
   * Here's how to create a new btree root node object, and initialize it
   * based on some known parameters.
   *
   * i.e. this generates the internal disk format byte representation! 
   * 
   * For first Btree Node the first three args are 0's
   * 
   * @return the richly structured object
   */
  public BtreeRoot(int LSN, int main_buck, int overflow_bucket,
			 boolean is_leaf_node,
			 BtreeEntry[] record_arr,
			 MemRegionIF reg) {
    byte_representation = reg;
    _bucket_records = new Vector();
    _bucket_record_offsets = new Vector();

	if (SN_Btree.debug) 
		System.out.println("starting BtreeRoot(......)..."
			+ROOT_HEADER_SIZE+" "+SN_Btree.max_root_slots);
    // set internal MemRegionIF state
	//MARK: The header info is put into in byte_representation as well.
    this.set_LSN(LSN);
    this.set_main_bucket_num(main_buck);
    this.set_overflow_bucket_num(overflow_bucket); /* link ptr */
    this.set_is_leaf_node(is_leaf_node);

    // setting the bucketrecord state is tricky, since doing it based on
    // a large input list is outside the purvue of our interfaces, so we
    // do it by hand.
    int len = record_arr.length;
	if (SN_Btree.debug) System.out.println("	BtreeEntry[].length: "+len);
    int offset = ROOT_HEADER_SIZE; /* first entry starts after header */
    byte[] recByte;
    for (int i=0; i<len; i++) {
      recByte = null;

      _bucket_records.addElement(record_arr[i]);
      _bucket_record_offsets.addElement(new Integer(offset));
      recByte = record_arr[i].get_byte_representation();
      byte_representation.copyByteArrayInto(recByte, 0,
					    offset, recByte.length);
      offset += recByte.length;
    }
	if (SN_Btree.debug) 
		System.out.println("	Vector.length: "+_bucket_records.size());

	//MARK: Btrees have a maximum number of entries 
	if ( offset < SN_Btree.table_blocksize - ROOT_HEADER_SIZE )
    {
      Debug.msg("BtreeRoot singlenode", Debug.FATAL,
		"warning: consistency bogosity in btree init");
      	System.exit(1); 
    }
  }

  public int get_numnodes() {
    return _num_nodes;
  }
  public int get_numblocks() {
    return _num_blocks;
  }
  public long get_min_key() {
    return _min_key;
  }
  public int get_min_ptr() {
    return _min_ptr;
  }
  public long get_max_key() {
    return _max_key;
  }
  public int get_max_ptr() {
    return _max_ptr;
  } 

  public String get_tablename() {
	return _tablename;
  }

 public void set_metadata(int numnodes, int numblocks,
			long minkey, int minptr, long maxkey, int maxptr) {

	set_numnodes(numnodes);
	set_numblocks(numblocks);
    set_min_key(minkey);
    set_min_ptr(minptr);
    set_max_key(maxkey);
    set_max_ptr(maxptr);
  }

 public void set_min(long key, int ptr) {
    set_min_key(key);
    set_min_ptr(ptr);
  }
  public void set_min_key(long key) {
    _min_key = key;

    // update bytes
    byte_representation.putLong(key, 16);
  }
  public void set_min_ptr(int ptr) {
    _min_ptr = ptr;

    // update bytes
    byte_representation.putInt(ptr, 24);
  }
  public void set_max(long key, int ptr) {
    set_max_key(key);
    set_max_ptr(ptr);
  }
  public void set_max_key(long key) {
    _max_key = key;

    // update bytes
    byte_representation.putLong(key, 28);
  }
  public void set_max_ptr(int ptr) {
    _max_ptr = ptr;

    // update bytes
    byte_representation.putInt(ptr, 36);
  }  
  public void set_numnodes(int numnodes) {
    _num_nodes = numnodes;

    // update bytes
    byte_representation.putInt(numnodes, 40);
  }  
  public void set_numblocks(int numblocks) {
    _num_blocks = numblocks;

    // update bytes
    byte_representation.putInt(numblocks, 44);
  }  

  public String toString() {
	return new String(super.toString() + ", min:max "+_min_key+":"+_max_key);
  }                                              
     

}

