/* This file is part of indexlib.
 * Copyright (C) 2005 Luís Pedro Coelho <luis@luispedro.org>
 *
 * Indexlib is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation and available as file
 * GPL_V2 which is distributed along with indexlib.
 * 
 * Indexlib 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.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA
 * 
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of this program with any edition of
 * the TQt library by Trolltech AS, Norway (or with modified versions
 * of TQt that use the same license as TQt), and distribute linked
 * combinations including the two.  You must obey the GNU General
 * Public License in all respects for all of the code used other than
 * TQt.  If you modify this file, you may extend this exception to
 * your version of the file, but you are not obligated to do so.  If
 * you do not wish to do so, delete this exception statement from
 * your version.
 */

#include "leafdata.h"
#include "logfile.h"
#include <iostream>
#include <algorithm>
#include "boost-compat/next_prior.hpp"
#include "format.h"

namespace {
	memory_manager* manager = 0;
}

memory_manager* get_leafdata_manager() { return manager ; }
void set_leafdata_manager( memory_manager* m ) { manager = m; }

uint32_t leaf_data::get_reference( unsigned idx ) const {
	leafdata_iterator iter = begin();
	while ( idx-- ) { *iter; ++iter; }
	return *iter;
}

bool leaf_data::can_add( uint32_t ref ) const {
	if ( ( capacity() - usedbytes() ) > ( 1 + byte_io::byte_length<uint32_t>() ) ) return true;
	if ( capacity() == usedbytes() ) return false;
	uint32_t last = 0;
	for ( iterator first = begin(), past = end(); first != past; ++first ) {
		assert( first < past );
		last = *first;
		if ( last == ref ) return true;
	}
	return ( ref > last && ( ref - last ) < 256 );
}

bool leaf_data::has_reference( uint32_t ref ) const {
	for ( iterator first = begin(), past = end(); first != past; ++first ) {
		uint32_t here = *first;
		//logfile() << format( "leaf_data[%s]::has_reference( %s ): looking at %s\n" ) % idx_ % ref % here;

		if ( here == ref ) {
			//logfile() << format( "leaf_data[%s]::has_reference( %s ): true\n" ) % idx_ % ref;
			return true;
		}
	}
	//logfile() << format( "leaf_data[%s]::has_reference( %s ): false\n" ) % idx_ % ref;
	return false;
}

void leaf_data::add_reference( uint32_t ref ) {
	//logfile() << format( "leaf_data[%s]::add_reference( %s )\n" ) % idx_ % ref;
	assert( can_add( ref ) );
	if ( has_reference( ref ) ) return;
	iterator first = begin();
	const iterator past = end();
	unsigned value = 0;
	while ( first != past ) {
		value = *first;
		++first;
	}
	++ref;
	if ( usedbytes() ) ++value;
	unsigned char* target = const_cast<unsigned char*>( first.raw() );
	assert( target == my_base() + usedbytes() );
	if ( ref > value && ( ref - value ) < 256 ) {
		assert( ref != value );
		*target = ref - value;
		set_usedbytes( usedbytes() + 1 );
	} else {
		*target++ = 0;
		byte_io::write<uint32_t>( target, ref );
		set_usedbytes( usedbytes() + 1 + byte_io::byte_length<uint32_t>() );
	}
	assert( usedbytes() <= capacity() );
}

void leaf_data::remove_reference( uint32_t ref ) {
	//logfile() << format( "leaf_data[%s]::remove_reference( %s )\n" ) % idx_ % ref;
	unsigned idx = 0;
	iterator first = begin();
	const iterator past = end();
	for ( ; first != past; ++first ) {
		if ( *first == ref ) break;
		++idx;
	}
	if ( first != past ) {
		//assert( get_reference( idx ) == ref );
		iterator next = boost::next( first );
		unsigned nbytes = end().raw() - first.raw();
		std::memmove( const_cast<unsigned char*>( first.raw() ), next.raw(), nbytes );
		set_usedbytes( usedbytes() - nbytes );
		unsigned char* iter = const_cast<unsigned char*>( first.raw() );
		for ( ; iter < end().raw(); ++iter) {
			if (*iter) --*iter;
			else {
				++iter;
				byte_io::write<uint32_t>(iter,byte_io::read<uint32_t>(iter)-1);
				iter += byte_io::byte_length<uint32_t>();
			}
		}
	}
}

unsigned leaf_data::nelems() const {
	unsigned res = 0;
	for ( iterator first = begin(), past =end(); first != past; ++first ) {
		++res;
		*first;
	}
	return res;
}


unsigned leaf_data::next_byte_size() const {
	return 2 * ( capacity() + data_offset );
}

void leaf_data::grow() {
	set_capacity( ( next_byte_size() - data_offset ) );
	memset( my_base() + usedbytes(), 0, capacity() - usedbytes() );
}

void leaf_data::construct( void* m ) {
	unsigned s = leaf_data::start_bytes();
	memset( m, 0, s );
	byte_io::write<uint16_t>( reinterpret_cast<unsigned char*>( m ), ( s - data_offset ) );
}

void leaf_data::print( std::ostream& out ) const {
	//out << format( "\tsize: %8s\n" ) % used();
	out << format( "\tcapacity: %8s\n" ) % capacity();
	//out << format( "\tnext: %8s\n" ) % next();
	int i = 0;
	for ( iterator first = begin(), past = end(); first != past; ++first ) {
		out << format( "\tref[ %1% ] = %2%\n" ) % i++ % *first;
	}
}