/*
 *	PROGRAM:	JRD Access Method
 *	MODULE:		btn.h
 *	DESCRIPTION:	B-tree management code
 *
 *  The contents of this file are subject to the Initial
 *  Developer's Public License Version 1.0 (the "License");
 *  you may not use this file except in compliance with the
 *  License. You may obtain a copy of the License at
 *  http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
 *
 *  Software distributed under the License is distributed AS IS,
 *  WITHOUT WARRANTY OF ANY KIND, either express or implied.
 *  See the License for the specific language governing rights
 *  and limitations under the License.
 *
 *  The Original Code was created by Arno Brinkman
 *  for the Firebird Open Source RDBMS project.
 *
 *  Copyright (c) 2003 Arno Brinkman and all contributors
 *  signed below.
 *
 *  All Rights Reserved.
 *  Contributor(s): ______________________________________.
 */

#ifndef JRD_BTN_H
#define JRD_BTN_H

#include "firebird.h"			// needed for get_long
#include "memory_routines.h"	// needed for get_long

#include "../jrd/ods.h"
#include "../common/classes/array.h"

namespace Jrd {

// Flags (3-bits) used for index node
const int BTN_NORMAL_FLAG					= 0;
const int BTN_END_LEVEL_FLAG				= 1;
const int BTN_END_BUCKET_FLAG				= 2;
const int BTN_ZERO_PREFIX_ZERO_LENGTH_FLAG	= 3;
const int BTN_ZERO_LENGTH_FLAG				= 4;
const int BTN_ONE_LENGTH_FLAG				= 5;
//const int BTN_ZERO_PREFIX_ONE_LENGTH_FLAG	= 6;
//const int BTN_GET_MORE_FLAGS	= 7;

// Firebird B-tree nodes
const USHORT BTN_LEAF_SIZE	= 6;
const USHORT BTN_PAGE_SIZE	= 10;

struct IndexNode
{
	UCHAR* nodePointer;			// pointer to where this node can be read from the page
	USHORT prefix;				// size of compressed prefix
	USHORT length;				// length of data in node
	ULONG pageNumber;			// page number
	UCHAR* data;				// Data can be read from here
	RecordNumber recordNumber;	// record number
	bool isEndBucket;
	bool isEndLevel;

	static USHORT computePrefix(const UCHAR* prevString, USHORT prevLength,
								const UCHAR* string, USHORT length);

	static SLONG findPageInDuplicates(const Ods::btree_page* page, UCHAR* pointer,
									  SLONG previousNumber, RecordNumber findRecordNumber);

	bool keyEqual(USHORT length, const UCHAR* data) const
	{
		if (length != this->length + this->prefix)
			return false;

		if (!this->length)
			return true;

		return !memcmp(this->data, data + this->prefix, this->length);
	}

	void setEndBucket()
	{
		this->isEndBucket = true;
		this->isEndLevel = false;
	}

	void setEndLevel()
	{
		this->isEndBucket = false;
		this->isEndLevel = true;
		this->prefix = 0;
		this->length = 0;
		this->pageNumber = 0;
		this->recordNumber.setValue(0);
	}

	void setNode(USHORT prefix = 0, USHORT length = 0,
				 RecordNumber recordNumber = RecordNumber(0), SLONG pageNumber = 0,
				 bool isEndBucket = false, bool isEndLevel = false)
	{
		this->isEndBucket = isEndBucket;
		this->isEndLevel = isEndLevel;
		this->prefix = prefix;
		this->length = length;
		this->recordNumber = recordNumber;
		this->pageNumber = pageNumber;
	}

	USHORT getNodeSize(bool leafNode) const;
	UCHAR* writeNode(UCHAR* pagePointer, bool leafNode, bool withData = true);

