#include <memory.h>
#include "mk.h"

static
 void mk_gc_scan_vector( MK_VECTOR *target );
static
void mk_gc_scan_hashtable( MK_HASHTABLE *target );
static 
void mk_gc_scan_vm_frame_item( MK_VM_FRAME_ITEM *target );
static
void mk_gc_clear_gc_bit( MK_MANAGED_VM_FRAME_ITEM_TABLE *frameItemTables );
static
void mk_gc_sweep_frame_item_table( MK_VM_STRUCT *vm );
static
void mk_gc_sweep_vector_table( MK_VM_STRUCT *vm );
static
void mk_gc_sweep_hashtable_table( MK_VM_STRUCT *vm );
static
void mk_gc_sweep_frame_table( MK_VM_STRUCT *vm );
static
MK_VM_FRAME_ITEM *mk_gc_get_vm_frame_item_top( MK_VM_FRAME_ITEM *target);

// gc mark & sweep implements.

int mk_gc_run( MK_VM_STRUCT *vm, int force )
{
	MK_VM_FRAME *pFrame = NULL;

	MK_MANAGED_VM_FRAME_ITEM_TABLE *pCurrentFrameTable = vm->pFrameItemTable;
	MK_MANAGED_HASH_TABLE *pCurrentHash = vm->pHashTable;

	if( vm->gcPhase == GC_EXECUTE_TIME_PER_CALL || force != 0 )
	{
		int index = 0;
		// clear local stack gcbit.
		MK_GC_CLEAR_GC_BIT( &vm->localStack->flags );

		// scan local stack
		mk_gc_scan_vector( vm->localStack );

		// exception object
		mk_gc_scan_vm_frame_item( vm->exceptionObject );

		// scan global
		mk_gc_scan_hashtable( vm->global );

		// scan vm_frame
		pFrame = pFrame = vm->pCurrentFrame;
		while( pFrame )
		{
			// set gc bit
			MK_GC_SET_GC_BIT( &pFrame->flags );

			// pThis
			mk_gc_scan_vm_frame_item( pFrame->pThis );

			// local variables
			mk_gc_scan_hashtable( pFrame->localVariables );

			pFrame = pFrame->previous;
		}

		// sweep
		// todo: optimize gc timing.
		mk_gc_sweep_vector_table( vm );
		mk_gc_sweep_hashtable_table( vm );
		mk_gc_sweep_frame_item_table( vm );
		mk_gc_sweep_frame_table( vm );
		vm->gcPhase = 0;
	}
	return 0;
}

static
MK_VM_FRAME_ITEM *mk_gc_get_vm_frame_item_top( MK_VM_FRAME_ITEM *target)
{
	if( target != NULL )
	{
		while( ( ( (INT_PTR)target & MK_VM_FRAME_ITEM_TYPE_DIRECT_VALUE ) == 0 ) &&
			( MK_TYPE_ATTRIBUTE( target->flags ) == MK_VM_FRAME_ITEM_TYPE_CLASS ) &&
			( target->classTypeValue.child != NULL ) )
		{
			if( ( (INT_PTR)target->classTypeValue.child & MK_VM_FRAME_ITEM_TYPE_DIRECT_VALUE ) == 0 )
				target = target->classTypeValue.child;
			else
				break;
		}
	}
	return target;
}

static
void mk_gc_scan_vector( MK_VECTOR *target )
{
	if( target != NULL && MK_GC_IS_SET_GC_BIT(target->flags) == 0 )
	{
		unsigned int index = 0;
		unsigned int sizeVector = mk_size_vector( target );
		MK_GC_SET_GC_BIT( &target->flags );
		if( MK_TYPE_ATTRIBUTE_VECTOR_STYLE( target->flags ) ==
			MK_TYPE_VECTOR_ARRAY_MANAGED_PTR )
		{
			for( index = 0; index < sizeVector; index ++ )
			{
				MK_VM_FRAME_ITEM *frameItem = 
					(MK_VM_FRAME_ITEM *)mk_get_at_vector( target, index );
				mk_gc_scan_vm_frame_item( mk_gc_get_vm_frame_item_top( frameItem ) );
			}
		}
	}
}

static
void mk_gc_scan_hashtable( MK_HASHTABLE *target )
{
	if( target != NULL && MK_GC_IS_SET_GC_BIT(target->flags) == 0 )
	{
		void *position = 
			mk_enum_item_hashtable_begin( target );
		MK_GC_SET_GC_BIT( &target->flags );
		while( position != NULL )
		{
			const MK_CHAR *key = NULL;
			MK_VM_FRAME_ITEM *value = NULL;
			position = mk_enum_item_hashtable_next( target, position, &key, (void**)&value );
			mk_gc_scan_vm_frame_item( mk_gc_get_vm_frame_item_top( value ) );
		}
	}
}

static
void mk_gc_scan_vm_frame_item( MK_VM_FRAME_ITEM *target )
{
	if( target != NULL && 
		( ( (INT_PTR)target & MK_VM_FRAME_ITEM_TYPE_DIRECT_VALUE ) == 0 ) )
	{
		while( MK_TYPE_ATTRIBUTE( target->flags ) == MK_VM_FRAME_ITEM_TYPE_REFERENCE_VALUE )
		{
			if( MK_GC_IS_SET_GC_BIT( target->flags ) == 0 )
			{
				MK_GC_SET_GC_BIT( &target->flags );
				MK_GC_CLEAR_CN_BIT( &target->flags );
			}
			else
			{
				break;
			}
			target = target->reference;
		}
		if( !MK_GC_IS_SET_GC_BIT( target->flags ) )
		{
			MK_GC_SET_GC_BIT( &target->flags );
			if( MK_TYPE_ATTRIBUTE( target->flags ) == MK_VM_FRAME_ITEM_TYPE_CLASS )
			{
				mk_gc_scan_hashtable( target->classTypeValue.variables );	// child class
				mk_gc_scan_vm_frame_item( target->classTypeValue.child );	// parent class
			}
			else if( MK_TYPE_ATTRIBUTE( target->flags ) == MK_VM_FRAME_ITEM_TYPE_ARRAY_VALUE )
			{
				mk_gc_scan_vector( target->arrayTypeValue );
			}
		}
	}
}

