/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---- includes ----------------------------------------------------------- */

#include "b_BasicEm/Functions.h"
#include "b_BasicEm/Context.h"
#include "b_BasicEm/String.h"

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ auxiliary functions } ---------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ constructor / destructor } ----------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

void bbs_Context_init( struct bbs_Context* cpA )
{
	uint32 iL;
	for( iL = 0; iL < bbs_CONTEXT_MAX_ERRORS; iL++ )
	{
		cpA->errStackE[ iL ].errorE = bbs_ERR_OK;
		cpA->errStackE[ iL ].fileE[ 0 ] = 0;
		cpA->errStackE[ iL ].lineE = 0;
		cpA->errStackE[ iL ].textE[ 0 ] = 0;
	}

	cpA->errIndexE = 0;

	bbs_MemTbl_init( cpA, &cpA->memTblE );

	for( iL = 0; iL < bbs_CONTEXT_MAX_MEM_MANAGERS; iL++ )
	{
		bbs_DynMemManager_init( cpA, &cpA->dynMemManagerArrE[ iL ] );
	}

	cpA->dynMemManagerArrSizeE = 0;
	cpA->errorHandlerE = NULL;
	cpA->callbackHandlerE = NULL;
	cpA->userPtrE = NULL;
}

/* ------------------------------------------------------------------------- */

void bbs_Context_exit( struct bbs_Context* cpA )
{
	uint32 iL;
	for( iL = 0; iL < bbs_CONTEXT_MAX_ERRORS; iL++ )
	{
		cpA->errStackE[ iL ].errorE = bbs_ERR_OK;
		cpA->errStackE[ iL ].fileE[ 0 ] = 0;
		cpA->errStackE[ iL ].lineE = 0;
		cpA->errStackE[ iL ].textE[ 0 ] = 0;
	}

	cpA->errIndexE = 0;

	bbs_MemTbl_exit( cpA, &cpA->memTblE );

	for( iL = 0; iL < cpA->dynMemManagerArrSizeE; iL++ )
	{
		bbs_DynMemManager_freeAll( cpA, &cpA->dynMemManagerArrE[ iL ] );
	}

	for( iL = 0; iL < bbs_CONTEXT_MAX_MEM_MANAGERS; iL++ )
	{
		bbs_DynMemManager_exit( cpA, &cpA->dynMemManagerArrE[ iL ] );
	}

	cpA->dynMemManagerArrSizeE = 0;
	cpA->errorHandlerE = NULL;
	cpA->callbackHandlerE = NULL;
	cpA->userPtrE = NULL;
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ operators } -------------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

void bbs_Context_copy( struct bbs_Context* cpA, const struct bbs_Context* srcPtrA )
{
	bbs_ERROR0( "void bbs_Context_copy( struct bbs_Context* cpA, const struct bbs_Context* srcPtrA ):\n"
		        "A comtext object cannot be copied" );
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ query functions } -------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ modify functions } ------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
	
struct bbs_Error bbs_Error_create( uint32 errorA, 
								   uint32 lineA, 
								   const char* fileA, 
								   const char* textA, 
								   ... )
{
	struct bbs_Error errorL;
	errorL.errorE = errorA;
	errorL.lineE = lineA;

	if( fileA != NULL )
	{
		uint32 lenL = bbs_strlen( fileA );
		uint32 ofsL = ( lenL + 1 > bbs_ERROR_MAX_FILE_CHARS ) ? lenL + 1 - bbs_ERROR_MAX_FILE_CHARS : 0;
		bbs_strcpy( errorL.fileE, fileA + ofsL );
	}
	else
	{
		errorL.fileE[ 0 ] = 0;
	}

	if( textA != NULL )
	{
		va_list argsL;
		va_start( argsL, textA );
		bbs_vsnprintf( errorL.textE, bbs_ERROR_MAX_TEXT_CHARS, textA, argsL );
		va_end( argsL );
	}
	else
	{
		errorL.textE[ 0 ] = 0;
	}

	return errorL;
}

/* ------------------------------------------------------------------------- */
	
/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ I/O } -------------------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
	
/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ exec functions } --------------------------------------------- */
/*                                                                           */
/* ========================================================================= */
	
/* ------------------------------------------------------------------------- */

