/*
 * Andrea Di Biagio
 * Politecnico di Milano, 2007
 * 
 * axe_labels.c
 * Formal Languages & Compilers Machine, 2007/2008
 * 
 */

#include "axe_errors.h"
#include "axe_labels.h"
#include "collections.h"


struct t_axe_label_manager
{
   t_list *labels;
   int current_label_ID;
   t_axe_label *label_to_assign;
};

int isAssignedLabel(t_axe_label_manager *lmanager)
{
   /* preconditions: lmanager must be different from NULL */
   if (lmanager == NULL)
      notifyError(AXE_INVALID_LABEL_MANAGER);

   if (  (lmanager->label_to_assign != NULL)
         && ((lmanager->label_to_assign)->labelID != LABEL_UNSPECIFIED) )
   {
      return 1;
   }

   return 0;
}

int compareLabels(t_axe_label *labelA, t_axe_label *labelB)
{
   if ( (labelA == NULL) || (labelB == NULL) )
      return 0;

   if (labelA->labelID == labelB->labelID)
      return 1;
   return 0;
}

/* reserve a new label identifier and return the identifier to the caller */
t_axe_label * reserveLabelID(t_axe_label_manager *lmanager)
{
   t_axe_label *result;

   /* preconditions: lmanager must be different from NULL */
   assert(lmanager != NULL);
   
   /* initialize a new label */
   result = alloc_label(lmanager->current_label_ID);

   /* update the value of `current_label_ID' */
   lmanager->current_label_ID++;
   
   /* tests if an out of memory occurred */
   if (result == NULL)
      return NULL;

   /* add the new label to the list of labels */
   lmanager->labels = addElement(lmanager->labels, result, -1);

   /* return the new label */
   return result;
}

/* assign the given label identifier to the next instruction. Returns
 * NULL if an error occurred; otherwise the assigned label */
t_axe_label * fixLabelID(t_axe_label_manager *lmanager, t_axe_label *label)
{
   /* precondition: lmanager must be different from NULL */
   assert(lmanager != NULL);

   /* precondition: label must be different from NULL and
    * must always carry a valid identifier */
   if (  (label == NULL)
         || (label->labelID == LABEL_UNSPECIFIED)
         || (label->labelID >= lmanager->current_label_ID))
   {
      notifyError(AXE_INVALID_LABEL);
   }

   /* test if the next instruction has already a label */
   if (  (lmanager->label_to_assign != NULL)
         && ((lmanager->label_to_assign)->labelID != LABEL_UNSPECIFIED) )
   {
      label->labelID = (lmanager->label_to_assign)->labelID;
   }
   else
      lmanager->label_to_assign = label;

   /* all went good */
   return label;
}

/* initialize the memory structures for the label manager */
t_axe_label_manager * initialize_label_manager()
{
   t_axe_label_manager *result;

   /* create an instance of `t_axe_label_manager' */
   result = (t_axe_label_manager *)
         _AXE_ALLOC_FUNCTION (sizeof(t_axe_label_manager));

   if (result == NULL)
      notifyError(AXE_OUT_OF_MEMORY);

   /* initialize the new instance */
   result->labels = NULL;
   result->current_label_ID = 0;
   result->label_to_assign = NULL;

   return result;
}

/* finalize an instance of `t_axe_label_manager' */
void finalize_label_manager(t_axe_label_manager *lmanager)
{
   t_list *current_element;
   t_axe_label *current_label;
   
   /* preconditions */
   if (lmanager == NULL)
      return;

   /* initialize `current_element' to the head of the list
    * of labels */
   current_element = lmanager->labels;

   while (current_element != NULL)
   {
      /* retrieve the current label */
      current_label = (t_axe_label *) LDATA(current_element);
      assert(current_label != NULL);

      /* free the memory associated with the current label */
      _AXE_FREE_FUNCTION(current_label);

      /* fetch the next label */
      current_element = LNEXT(current_element);
   }

   /* free the memory associated to the list of labels */
   freeList(lmanager->labels);

   _AXE_FREE_FUNCTION(lmanager);
}

t_axe_label * assign_label(t_axe_label_manager *lmanager)
{
   t_axe_label *result;
   
   /* precondition: lmanager must be different from NULL */
   if (lmanager == NULL)
      notifyError(AXE_INVALID_LABEL_MANAGER);

   /* the label that must be returned (can be a NULL pointer) */
   result = lmanager->label_to_assign;

   /* update the value of `lmanager->label_to_assign' */
   lmanager->label_to_assign = NULL;

   /* return the label */
   return result;
}

int get_number_of_labels(t_axe_label_manager *lmanager)
{
   if (lmanager == NULL)
      return 0;

   if (lmanager->labels == NULL)
      return 0;

   /* postconditions */
   return getLength(lmanager->labels);
}
