#include <stdlib.h>
#include <string.h>
#include "labellist.h"
#include "bintree.h"

LabelList *initLabelList(int initialLength) {
   LabelList *ll;

   if(initialLength < 0) return NULL;
   if((ll = (LabelList *)malloc(sizeof(LabelList))) == NULL) {
      return NULL;
   }

   if(initialLength == 0) {
      initialLength = DEFAULTLEN;
   }

   ll->list = (char **)malloc(sizeof(char *)*initialLength);
   if(ll->list == NULL) {
      free(ll);
      return NULL;
   }

   ll->listLen = initialLength;
   ll->curLen = 0;

   if((ll->bt = initBinTree(icomp, fcomp, free)) == NULL) {
	   free(ll->list);
	   free(ll);
	   return NULL;
   }

   return ll;
}

LabelList *copyLabelList(const LabelList *from) {
   unsigned int i, len;
   LabelList *ll;
   if(from == NULL) return NULL;

   len = ll_getLength(from);

   if((ll = initLabelList(len)) == NULL) return NULL;

   for(i = 0; i < len; i++) {
      if(ll_setLabel(ll, i, ll_getLabel(from, i)) < 0) {
         freeLabelList(ll);
         return NULL;
      }
   }

   return ll;

}

void freeLabelList(LabelList *ll) {
   unsigned int idx;

   if(ll == NULL) return;

   for(idx = 0; idx < ll->curLen; idx++) {
      free(ll->list[idx]);
   }
   freeBinTree(ll->bt);
   free(ll->list);
   free(ll);
}

int ll_setLabel(LabelList *ll, int idx, const char *label) {
   int ret;
   Node *newLabel;

   if(ll == NULL) return -1;
   if(idx < 0) {
      return -1;
   }

   if(idx < (long)ll->curLen) {
      Node oldLabel;
      
      oldLabel.label = ll->list[idx];
      oldLabel.idx = idx;

      bt_deleteVal(ll->bt, &oldLabel);
      free(ll->list[idx]);
      if(label == NULL) {
         ll->list[idx] = NULL;
         while(ll->list[ll->curLen-1] == NULL) ll->curLen--;
         return 0;
      }
   } else {
      if(label == NULL) return 0;
      while(idx >= (long)ll->listLen) {
         if((ret = resizeList(ll, ll->listLen)) < 0) {
            return ret;
         }
      }
   }

   if((ll->list[idx] = strdup(label)) == NULL) {
      return -3;
   }
   if(idx >= (long)ll->curLen) {
      ll->curLen = idx+1;
   }

   newLabel = (Node *)malloc(sizeof(Node));
   newLabel->label = ll->list[idx];
   newLabel->idx = idx;
   bt_insertVal(ll->bt, newLabel);

   return 0;
}

int ll_appendLabel(LabelList *ll, const char *label) {
   if(ll == NULL || label == NULL) return -1;
   return ll_setLabel(ll, ll->curLen, label);
}

int ll_compactList(LabelList *ll) {
   int ret;

   if(ll == NULL) return -1;

   if((ret = resizeList(ll, ll->curLen - ll->listLen)) < 0) {
      return ret;
   }

   return 0;
}

unsigned int ll_getIndex(const LabelList *ll, const char *label, int *err) {
   Node *res;
   Node srch;

   if(err != NULL) {
      *err = 0;
   }

   if(ll == NULL) {
      if(err != NULL) {
         *err = -1;
      }
      return 0;
   }

   srch.label = label;
   srch.idx = 0;

   if((res = (Node *)bt_findVal(ll->bt, &srch)) == NULL) {
      if(err != NULL) {
         *err = -1;
      }
	   return 0;
   }

   return res->idx;
}

const char *ll_getLabel(const LabelList *ll, unsigned int idx) {
   if(ll == NULL) return NULL;
   if(idx >= ll->curLen) {
      return NULL;
   }
   return ll->list[idx];
}

unsigned int ll_getLength(const LabelList *ll) {
   if(ll == NULL) return 0;
   return ll->curLen;
}

const char **ll_getLabels(const LabelList *ll) {
   if(ll == NULL) return NULL;
   return (const char **)ll->list;
}

/********** private functions *********/

static int resizeList(LabelList *ll, int by) {
   char **tmp;

   if(by == 0) return 0;
   if((by > 0 && ll->listLen >= ll->listLen+by) ||
     (ll->listLen+by <= 0)) {
      return -2;
   }
   ll->listLen+= by;
   tmp = (char **)realloc(ll->list, ll->listLen*sizeof(char *));
   if(tmp == NULL) {
      ll->listLen-= by;
      return -3;
   }
   ll->list = tmp;

   return 0;
}

static int icomp(const void *a, const void *b) {
   const Node *na = (const Node *)a, *nb = (const Node *)b;
   int ret;

   if((ret = strcmp(na->label, nb->label)) == 0) {
      ret = na->idx - nb->idx;
   }
   return ret;
}

static int fcomp(const void *a, const void *b){
   const Node *na = (const Node *)a, *nb = (const Node *)b;

   return strcmp(na->label, nb->label);
}