flag bbs_Context_pushError( struct bbs_Context* cpA, struct bbs_Error errorA )
{
	flag returnL = FALSE;
	if( cpA->errIndexE < bbs_CONTEXT_MAX_ERRORS )
	{
		cpA->errStackE[ cpA->errIndexE++ ] = errorA;
		returnL = TRUE;
	}

	if( cpA->errorHandlerE != NULL )
	{
		cpA->errorHandlerE( cpA );
	}

	return returnL;
}

/* ------------------------------------------------------------------------- */

struct bbs_Error bbs_Context_popError( struct bbs_Context* cpA )
{
	if( cpA->errIndexE > 0 )
	{
		return cpA->errStackE[ --( cpA->errIndexE ) ];
	}
	else
	{
		return cpA->errStackE[ 0 ];
	}
}

/* ------------------------------------------------------------------------- */

struct bbs_Error bbs_Context_peekError( struct bbs_Context* cpA )
{
	if( cpA->errIndexE > 0 )
	{
		return cpA->errStackE[ cpA->errIndexE - 1 ];
	}
	else
	{
		return cpA->errStackE[ 0 ];
	}
}

/* ------------------------------------------------------------------------- */

flag bbs_Context_error( struct bbs_Context* cpA )
{
	return cpA->errIndexE > 0;
}

/* ------------------------------------------------------------------------- */

bbs_errorFPtr bbs_Context_setErrorHandler( struct bbs_Context* cpA, 
									       bbs_errorFPtr errorHandlerA )
{
	bbs_errorFPtr oldErrorHandlerL = cpA->errorHandlerE;
	cpA->errorHandlerE = errorHandlerA;
	return oldErrorHandlerL;
}

/* ------------------------------------------------------------------------- */

void bbs_Context_doCallback( struct bbs_Context* cpA )
{
	if( cpA->callbackHandlerE != NULL )
	{
		uint32 errorL = ( *cpA->callbackHandlerE )( cpA );
		if( errorL != bbs_ERR_OK ) 
		{
			bbs_Context_pushError( cpA, bbs_Error_create( errorL, 0, NULL, NULL ) );
		}
	}
}

/* ------------------------------------------------------------------------- */

bbs_callbackFPtr bbs_Context_setCallbackHandler( struct bbs_Context* cpA,
									       bbs_callbackFPtr callbackHandlerA )
{
	bbs_callbackFPtr oldCallbackHandlerL = cpA->callbackHandlerE;
	cpA->callbackHandlerE = callbackHandlerA;
	return oldCallbackHandlerL;
}

/* ------------------------------------------------------------------------- */

/** adds a static memory segment to memory table of context */
void bbs_Context_addStaticSeg(	struct bbs_Context* cpA,
							    uint16* memPtrA, /* pointer to memory */
								uint32 sizeA,    /* size of memory segment in 16 bit units */
								flag sharedA,    /* Indicates that this segment is to be shared among multiple objects */
								uint32 idA )     /* ID of segment, id=0: unspecified */
{
	struct bbs_MemSeg memSegL;
	bbs_DEF_fNameL( "void bbs_Context_addStaticSeg(....)" )


	/* checks */
	if( sharedA && cpA->memTblE.ssSizeE == bbs_MAX_MEM_SEGS )
	{
		bbs_ERROR1( "%s:\nShared Memory Table is full! Increase bbs_MAX_MEM_SEGS", fNameL );
		return;
	}
	if( sharedA && cpA->memTblE.esSizeE == bbs_MAX_MEM_SEGS )
	{
		bbs_ERROR1( "%s:\nExclusive Memory Table is full! Increase bbs_MAX_MEM_SEGS", fNameL );
		return;
	}


	bbs_MemSeg_init( cpA, &memSegL );
	memSegL.memPtrE = memPtrA;
	memSegL.sizeE = sizeA;
	memSegL.allocIndexE = 0;
	memSegL.sharedE = sharedA;
	memSegL.idE = idA;
	memSegL.dynMemManagerPtrE = NULL;

	if( sharedA )
	{
		cpA->memTblE.ssArrE[ cpA->memTblE.ssSizeE++ ] = memSegL;
	}
	else
	{
		cpA->memTblE.esArrE[ cpA->memTblE.esSizeE ] = memSegL;
		cpA->memTblE.espArrE[ cpA->memTblE.esSizeE ] = &cpA->memTblE.esArrE[ cpA->memTblE.esSizeE ];
		cpA->memTblE.esSizeE++;
	}
}

