#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include "../xmlparse.h"
#include "shared_util.h"
#include "shared_survey_string.h"
#include "shared_record_parser.h"
#include "shared_lock.h"
#include "shared_cs_util.h"

char big_buffer[500];

static RESULT *
find_same_id(RECORD *list, int id)
{
   RESULT  *ptr;
   ptr = list->head;
   while(ptr)
   {
       if(ptr->id == id)
		return ptr;
        ptr = ptr->next;
   }
   return 0;
}




static VALUE_COUNT * 
find_same_value(RESULT *list, const char *value)
{
    VALUE_COUNT *ptr;
    ptr = list->value_head;
    while(ptr)
    {
        if(strcmp(ptr->value, value)== 0)
	    return ptr;
	ptr = ptr->next;
    }
    return 0;
}
    


static void 
StartElement(void *userData, const char *name, const char **atts) 
{
    RECORD *list = userData;
    int i; 
    RESULT *ptr;
    VALUE_COUNT *val_ptr;

    if(strcmp(name, "result") == 0)
    {
	  i = 0;
         ptr = find_same_id(list, atoi(atts[i+1]));
	 if(!ptr)
	 {
	    if(!list->head)
	    {
		list->head = (RESULT*)malloc(sizeof(RESULT));
		list->current = list->head;
	    }
	    else
	    {
		list->current->next= (RESULT*)malloc(sizeof(RESULT));
		list->current = list->current->next;
	    }
            list->current->next = 0;
	    list->current->value_head = (VALUE_COUNT*)malloc(sizeof(VALUE_COUNT));
	    list->current->value_current = list->current->value_head;
	    list->current->value_current->next = 0;
	    list->current->id = atoi(atts[i+1]);
	    list->current->count = 1;
	    i = i+2;
	    list->current->value_current->value = malloc_str(atts[i+1]);
	    list->current->value_current->count = 1;
	    
	 }
	 else
	 {
	   ptr->count++;
	   i = i +2;
	   val_ptr = find_same_value(ptr, atts[i+1]);
	   if(!val_ptr)
	   {
	      ptr->value_current->next = (VALUE_COUNT*)malloc(sizeof(VALUE_COUNT));
	      ptr->value_current = ptr->value_current->next;
	      ptr->value_current->next = 0;
	      ptr->value_current->value = malloc_str(atts[i+1]);
	      ptr->value_current->count = 1;
	   }
	   else
	   {
	     val_ptr->count++;
	   }
	 }
    }
   memset(big_buffer, 0, strlen(big_buffer));
}


static void
EndElement(void *userData, const char *name)
{
    RECORD *list = userData;
  
  if(strcmp(name, "expect_takers") ==0)
     list->expect_takers = atoi(big_buffer);   
   memset(big_buffer, 0, strlen(big_buffer));
}




static void startCharacter(void *userData, const char *text, int len)
{
   strncat(big_buffer, text, len);
}




void
free_record_list(RECORD *list)
{
    RESULT  *ptr;
    VALUE_COUNT  *value_ptr;

    list->current = list->head;

    while(list->current)
    {
       
        ptr = list->current;
	
	if(ptr->value_head)
	{
	   ptr->value_current = ptr->value_head;
	   while(ptr->value_current)
	   {
	       value_ptr = ptr->value_current;
	       if(value_ptr->value)
		       free(value_ptr->value);
	       ptr->value_current = ptr->value_current->next;
	       free(value_ptr);
	   }
	}
	list->current = list->current->next;
	free(ptr);
    }
   if(list)
      free(list);
}
    

/* Moves the node pointed to by target to the head of the list */    
    
static
VALUE_COUNT *move_to_head(VALUE_COUNT *head, VALUE_COUNT *target)
{
 VALUE_COUNT *prev;
 
 if( (!target || !head) || (target == head))   /* none of these should ever happen */
     return head;

 /* advance 'prev' pointer to the node immediately before the target */     
 for(prev=head; prev->next && (prev->next != target); prev = prev->next);

 if(!prev->next)  /* couldn't find target - shouldn't happen */
    return head;      
    
 prev ->next = target->next;
 target->next = head;
 return target;
}     
     
     
     