	UCHAR* readNode(UCHAR* pagePointer, bool leafNode)
	{
	/**************************************
	 *
	 *	r e a d N o d e
	 *
	 **************************************
	 *
	 * Functional description
	 *	Read a leaf/page node from the page by the
	 *  given pagePointer and the return the
	 *  remaining position after the read.
	 *
	 **************************************/
		nodePointer = pagePointer;

		// Get first byte that contains internal flags and 6 bits from number
		UCHAR* localPointer = pagePointer;
		UCHAR internalFlags = *localPointer++;
		SINT64 number = (internalFlags & 0x1F);
		internalFlags = ((internalFlags & 0xE0) >> 5);

		isEndLevel = (internalFlags == BTN_END_LEVEL_FLAG);
		isEndBucket = (internalFlags == BTN_END_BUCKET_FLAG);

		// If this is a END_LEVEL marker then we're done
		if (isEndLevel)
		{
			prefix = 0;
			length = 0;
			recordNumber.setValue(0);
			pageNumber = 0;
			return localPointer;
		}

		// Get remaining bits for number
		ULONG tmp = *localPointer++;
		number |= (tmp & 0x7F) << 5;
		if (tmp >= 128)
		{
			tmp = *localPointer++;
			number |= (tmp & 0x7F) << 12;
			if (tmp >= 128)
			{
				tmp = *localPointer++;
				number |= (tmp & 0x7F) << 19;
				if (tmp >= 128)
				{
					tmp = *localPointer++;
					number |= (FB_UINT64) (tmp & 0x7F) << 26;
					if (tmp >= 128)
					{
						tmp = *localPointer++;
						number |= (FB_UINT64) (tmp & 0x7F) << 33;
	/*
		Uncomment this if you need more bits in record number
							if (tmp >= 128)
							{
								tmp = *localPointer++;
								number |= (FB_UINT64) (tmp & 0x7F) << 40;
								if (tmp >= 128)
								{
									tmp = *localPointer++;
									number |= (FB_UINT64) (tmp & 0x7F) << 47;
									if (tmp >= 128)
									{
										tmp = *localPointer++;
										number |= (FB_UINT64) (tmp & 0x7F) << 54; // We get 61 bits at this point!
									}
								}
							}
	*/
					}
				}
			}
		}
		recordNumber.setValue(number);

		if (!leafNode)
		{
			// Get page number for non-leaf pages
			tmp = *localPointer++;
			number = (tmp & 0x7F);
			if (tmp >= 128)
			{
				tmp = *localPointer++;
				number |= (tmp & 0x7F) << 7;
				if (tmp >= 128)
				{
					tmp = *localPointer++;
					number |= (tmp & 0x7F) << 14;
					if (tmp >= 128)
					{
						tmp = *localPointer++;
						number |= (tmp & 0x7F) << 21;
						if (tmp >= 128)
						{
							tmp = *localPointer++;
							number |= (tmp & 0x0F) << 28;
						}
					}
				}
			}
			pageNumber = number;
		}

		if (internalFlags == BTN_ZERO_PREFIX_ZERO_LENGTH_FLAG)
		{
			// Prefix is zero
			prefix = 0;
		}
		else
		{
			// Get prefix
			tmp = *localPointer++;
			prefix = (tmp & 0x7F);
			if (tmp & 0x80)
			{
				tmp = *localPointer++;
				prefix |= (tmp & 0x7F) << 7; // We get 14 bits at this point
			}
		}

		if ((internalFlags == BTN_ZERO_LENGTH_FLAG) ||
			(internalFlags == BTN_ZERO_PREFIX_ZERO_LENGTH_FLAG))
		{
			// Length is zero
			length = 0;
		}
		else if (internalFlags == BTN_ONE_LENGTH_FLAG)
		{
			// Length is one
			length = 1;
		}
		else
		{
			// Get length
			tmp = *localPointer++;
			length = (tmp & 0x7F);
			if (tmp & 0x80)
			{
				tmp = *localPointer++;
				length |= (tmp & 0x7F) << 7; // We get 14 bits at this point
			}
		}

		// Get pointer where data starts
		data = localPointer;
		localPointer += length;

		return localPointer;
	}

};

struct IndexJumpNode
{
	UCHAR* nodePointer;	// pointer to where this node can be read from the page
	USHORT prefix;		// length of prefix against previous jump node
	USHORT length;		// length of data in jump node (together with prefix this is prefix for pointing node)
	USHORT offset;		// offset to node in page
	UCHAR* data;		// Data can be read from here

	USHORT getJumpNodeSize() const;
	UCHAR* readJumpNode(UCHAR* pagePointer);
	UCHAR* writeJumpNode(UCHAR* pagePointer);
};

} // namespace Jrd

#endif // JRD_BTN_H
