/*
 * Copyright 2010  OpenSourceCodeStewardshipFoundation
 *
 * Licensed under BSD
 */



#include <stdio.h>

#include "DynArray.h"
#include "../vmalloc.h"

//== declarations
void
increaseSizeOfDynArrayTo_Ext( PrivDynArrayInfo *info, int32 newSize );
//==

PrivDynArrayInfo *
makePrivDynArrayInfoFrom( void ***addrOfPtrToArray, int32 sizeOfArray )
 { PrivDynArrayInfo *info;

   info = malloc( sizeof(PrivDynArrayInfo) );

   info->addrOfPtrToArray = addrOfPtrToArray;
   info->sizeOfArray      = sizeOfArray;
   info->numInArray       = 0;
   return info;
 }

PrivDynArrayInfo *
makePrivDynArrayOfSize( void ***addrOfPtrToArray, int32 sizeOfArray )
 { PrivDynArrayInfo *info;

   info = malloc( sizeof(PrivDynArrayInfo) );

   info->addrOfPtrToArray = addrOfPtrToArray;

   *(addrOfPtrToArray)    = malloc( sizeOfArray * sizeof(void *) );
   info->sizeOfArray      = sizeOfArray;
   info->numInArray       = 0;
   return info;
 }

PrivDynArrayInfo *
makePrivDynArrayOfSize_Ext( void ***addrOfPtrToArray, int32 sizeOfArray )
 { PrivDynArrayInfo *info;

   info = malloc( sizeof(PrivDynArrayInfo) );

   info->addrOfPtrToArray = addrOfPtrToArray;

   *(addrOfPtrToArray)    = malloc( sizeOfArray * sizeof(void *) );
   info->sizeOfArray      = sizeOfArray;
   info->numInArray       = 0;
 }


/*A dynamic array is same as any other array, but add a DynArrayInfo next
 * to it.  Accesses and updates of array indexes are done normally, it's
 * only when add a new element into array that use the extra info.
 * An add can cause the pointer to the normal array to change..  so must
 * be protected to single VP at a time.
 *
 *Only need to use this Fn when need a new index, higher than any previous
 */
int32
addToDynArray( void *value, PrivDynArrayInfo *info )
 { int32 numInArray, sizeOfArray;
   void **array;

   numInArray = info->numInArray;
   sizeOfArray    = info->sizeOfArray;

   if( numInArray >= sizeOfArray )
    {
      increaseSizeOfDynArrayTo( info, sizeOfArray * 2 );
    }

   array = *(info->addrOfPtrToArray);
   array[ numInArray ] = value;
   info->numInArray++;

   return numInArray; //pre-incr value is the index put value into
 }
int32
addToDynArray_Ext( void *value, PrivDynArrayInfo *info )
 { int32 numInArray, sizeOfArray;
   void **array;

   numInArray = info->numInArray;
   sizeOfArray    = info->sizeOfArray;

   if( numInArray >= sizeOfArray )
    {
      increaseSizeOfDynArrayTo_Ext( info, sizeOfArray * 2 );
    }

   array = *(info->addrOfPtrToArray);
   array[ numInArray ] = value;
   info->numInArray++;

   return numInArray; //pre-incr value is the index put value into
 }


/*Use this when know how many things going to add in -- then can do this
 * once and use as normal array afterwards.  If later add another chunk,
 * do this again.  Note, this makes new size be just big enough to hold
 * highest index, so will do a linear number of copies if use only this.
 *To cut down on number of copies, can use the increaseSizeTo Fn to
 * exponentially increase size..
 */
void
makeHighestDynArrayIndexBe( PrivDynArrayInfo *info, int32 highestIndex )
 {
   if( info->sizeOfArray <= highestIndex )
    {
      increaseSizeOfDynArrayTo( info, highestIndex + 1 );
    }
   info->numInArray = highestIndex + 1;
 }

void
makeHighestDynArrayIndexBeAtLeast(PrivDynArrayInfo *info, int32 index)
 {
   if( index < info->numInArray ) return;
   else makeHighestDynArrayIndexBe( info, index );
 }


/*Only use this if certain new size is bigger than current size
 */
void
increaseSizeOfDynArrayTo( PrivDynArrayInfo *info, int32 newSize )
 { int32 oldSizeOfArray, i;
   void **newArray, **oldArray;

   oldSizeOfArray   = info->sizeOfArray;
   if( newSize <= oldSizeOfArray ) return;

   oldArray         = *(info->addrOfPtrToArray);
   newArray         = malloc( newSize * sizeof(void *) );

   for( i = 0; i < oldSizeOfArray; i++ )
    {
      newArray[i] = oldArray[i];
    }
   *(info->addrOfPtrToArray) = newArray; //change location of array-ptr
   info->sizeOfArray = newSize;

   free( oldArray );
 }

/*Can't mix malloc locations with external malloc locations -- so use
 * this version inside VMS, which will perform normal malloc in the core
 * loop -- hopefully avoiding the annoying system-stack bugs..
 */
void
increaseSizeOfDynArrayTo_Ext( PrivDynArrayInfo *info, int32 newSize )
 { int32 oldSizeOfArray, i;
   void **newArray, **oldArray;

   oldSizeOfArray   = info->sizeOfArray;
   if( newSize <= oldSizeOfArray ) return;

   oldArray         = *(info->addrOfPtrToArray);
   newArray         = malloc( newSize * sizeof(void *) );

   for( i = 0; i < oldSizeOfArray; i++ )
    {
      newArray[i] = oldArray[i];
    }
   *(info->addrOfPtrToArray) = newArray; //change location of array-ptr
   info->sizeOfArray = newSize;

   free( oldArray );
 }


/* Frees the array, plus the info
 */
void
freeDynArrayDeep( PrivDynArrayInfo *info, FreeFnPtr freeFnPtr )
 {
   forAllInDynArrayDo( info, freeFnPtr );
   free( *(info->addrOfPtrToArray) );
   free( info );
 }

/* Only frees the info
 */
void
freeDynArrayFlat( PrivDynArrayInfo *info )
 {
   free( info );
 }


/*The function has a fixed prototype: takes a void * returns void
 * So, the function has to internally cast void * to whatever data struc..
 */
void
forAllInDynArrayDo( PrivDynArrayInfo *info, DynArrayFnPtr fnPtr )
 { int32 idx;
   void **array;

   array = *(info->addrOfPtrToArray);
   for( idx = 0; idx < info->numInArray; idx++ )
    {
      (*fnPtr)(array[idx]);
    }
 }