/* This function randomizes the order of the responses, to better
** preserve the anonymity of the results.  
** 
** If there is only one response, do nothing, since there is nothing
** to randomize.
**
** If there are two responses, 'flip a coin' to decide to either 
** leave them as-is, or to swap the two responses in the list.
**
** For three or more nodes, do the following 'many times':
**   move a randomly selected item from the list to the top of the list.
*/
    

static
VALUE_COUNT *shuffle_value_list(VALUE_COUNT *head)
{
#define SHUFFLE_MULTIPLIER 3   /* used to determine how many times to move a randomly selected item to the top of the list */
 int node_count, random_num, i, j;
 VALUE_COUNT *ptr;
 VALUE_COUNT *random_head;
  
 /* how many items are there? */ 
 for(node_count=0, ptr = head; ptr; ptr=ptr->next, node_count++);
 
 if(node_count <= 1)   /* 0 or 1 nodes, nothing to sort */
    return head;
 
 random_head = head;   
 srand ((int) time (NULL));    /* seed random number generator */
 
 /* If there are only two nodes, 'flip a coin to decide whether or not to swap the nodes. */
 if(node_count == 2)
  {
    random_num = 1  + (int) (2.0 * (rand() / (RAND_MAX + 1.0)));  /* generates a random number between 1 and 2 */
    if(random_num == 1)
      random_head = move_to_head(random_head, head->next);   /* swaps the two nodes */
   }   
 else/* there are three or more nodes - move a randomly selected item to the top of the list 'many times' */
  {
    for(i = 1; i <= (node_count * SHUFFLE_MULTIPLIER); i++)  /* do the shuffle 'many times' - SHUFFLE_MODIFIER #defined in this function is a guess for a good number */
     {
      random_num = 1  + (int) ((float)(node_count - 1) * (rand() / (RAND_MAX + 1.0)));  /* random # between 1 and number of nodes minus one */
      for(ptr = random_head, j = 0; (j < random_num) && ptr && ptr->next; j++, ptr=ptr->next);  /* move ptr to a randomly selected node - note ptr will never point to the list head */
      random_head = move_to_head(random_head, ptr);   /* move the randomly selected node to the top of the list */
     }
  }
  
 return random_head;
 
 #undef SHUFFLE_MULTIPLIER
 
}      
          

static
RESULT *shuffle_values(RESULT *result_head)
{
  RESULT *ptr;
  
  for(ptr = result_head; ptr; ptr = ptr->next)
     ptr->value_head = shuffle_value_list(ptr->value_head);
     
  return result_head;   
}


     
	
RECORD *
record_parser(char *filename)
{
    char *buf, *error_buf;
    RECORD *list;  /* shared_record_parser.h */
    FILE *fp;
    struct stat file_stat;
    XML_Parser parser;
    
    /*initialize list */
    list = (RECORD *)malloc(sizeof(RECORD));
    list->head = 0;
    list->current = 0;
    list->expect_takers =0; 

    if(stat(filename, &file_stat))
       cs_critical_error( ERR_FOPEN_READ_FAILED, filename);
    buf = (char*)malloc(file_stat.st_size);
    if(!buf)
	cs_critical_error(ERR_MALLOC_FAILED, "");
    
    fp = fopen(filename, "r");
    if(!fp)
	cs_critical_error( ERR_FOPEN_READ_FAILED, filename);
    get_shared_lock(fileno(fp), 1);
    if(fread(buf, file_stat.st_size, 1, fp) != 1)
	cs_critical_error( ERR_FREAD_FAILED, filename);
    release_lock(fileno(fp));
    fclose(fp);
    parser = XML_ParserCreate(NULL); 
    XML_SetUserData(parser, list);
    XML_SetElementHandler(parser, StartElement, EndElement);
     XML_SetCharacterDataHandler(parser, startCharacter);
    if(!XML_Parse(parser, buf, file_stat.st_size, 1))
    {
     error_buf = malloc_str(XML_ErrorString(XML_GetErrorCode(parser)));
     
     cs_critical_error(ERR_XML_PARSER, error_buf);
     free(error_buf);
     }
    XML_ParserFree(parser);
    free(buf);
    
    list->head = shuffle_values(list->head);
    
    return list;
    
}
     

