#include "stack.h"

STACK *createStack(int num_items, size_t unit_size)
{
  STACK *newStack = (STACK *)malloc(1 * sizeof(STACK));

  newStack->block_list = NULL;
  newStack->items_per_block = num_items;
  newStack->item_size = unit_size;
  newStack->item_c = 0;
  return newStack;
}

void emptyStack(STACK *astack)
{
  BLOCK_STARTER *block;

  if(!astack || !astack->block_list)
    return;

  block = astack->block_list;

  if(block->next)
    block = block->next;

  astack->block_list = block;
  astack->item_c = 0;
  astack->index_in_block = 0;
}

void freeStack(STACK *astack)
{
  BLOCK_STARTER *ite_block, *temp_block;

  if(!astack)
    return;

  ite_block = astack->block_list;

  if(ite_block)
    {
      while(ite_block->next)
        ite_block = ite_block->next;
    }

  while(ite_block)
    {
      temp_block = ite_block;
      ite_block = ite_block->prev;
      free((void *)temp_block);
    }

  free((void *)astack);
}

void stackBackup(STACK *astack)
{
  astack->block_backup = astack->block_list;
  astack->index_backup = astack->index_in_block;
  astack->item_c_backup = astack->item_c;
}

void stackRecover(STACK *astack)
{
  astack->block_list = astack->block_backup;
  astack->index_in_block = astack->index_backup;
  astack->item_c = astack->item_c_backup;
}

void *stackPop(STACK *astack)
{
  BLOCK_STARTER *block;

  if(!astack || !astack->block_list || !astack->item_c)
    return NULL;

  astack->item_c--;
  block = astack->block_list;

  if(astack->index_in_block == 1)
    {
      if(block->next)
        {
          astack->block_list = block->next;
          astack->index_in_block = astack->items_per_block;
        }
      else
        {
          astack->index_in_block = 0;
          astack->item_c = 0;
        }

      return (void *)((void *)block + sizeof(BLOCK_STARTER));

    }

  return (void *)((void *)block + sizeof(BLOCK_STARTER) + astack->item_size * (--astack->index_in_block));
}

void *stackPush(STACK *astack)
{
  BLOCK_STARTER *block;

  if(!astack)
    return NULL;

  astack->item_c++;

  if(!astack->block_list || (astack->index_in_block == astack->items_per_block && !astack->block_list->prev))
    {
      block = malloc(sizeof(BLOCK_STARTER) + astack->items_per_block * astack->item_size);
      block->prev = NULL;

      if(astack->block_list)
        astack->block_list->prev = block;

      block->next = astack->block_list;
      astack->block_list = block;
      astack->index_in_block = 1;
      return (void *)((void *)block + sizeof(BLOCK_STARTER));
    }
  else if(astack->index_in_block == astack->items_per_block && astack->block_list->prev)
    {
      astack->block_list = astack->block_list->prev;
      astack->index_in_block = 1;
      return (void *)((void *)astack->block_list + sizeof(BLOCK_STARTER));
    }

  block = astack->block_list;
  return (void *)((void *)block + sizeof(BLOCK_STARTER) + astack->item_size * astack->index_in_block++);

}
