/** 
 * @file lscript_alloc.h
 * @brief General heap management for scripting system
 *
 * $LicenseInfo:firstyear=2002&license=viewergpl$
 * 
 * Copyright (c) 2002-2008, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#ifndef LL_LSCRIPT_ALLOC_H
#define LL_LSCRIPT_ALLOC_H
// #define at top of file accelerates gcc compiles
// Under gcc 2.9, the manual is unclear if comments can appear above #ifndef
// Under gcc 3, the manual explicitly states comments can appear above the #ifndef

#include "lscript_byteconvert.h"
#include "lscript_library.h"

void reset_hp_to_safe_spot(const U8 *buffer);


// supported data types

//	basic types
//	integer			4 bytes of integer data
//	float			4 bytes of float data
//	string data		null terminated 1 byte string
//	key data		null terminated 1 byte string
//	vector data		12 bytes of 3 floats
//	quaternion data	16 bytes of 4 floats

//	list type
//	list data		4 bytes of number of entries followed by followed by pointer

//	string pointer		4 bytes of address of string data on the heap (only used in list data)
//  key pointer			4 bytes of address of key data on the heap (only used in list data)

// heap format
// 
// 4 byte offset to next block (in bytes)
// 1 byte of type of variable or empty
// 2 bytes of reference count
// nn bytes of data

const S32 MAX_HEAP_SIZE = TOP_OF_MEMORY;

class LLScriptAllocEntry
{
public:
	LLScriptAllocEntry() : mSize(0), mType(LST_NULL), mReferenceCount(0) {}
	LLScriptAllocEntry(S32 offset, U8 type) : mSize(offset), mType(type), mReferenceCount(1) {}
	friend std::ostream&	 operator<<(std::ostream& s, const LLScriptAllocEntry &a)
	{
		s << "Size: " << a.mSize << " Type: " << LSCRIPTTypeNames[a.mType] << " Count: " << a.mReferenceCount;
		return s;
	}

	S32 mSize;
	U8	mType;
	S16 mReferenceCount;
};

// this is only OK because we only load/save via accessors below
const S32 SIZEOF_SCRIPT_ALLOC_ENTRY = 7;

inline void alloc_entry2bytestream(U8 *buffer, S32 &offset, const LLScriptAllocEntry &entry)
{
	if (  (offset < 0)
		||(offset > MAX_HEAP_SIZE))
	{
		set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
	}
	else
	{
		integer2bytestream(buffer, offset, entry.mSize);
		byte2bytestream(buffer, offset, entry.mType);
		s162bytestream(buffer, offset, entry.mReferenceCount);
	}
}

inline void bytestream2alloc_entry(LLScriptAllocEntry &entry, U8 *buffer, S32 &offset)
{
	if (  (offset < 0)
		||(offset > MAX_HEAP_SIZE))
	{
		set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
		reset_hp_to_safe_spot(buffer);
	}
	else
	{
		entry.mSize = bytestream2integer(buffer, offset);
		entry.mType = bytestream2byte(buffer, offset);
		entry.mReferenceCount = bytestream2s16(buffer, offset);
	}
}

// create a heap from the HR to TM
BOOL lsa_create_heap(U8 *heap_start, S32 size);
void lsa_fprint_heap(U8 *buffer, LLFILE *fp);

void lsa_print_heap(U8 *buffer);

// adding to heap
//	if block is empty
//		if block is at least block size + 4 larger than data
//			split block
//			insert data into first part
//			return address
//		else
//			insert data into block
//			return address
//	else
//		if next block is >= SP 
//			set Stack-Heap collision
//			return NULL
//		if next block is empty
//			merge next block with current block
//			go to start of algorithm
//		else
//			move to next block
//			go to start of algorithm

S32 lsa_heap_add_data(U8 *buffer, LLScriptLibData *data, S32 heapsize, BOOL b_delete);

S32 lsa_heap_top(U8 *heap_start, S32 maxsize);

// split block
//	set offset to point to new block
//	set offset of new block to point to original offset - block size - data size
//	set new block to empty
//	set new block reference count to 0
void lsa_split_block(U8 *buffer, S32 &offset, S32 size, LLScriptAllocEntry &entry);

// insert data
//	if data is non-list type
//		set type to basic type, set reference count to 1, copy data, return address
//	else
//		set type to list data type, set reference count to 1
//		for each list entry
//			insert data
//			return address

void lsa_insert_data(U8 *buffer, S32 &offset, LLScriptLibData *data, LLScriptAllocEntry &entry, S32 heapsize);

S32 lsa_create_data_block(U8 **buffer, LLScriptLibData *data, S32 base_offset);

// increase reference count
//		increase reference count by 1

void lsa_increase_ref_count(U8 *buffer, S32 offset);

// decrease reference count
//		decrease reference count by 1
//		if reference count == 0
//			set type to empty

void lsa_decrease_ref_count(U8 *buffer, S32 offset);

inline S32 get_max_heap_size(U8 *buffer)
{
	return get_register(buffer, LREG_SP) - get_register(buffer, LREG_HR);
}


LLScriptLibData *lsa_get_data(U8 *buffer, S32 &offset, BOOL b_dec_ref);
LLScriptLibData *lsa_get_list_ptr(U8 *buffer, S32 &offset, BOOL b_dec_ref);

S32 lsa_cat_strings(U8 *buffer, S32 offset1, S32 offset2, S32 heapsize);
S32 lsa_cmp_strings(U8 *buffer, S32 offset1, S32 offset2);

S32 lsa_cat_lists(U8 *buffer, S32 offset1, S32 offset2, S32 heapsize);
S32 lsa_cmp_lists(U8 *buffer, S32 offset1, S32 offset2);
S32 lsa_preadd_lists(U8 *buffer, LLScriptLibData *data, S32 offset2, S32 heapsize);
S32 lsa_postadd_lists(U8 *buffer, S32 offset1, LLScriptLibData *data, S32 heapsize);

// modifying a list
//	insert new list that is modified
//	store returned address in original list's variable
//	decrease reference count on old list

//	list l1 = [10];
//	list l2 = l1;
//	l1 = [11];

//	we want l2 == [10];

// more complicated example:
//	list l1 = [10, 11];
//	list l2 = l1;
//	l1[0] = 12

// I think that we want l2 = [10, 11];

// one option would be to use syntax like:
//	l1 = llSetList(l1, 0, 12);
// but this would require variable argument list matching
// which maybe is ok, but would be work
// the other option would be changes to lists that have multiple references causes a copy to occur

// popl @l1, 0, integer, 12
//
// would cause l1 to be copied, 12 to replace the 0th entry, and the address of the new list to be saved in l1
//

inline LLScriptLibData *lsa_bubble_sort(LLScriptLibData *src, S32 stride, S32 ascending)
{
	S32 number = src->getListLength();

	if (number <= 0)
	{
		return NULL;
	}

	if (stride <= 0)
	{
		stride = 1;
	}

	S32 i = 0;

	if (number % stride)
	{
		LLScriptLibData *retval = src->mListp;
		src->mListp = NULL;
		return retval;
	}

	LLScriptLibData **sortarray = new LLScriptLibData*[number];

	LLScriptLibData *temp = src->mListp;
	while (temp)
	{
		sortarray[i] = temp;
		i++;
		temp = temp->mListp;
	}

	S32 j, s;

	for (i = 0; i < number; i += stride)
	{
		for (j = i; j < number; j += stride)
		{
			if (  ((*sortarray[i]) <= (*sortarray[j]))
				!= (ascending == TRUE))
			{
				for (s = 0; s < stride; s++)
				{
					temp = sortarray[i + s];
					sortarray[i + s] = sortarray[j + s];
					sortarray[j + s] = temp;
				}
			}
		}
	}

	i = 1;
	temp = sortarray[0];
	while (i < number)
	{
		temp->mListp = sortarray[i++];
		temp = temp->mListp;
	}
	temp->mListp = NULL;

	src->mListp = NULL;

	temp = sortarray[0];
	delete[] sortarray;
	return temp;
}


LLScriptLibData* lsa_randomize(LLScriptLibData* src, S32 stride);

#endif
