#include <stdlib.h>

#include "swis.h"

#include "Desk.Error2.h"
#include "Desk.Debug.h"
#include "Desk.Core.h"
#include "Desk.DynamArea.h"

#include "MemCheck.MemCheck.h"





typedef union	{
	int	value;
	struct	{
		unsigned int	access			:  4;
		unsigned int	notbufferable		:  1;
		unsigned int	notcacheable		:  1;
		unsigned int	doublemapped		:  1;
		unsigned int	notdraggable		:  1;
		unsigned int	specificphysicalpages	:  1;
		unsigned int	other9_31		: 23;
		}
		data;
	}
	Desk_dynamicarea_flags;



static Desk_DynamicArea_block*	Desk_DynamicArea_lastblock = NULL;
/*
Always points to last dynamic area created.
 */


void	Desk_DynamicArea_DeleteAll( void)
	{
	Desk_Debug_Printf( Desk_error_PLACE "Desk_DynamicArea_DeleteAll\n");
	while ( Desk_DynamicArea_lastblock)	{
		Desk_Debug_Printf( Desk_error_PLACE "Desk_DynamicArea_DeleteAll deleting dynamic area at 0x%p\n", Desk_DynamicArea_lastblock);
		Desk_DynamicArea_Delete( Desk_DynamicArea_lastblock);
		Desk_Debug_Printf( 
			Desk_error_PLACE "Desk_DynamicArea_DeleteAll deleted dynamic area, Desk_DynamicArea_lastblock=0x%p\n", 
			Desk_DynamicArea_lastblock
			);
		}
	Desk_Debug_Printf( Desk_error_PLACE "Desk_DynamicArea_DeleteAll finished\n");
	}



void	Desk_DynamicArea_Create( Desk_DynamicArea_block* da, size_t maxsize, const char* name)
	{
	Desk_dynamicarea_flags	flags;
	
	#ifndef Desk__MODULE_CLIENT
		static int		inited = 0;
		
		if (!inited)	{
			atexit( Desk_DynamicArea_DeleteAll);
			inited = 1;
			}
	#endif
	/*
	atexit doesn't work too well in modules, so it is up to the
	client to call Desk_DynamicArea_DeleteAll from its module
	finalisation code.
	 */
	
	da->datasize	= 0;
	
	flags.value 		= 0;
	flags.data.notdraggable	= 1;
	
	Desk_Error2_CheckOS(
		_swix( OS_DynamicArea, _INR( 0, 8)|_OUT(1)|_OUT(3),
			0,		/* Create		*/
			-1,		/* No initial id	*/
			0,		/* Initial size		*/
			-1,		/* No initial base	*/
			flags.value,	/* Flags	*/
			maxsize,	/* Max size		*/
			NULL,		/* Handler		*/
			NULL,		/* Reference for handler*/
			name,		/* Name	*/
			&da->id,
			&da->data
			)
		);
	/* Get size of created area	*/
	Desk_Error2_CheckOS(
		_swix( OS_DynamicArea, _INR( 0, 1)|_OUT(2), 2, da->id, &da->size)
		);
	
	da->previous			= Desk_DynamicArea_lastblock;
	Desk_DynamicArea_lastblock	= da;
	
	MemCheck_RegisterMiscBlock( da->data, 0);
	
	Desk_Debug_Printf( Desk_error_PLACE "Desk_DynamicArea_Create for da 0x%p, da->id=%i, size=%i\n", da, da->id, da->size);
	}



#define	Desk_DynamicArea__ASSERTBLOCK( da)	\
	do	{				\
		Desk_Debug_Assert( da);		\
		Desk_Debug_Assert( (da)->id);	\
		Desk_Debug_Assert( (da)->data);	\
		}				\
		while (0)



void	Desk_DynamicArea_Delete( Desk_DynamicArea_block* da)
	{
	Desk_Debug_Printf( Desk_error_PLACE "Desk_DynamicArea_Delete called, da=0x%p\n", da);
	
	Desk_DynamicArea__ASSERTBLOCK( da);
	
	Desk_Debug_Printf( Desk_error_PLACE "Desk_DynamicArea_Delete called for da 0x%p, da->id=%i\n", da, da->id);
	
	Desk_Error2_CheckOS(
		_swix( OS_DynamicArea, _INR( 0, 1), 1, da->id)
		);
	
	MemCheck_UnRegisterMiscBlock( da->data);
	
	da->id		= 0;
	da->data	= NULL;
	da->datasize	= 0;
	da->size	= 0;
	
	
		{
		/* Keep the single-linked list of dynamic areas up-to-date.	*/
		if ( Desk_DynamicArea_lastblock==da)	{
			Desk_Debug_Printf( Desk_error_PLACE "DA being deleted is last DA, setting Desk_DynamicArea_lastblock to 0x%p\n", da->previous);
			Desk_DynamicArea_lastblock = da->previous;
			}
		else	{
			Desk_DynamicArea_block*	d;
			for ( d = Desk_DynamicArea_lastblock; d; d=d->previous)	{
				if ( d->previous==da)	{
					d->previous = da->previous;
					break;
					}
				}
			}
		}
	}




void*	Desk_DynamicArea_SetSize( Desk_DynamicArea_block* da, size_t newsize)
	{
	Desk_os_error*	e;
	int		dsize		= newsize - da->size;
	unsigned int	dsizeout	= 0;
	
	Desk_DynamicArea__ASSERTBLOCK( da);
	
	e = (Desk_os_error*) _swix( OS_ChangeDynamicArea, _INR(0,1)|_OUT(1), da->id, dsize, &dsizeout);
	
	da->size += (dsize > 0) ? dsizeout : -dsizeout;
	
	Desk_Debug_Printf( Desk_error_PLACE "Desk_DynamicArea_Realloc. original size=%i, dsize=%i, dsizeout=%i\n", da->size, dsize, dsizeout);
	
	if ( e)	Desk_Error2_CheckOS( e);
	
	MemCheck_UnRegisterMiscBlock( da->data);
	MemCheck_RegisterMiscBlock( da->data, newsize);
	
	da->datasize	= newsize;
	return da->data;
	}