static
void mk_gc_sweep_vector_table( MK_VM_STRUCT *vm )
{
	MK_MANAGED_VECTOR_TABLE *vectorTables = vm->pVectorTable;
	while( vectorTables != NULL )
	{
		int count = 0;
		int countRemove = 0;
		MK_VECTOR *pItem = &(vectorTables->vectors[0]);
		for( count = 0; count < MK_SIZEOF_MANAGED_TABLE; count ++, pItem ++ )
		{
			if( MK_GC_IS_ALLOCATED_OBJECT( pItem->flags ) )
			{
				if( !MK_GC_IS_SET_GC_BIT(pItem->flags ) && !MK_GC_IS_SET_CN_BIT(pItem->flags ) )
				{
					free( pItem->items );
					pItem->items = NULL;
					pItem->size = 0;
					pItem->flags = 0;
					pItem->used = 0;
					countRemove ++;
				}
				else
				{
					MK_GC_CLEAR_GC_BIT( &pItem->flags );
					MK_GC_CLEAR_CN_BIT( &pItem->flags );
				}
			}
		}
		vectorTables->freeSpace += countRemove;
		vectorTables = vectorTables->previous;
	}
}

static
void mk_gc_sweep_hashtable_table( MK_VM_STRUCT *vm )
{
	MK_MANAGED_HASH_TABLE *hashTables = vm->pHashTable;
	while( hashTables != NULL )
	{
		int count = 0;
		int countRemove = 0;
		MK_HASHTABLE *pItem = &(hashTables->hashTables[0]);
		for( count = 0; count < MK_SIZEOF_MANAGED_TABLE; count ++, pItem ++ )
		{
			if( MK_GC_IS_ALLOCATED_OBJECT( pItem->flags ) )
			{
				if( !MK_GC_IS_SET_GC_BIT(pItem->flags ) && !MK_GC_IS_SET_CN_BIT(pItem->flags ) )
				{
					mk_destroy_map_item( vm->memoryPool, pItem->elems, pItem->used, 0 );
					if( pItem->size != MK_SIZEOF_HASH_DEFAULT )
					{	// delete elems if size != MK_SIZEOF_HASH_DEFAULT
						free( pItem->elems );
						pItem->elems = NULL;
						pItem->size = 0;
					}
					pItem->flags = 0;
					pItem->used = 0;
					countRemove ++;
				}
				else
				{
					MK_GC_CLEAR_GC_BIT( &pItem->flags );
					MK_GC_CLEAR_CN_BIT( &pItem->flags );
				}
			}
		}
		hashTables->freeSpace += countRemove;
		hashTables = hashTables->previous;
	}
}

static
void mk_gc_sweep_frame_item_table( MK_VM_STRUCT *vm )
{
	MK_MANAGED_VM_FRAME_ITEM_TABLE *frameItemTables = vm->pFrameItemTable;
	while( frameItemTables != NULL )
	{
		int countRemove = 0;
		MK_VM_FRAME_ITEM *pItem = frameItemTables->table;
		while( pItem != frameItemTables->table + MK_SIZEOF_MANAGED_TABLE )
		{
			if( MK_GC_IS_ALLOCATED_OBJECT( pItem->flags ) )
			{
				if( !MK_GC_IS_SET_GC_BIT(pItem->flags) && !MK_GC_IS_SET_CN_BIT(pItem->flags) )
				{
					mk_destroy_vm_frame_item( vm->memoryPool, pItem );
					countRemove ++;
				}
				else
				{
					MK_GC_CLEAR_GC_BIT( &pItem->flags );
					MK_GC_CLEAR_CN_BIT( &pItem->flags );
				}
			}
			pItem ++;
		}
		frameItemTables->freeSpace += countRemove;
		frameItemTables = frameItemTables->previous;
	}
}

static
void mk_gc_sweep_frame_table( MK_VM_STRUCT *vm )
{
	MK_MANAGED_VM_FRAME_TABLE *pFrameTable = vm->pFrameTable;
	while( pFrameTable != NULL )
	{
		int countRemove = 0;
		MK_VM_FRAME *pFrame = pFrameTable->table;
		while( pFrame != pFrameTable->table + MK_SIZEOF_MANAGED_TABLE )
		{
			if( MK_GC_IS_ALLOCATED_OBJECT( pFrame->flags ) )
			{
				if( !MK_GC_IS_SET_GC_BIT(pFrame->flags) && !MK_GC_IS_SET_CN_BIT(pFrame->flags) )
				{
					pFrame->flags = 0;
					pFrame->localVariables = NULL;
					countRemove ++;
				}
				else
				{
					MK_GC_CLEAR_GC_BIT( &pFrame->flags );
					MK_GC_CLEAR_CN_BIT( &pFrame->flags );
				}
			}
			pFrame ++;
		}
		pFrameTable->freeSpace += countRemove;
		pFrameTable = pFrameTable->previous;
	}
}
