/* Copyright (C) 2009  CSE,IIT Bombay  http://www.cse.iitb.ac.in

This file is part of the ConStore open source storage facility for concept-nets.

ConStore is free software and distributed under the 
Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License;
you can copy, distribute and transmit the work
with the work attribution in the manner specified by the author or licensor.
You may not use this work for commercial purposes and may not alter, 
transform, or build upon this work.

Please refer the legal code of the license, available at
http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode

ConStore is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  */

package iitb.con.indexing.isam;

import iitb.con.ds.ItemSerializer;

import java.nio.ByteBuffer;

/**
 * Non-Leaf Node of the index tree. 
 * A non-leaf node consists of the key with respective pointers (block id) of leaf nodes.
 * 
 * @author Prathab K
 *
 */
public class NonLeafNode implements ItemSerializer<NonLeafNode> {
    
    /** Block id of leaf node */
    public short BlockId = -1;
    
    /** Key for the non-leaf node */
    public Object value;
    
    /** Prefix size for the string data type */
    private static final byte PREFIX_SIZE = 8;
    
    /** Terminating character for the prefix computation */
    private static final char TERMINATING_CHAR = '$';
    
    public NonLeafNode(){ }
    
    public NonLeafNode(Object value){
        this.value = value;
    }
    
    public NonLeafNode(Object value, short BlockId){
        this.BlockId = BlockId;
        this.value = value;
    }
    
    /**
     * Serializes into bytes of the given non-leaf node.<p>
     * Format {[Key] : [Block Id]}
     * @param node non-leaf node - {@link NonLeafNode}
     * @return serialized node as ByteBuffer
     */
    public ByteBuffer serialize(NonLeafNode node) {
        //structSize = BlockId (2) + value (x) = 2 + x bytes 
        ByteBuffer buf = null;
        if(value != null) {
            if(value instanceof String) {
                value = getStringPrefix(((String)value));
                buf = ByteBuffer.allocate(2 + PREFIX_SIZE);
                buf.put(((String)value).getBytes());
            }else if(value instanceof Integer) {
                buf = ByteBuffer.allocate(6);
                buf.putInt((Integer)value);
            }else if(value instanceof Float) {
                buf = ByteBuffer.allocate(6);
                buf.putFloat((Float)value);
            }else if(value instanceof Double) {
                buf = ByteBuffer.allocate(10);
                buf.putDouble((Double)value);
            }else if(value instanceof Boolean) {
                buf = ByteBuffer.allocate(3);
                if(((Boolean)value).booleanValue()) 
                    buf.put((byte)1);
                else
                    buf.put((byte)0);
            }
            buf.putShort(node.BlockId);
        }
        buf.rewind();
        return buf;
    }
    
    /**
     * Returns the prefix of the given string
     * @param s string
     * @return string prefix of PREFIX_SIZE
     */
    private String getStringPrefix(String s){
        //returns prefix of length of size PREFIX_SIZE (4)
        
        if(s.length() >= PREFIX_SIZE)
            return s.substring(0, PREFIX_SIZE);
        else {
            char[] chars = new char[PREFIX_SIZE];
            for(int i = 0; i < s.length(); i++)
                chars[i] = s.charAt(i);
            for(int i = s.length(); i < PREFIX_SIZE; i++)
                chars[i] = TERMINATING_CHAR; 
            return new String(chars);
        }
    }
    
    /**
     * Trims the given bytes according the string of PREFIX_SIZE
     * @param b bytes of string value
     * @return string prefix
     */
    private String trimStringPrefix(byte[] b){
        char[] chars = new char[PREFIX_SIZE];
        for(int i = 0; i < b.length; i++) {
            chars[i] = (char)b[i];
            if(chars[i] == TERMINATING_CHAR) {
                char [] ch = new char[i];
                for(int j = 0; j < i ; j++)
                    ch[j] = chars[j];
                return new String(ch);
            }
        }
        return new String(chars);
    }
    
    /**
     * Deserializes the given bytes into non-leaf node.
     * Format {[Key] : [Block Id]}
     * @param buf bytes as ByteBuffer
     * @return non-leaf node - {@link NonLeafNode}
     */
    public NonLeafNode deSerialize(ByteBuffer buf) {
        //buf.position(0);
        if(value != null) {
            if(value instanceof String) {
                byte[] tempBuf = new byte[PREFIX_SIZE];
                buf.get(tempBuf);
                this.value = trimStringPrefix(tempBuf);
            }else if(value instanceof Integer) {
                this.value = new Integer(buf.getInt());
            }else if(value instanceof Float) {
                this.value = new Float(buf.getFloat());
            }else if(value instanceof Double) {
                this.value = new Double(buf.getDouble());
            }else if(value instanceof Boolean) {
                if (buf.get() == 0)
                    this.value = new Boolean(false);
                else
                    this.value = new Boolean(true);
            }
            this.BlockId = buf.getShort();
        }
        return this;
    }
    
    /**
     * Deserializes the specified attribute to object value
     * @param buf bytes as {@link ByteBuffer}
     * @param name attribute name
     * @return deserialized attribute value as <tt>Object</tt>
     */
    public Object attributeDeSerialize(ByteBuffer buf, Object name) {
        return null;
    }
    
}