
class isttreeexcept : public except
{
public:
	isttreeexcept(const char *szdesc=0):except("B+ tree exception",szdesc){};
	~isttreeexcept(){};
};

class isttree
{
public:
	struct INFO
	{
		int					blocksize;
		int					keysize;
		int					datasize;
		long				root;
		KEY_BRANCH_PAIR		leftmostbtree;
		KEY_BRANCH_PAIR		rightmostbtree;
		int					keys;
		int					depth;
		LIMITS				root_limits;
		INFO():root(sizeof(INFO)),keys(0),depth(0),blocksize(BLOCKSIZE),
			keysize(sizeof(REP_TYPE)),datasize(sizeof(IST_DATA)){};
		~INFO(){};
	};


private:
	long FindBPTree(const REP_TYPE &);

public:
	void ConstructTree(KEY_BRANCH_PAIR *pkey_branch);
//	void BulkLoadIntegers(unsigned int,stabptree<int,int> *);

private:
	INFO _info;
	fileobject		_indexf;
	datafile<int>	_dataf;
	bool			_open;

public:
	isttree();
	virtual ~isttree();
	bool open(const char *,const char *);
	void close();
	INFO & Info() { return _info; };
	
	
	bool find(const REP_TYPE &,long &);
	void insert(REP_TYPE,IST_DATA);
	bool del(const REP_TYPE &);


	static void createfiles(const char *,const char *);
	fileobject &  get_full_index_file() { return _indexf; };
	datafile<int> & get_full_data_file() { return _dataf; };
	filewrapper & get_index_file() { return _indexf; };
	filewrapper & get_data_file() { return _dataf; };
};

isttree::isttree():_open(false)
{
}

isttree::~isttree()
{
	close();
}

void isttree::close()
{
	if ( _open )
	{
		_indexf.write(&_info,0,sizeof(INFO));
		_indexf.close();
		_dataf.close();
		_open = false;
	}
}

long isttree::FindBPTree(const REP_TYPE &key)
{
	if ( key<_info.leftmostbtree.key )
		return _info.leftmostbtree.branch;

//	if ( key>_info.rightmostbtree.key )
//		return _info.rightmostbtree.branch;

	istnode rootnode(_info.root);
	
	long lbestbtreepos;
	bool bfound = rootnode.find_btree(key,lbestbtreepos,_info.keys,_info.root_limits,false);
	assert( bfound );

	return lbestbtreepos;
}

bool isttree::find(const REP_TYPE &key,long &pos)
{
	long lbestbtreepos = FindBPTree(key);

	bptree<REP_TYPE,IST_DATA> newtree(_indexf,_dataf,lbestbtreepos);

	return newtree.find(key,pos);
}

void isttree::insert(REP_TYPE key,IST_DATA data)
{
	long lbestbtreepos = FindBPTree(key);

	bptree<REP_TYPE,IST_DATA> newtree(_indexf,_dataf,lbestbtreepos);
	newtree.insert(key,data);
}

bool isttree::del(const REP_TYPE &key)
{
	long lbestbtreepos = FindBPTree(key);

	bptree<REP_TYPE,IST_DATA> newtree(_indexf,_dataf,lbestbtreepos);
	return newtree.del(key);
}
/*
void isttree::BulkLoadIntegers(unsigned int size,stabptree<int,int> *pstandalonebptree)
{
	//remember that size cannot be more than UINT_MAX = 4294967295 (0xffffffff)

	//if i don't use ceil() some elements would be left out.
	int btree_size = ceil(Ln(size));
//	btree_size = 1;
	_info.keys = ceil( double(size)/double(btree_size) );

	srand( (unsigned)time( NULL ) );

	KEY_BRANCH_PAIR * pkey_branch = (KEY_BRANCH_PAIR *) malloc( _info.keys*sizeof(KEY_BRANCH_PAIR) );

	int curkeyindex = 0;
	int lastkey=0;

	for (int i=0;(i<_info.keys && curkeyindex<size);i++)
	{
		long lreepos = bptree<REP_TYPE,IST_DATA>::createbtree(_indexf);
		bptree<REP_TYPE,IST_DATA> newtree(_indexf,_dataf,lreepos);
		pkey_branch[i].branch=lreepos;

		for (int j=0;(j<btree_size && curkeyindex<size);j++)
		{
			//the following ensures that i will not have dublicate keys and that they will be sorted ...
			int offset = (int) ((double(rand())/double(RAND_MAX))*(double(INT_MAX-lastkey-1)/double(size-curkeyindex)));
			offset = 0;
			int newkey = lastkey+offset+1;
			
			if ( pstandalonebptree )
				pstandalonebptree->insert(newkey,10);

			newtree.insert(newkey,rand());

			if (j==0)
				pkey_branch[i].key=newkey;

			lastkey = newkey;
			curkeyindex++;
		}
	}

	ConstructTree(pkey_branch);

	_info.leftmostbtree = pkey_branch[0];
	_info.rightmostbtree = pkey_branch[_info.keys-1];

	free(pkey_branch);

	_indexf.write(&_info,0,sizeof(INFO));
}
*/

void isttree::ConstructTree(KEY_BRANCH_PAIR *pkey_branch)
{
	istnode rootnode(fileblock::newpos);

	rootnode.build_node(pkey_branch,0,_info.keys-1);

	_info.root = rootnode.pos();

	_info.depth = 0;
	int reps= floor(pow( _info.keys, (double(IST_REPS_REP_BLOCK) - double(1))/double(IST_REPS_REP_BLOCK)) );
	while (reps>IST_REPS_REP_BLOCK)
	{
		_info.depth++;
		reps= floor(pow( reps , (double(IST_REPS_REP_BLOCK) - double(1))/double(IST_REPS_REP_BLOCK)) );
	}
}

void isttree::createfiles(const char *szindex,const char *szdata)
{
	fileobject index;
	index.create(szindex);
	INFO info;
	index.append(&info,sizeof(INFO));
	index.close();

	datafile<int> data;
	data.create(szdata);
	char dump;
	data.append(&dump,1);//just move to pos 1, as 0 is used to mark the size
	data.close();
}

bool isttree::open(const char *szindexfile,const char *szdatafile)
{
	assert( !_open );
	try
	{
		_indexf.open(szindexfile);
		_dataf.open(szdatafile);
		_indexf.read(&_info,0,sizeof(INFO));
	}
	catch(exception *pe)
	{
		_indexf.close();
		_dataf.close();
		
		printf(pe->what());
		delete pe;
		return false;
	}

	if ( _info.blocksize != BLOCKSIZE )
		throw new isttreeexcept("The specified index file has different block size than specified");
	if ( _info.keysize != sizeof(REP_TYPE) )
		throw new isttreeexcept("The specified index file has different key type than specified");
	if ( _info.datasize != sizeof(IST_DATA) )
		throw new isttreeexcept("The specified index file has different data type than specified");

	istnode_block::pindexfile = &_indexf;
	_open = true;

	return true;
}