/* ------------------------------------------------------------------------- */

/* adds a dynamic memory segment to memory table of context
 * Upon destruction of the context object any residual will be freed automatically
 */
void bbs_Context_addDynamicSeg(	struct bbs_Context* cpA,
								bbs_mallocFPtr mallocFPtrA,	/* function pointer to external mem alloc function (s. comment of type declaration)*/
								bbs_freeFPtr freeFPtrA,     /* function pointer to external mem free function */
								flag sharedA,    /* Indicates that this segment is to be shared among multiple objects */
								uint32 idA )     /* ID of segment, id=0: unspecified */
{
	struct bbs_DynMemManager memManagerL;
	struct bbs_MemSeg memSegL;
	bbs_DEF_fNameL( "void bbs_Context_addDynamicSeg(....)" )


	/* checks */
	if( cpA->dynMemManagerArrSizeE == bbs_CONTEXT_MAX_MEM_MANAGERS )
	{
		bbs_ERROR1( "%s:\nMemory Manager Table is full! Increase bbs_CONTEXT_MAX_MEM_MANAGERS", fNameL );
		return;
	}
	if( sharedA && cpA->memTblE.ssSizeE == bbs_MAX_MEM_SEGS )
	{
		bbs_ERROR1( "%s:\nShared Memory Table is full! Increase bbs_MAX_MEM_SEGS", fNameL );
		return;
	}
	if( sharedA && cpA->memTblE.esSizeE == bbs_MAX_MEM_SEGS )
	{
		bbs_ERROR1( "%s:\nExclusive Memory Table is full! Increase bbs_MAX_MEM_SEGS", fNameL );
		return;
	}
	
	bbs_DynMemManager_init( cpA, &memManagerL );
	memManagerL.mallocFPtrE = mallocFPtrA;
	memManagerL.freeFPtrE = freeFPtrA;
	memManagerL.memPtrE = NULL;
	cpA->dynMemManagerArrE[ cpA->dynMemManagerArrSizeE++ ] = memManagerL;

	bbs_MemSeg_init( cpA, &memSegL );
	memSegL.memPtrE = NULL;
	memSegL.sizeE = 0;
	memSegL.allocIndexE = 0;
	memSegL.sharedE = sharedA;
	memSegL.idE = idA;
	memSegL.dynMemManagerPtrE = &cpA->dynMemManagerArrE[ cpA->dynMemManagerArrSizeE - 1 ];

	if( sharedA )
	{
		cpA->memTblE.ssArrE[ cpA->memTblE.ssSizeE++ ] = memSegL;
	}
	else
	{
		cpA->memTblE.esArrE[ cpA->memTblE.esSizeE ] = memSegL;
		cpA->memTblE.espArrE[ cpA->memTblE.esSizeE ] = &cpA->memTblE.esArrE[ cpA->memTblE.esSizeE ];
		cpA->memTblE.esSizeE++;
	}
}
			  
/* ------------------------------------------------------------------------- */

uint32 bbs_Context_exclAllocSize( struct bbs_Context* cpA, uint32 segIndexA )
{
	return bbs_MemSeg_allocatedSize( cpA, &cpA->memTblE.esArrE[ segIndexA ] );
}
								  
/* ------------------------------------------------------------------------- */

uint32 bbs_Context_shrdAllocSize( struct bbs_Context* cpA, uint32 segIndexA )
{
	return bbs_MemSeg_allocatedSize( cpA, &cpA->memTblE.ssArrE[ segIndexA ] );
}
								  
/* ------------------------------------------------------------------------- */

void bbs_Context_quickInit( struct bbs_Context* cpA, 
	 					    bbs_mallocFPtr mallocFPtrA,	/* function pointer to external mem alloc function (s. comment of type declaration)*/
						    bbs_freeFPtr freeFPtrA,
						    bbs_errorFPtr errorHandlerA )
{
	bbs_Context_init( cpA );
	bbs_Context_addDynamicSeg( cpA, mallocFPtrA, freeFPtrA, FALSE, 0 );
	bbs_Context_addDynamicSeg( cpA, mallocFPtrA, freeFPtrA, TRUE, 0 );
	bbs_Context_setErrorHandler( cpA, errorHandlerA );
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */