/*
 * StringStack Copyright (c) 2017, James Bailie.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *     * The name of James Bailie may not be used to endorse or promote
 * products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdlib.h>
#include <stdio.h>

#define STACK_INC 64

union stack_val
{
   float num;
   void *ptr;
};

struct stack
{
   int free, used;
   union stack_val *top, *values;
};

struct stack *stack_make();
void stack_free( struct stack * );
void stack_clear( struct stack * );

int stack_push( struct stack *, union stack_val );
union stack_val *stack_pop( struct stack * );

int stack_shift( struct stack *, union stack_val * );
int stack_unshift( struct stack *, union stack_val );

struct stack *stack_make()
{
   struct stack *a;

   if (( a = malloc( sizeof( struct stack ))) == NULL )
      return NULL;

   if (( a->values = malloc( sizeof( union stack_val ) * STACK_INC )) == NULL )
   {
      free( a );
      return NULL;
   }

   a->free = STACK_INC;
   a->used = 0;
   a->top = a->values;

   return a;
}

void stack_free( struct stack *a )
{
   free( a->values );
   free( a );
}

void stack_clear( struct stack *s )
{
   s->free += s->used;
   s->used = 0;
   s->top = s->values;
}

int stack_unshift( struct stack *a, union stack_val o )
{
   union stack_val *vptr;
   int n;

   if ( a->free == 0 )
   {
      if (( a->values = realloc( a->values, sizeof( union stack_val ) * ( a->used + STACK_INC ))) == NULL )
         return 1;

      a->free = STACK_INC;
      a->top = &a->values[ a->used - 1 ];
   }

   for( vptr = &a->values[ a->used - 1 ], n = 0; n < a->used; ++n, --vptr )
      *( vptr + 1 ) = *vptr;

   a->values[ 0 ] = o;

   if ( a->used )
      ++a->top;

   ++a->used;
   --a->free;

   return 0;
}

int stack_push( struct stack *a, union stack_val o )
{
   if ( a->free == 0 )
   {
      if (( a->values = realloc( a->values, sizeof( union stack_val ) * ( a->used + STACK_INC ))) == NULL )
         return 1;

      a->free = STACK_INC;
      a->top = &a->values[ a->used - 1 ];
   }

   if ( a->used )
      ++a->top;

   *a->top = o;
   --a->free;
   ++a->used;

   return 0;
}

int stack_shift( struct stack *stack, union stack_val *result )
{
   union stack_val *vptr;
   int n;

   if ( ! stack->used )
      return 1;

   *result = *stack->values;

   --stack->used;
   ++stack->free;

   if ( stack->top != stack->values )
      --stack->top;

   for( vptr = stack->values, n = 0; n < stack->used; ++n, ++vptr )
      *vptr = *( vptr + 1 );

   return 0;
}

union stack_val *stack_pop( struct stack *stack )
{
   union stack_val *result = NULL;

   if ( ! stack->used )
      return result;

   result = stack->top;

   ++stack->free;
   --stack->used;

   if ( stack->top != stack->values )
      --stack->top;

   return result;
}
