#include"heap.h"
#include<stdlib.h>
#include<string.h>
#include<stddef.h>
#define LIBRARYIMPLEMENTATION

typedef struct{
  heap *lib_on_which_heap;
  struct libnode *root;
  long number_of_entries;
}library;

struct libnode{
  struct libnode *left;
  struct libnode *right;
  long how_often_occurred;
  unsigned char *string;
};

#include "library.h"

static void fill_nodelist( struct libnode *node, struct libnode ***nodelist );
static int compare( struct libnode **first, struct libnode **second );
static void fill_tree_hist(struct libnode *node,long tree_hist[],long weight_hist[],long histlenght,long *deepest_branch,int depth);
static struct libnode *reorder(struct libnode *node, long *depth,long level);


library *library_create( long blocksize )
{
  library *lib;
  heap *lib_heap;
  if( NULL == ( lib_heap = heap_create( blocksize ))) return(NULL);
  if( NULL == ( lib = heap_allocate( lib_heap, sizeof( library )))) return(NULL);
  lib->lib_on_which_heap = lib_heap;
  lib->root = NULL;
  lib->number_of_entries = 0;
  return(lib);
}										/* library_create */

void library_delete( library *lib )
{
  heap_delete( lib->lib_on_which_heap );
}


int library_read( library *lib, FILE *libfile ) /* reads library */
{
  long occurrences;
  unsigned char string[256];
  
  while(!feof(libfile))
    {
      if (!fscanf(libfile,"%s\t%ld\n", string, &occurrences)) break;
      if ( occurrences <= 0 ) break;
      if (!library_enter_entry( lib, string, occurrences ))
	return(0);	/* eintragen hat nicht geklappt */
      
    }					/* while !feof() */
  return(1);
}


int library_write( library *lib, FILE *libfile )
{
  long i;
  struct libnode **localptr;
  struct libnode **nodelist;
  if ( NULL == (nodelist = (struct libnode **)malloc( sizeof(struct libnode *)*lib->number_of_entries))) return(0);
  localptr = nodelist;
  fill_nodelist( lib->root, &localptr );
  qsort( nodelist, lib->number_of_entries, sizeof( struct libnode *), (int  (*)(const void *, const void *)) compare );
/* the qsort is buggy in some libraries, to have a look at the result!! */
  for(i = 0; i < lib->number_of_entries; i++ )
    fprintf( libfile, "%s\t%ld\n", nodelist[i]->string, nodelist[i]->how_often_occurred);
  fprintf( libfile, "end-of-file\t-1\n");
  free(nodelist);
  return(1);
}											/* library_write */

static void fill_nodelist( struct libnode *node, struct libnode ***nodelist )
{
  if( node )
    {
      *((*nodelist)++) = node;
      fill_nodelist( node->left, nodelist );
      fill_nodelist( node->right, nodelist );		
    }	
}     							/* fill_nodelist */

static int compare( struct libnode **first, struct libnode **second )
{
  long local;
  
  local =(*first)->how_often_occurred - (*second)->how_often_occurred;
  if (local < 0 ) return( 1 );
  if (local > 0 ) return( -1 );
  return( 0 );
} 


int library_enter_entry( library *lib, const unsigned char *string, long occurrences_so_far )
{
  int comp = 1;	
  struct libnode *node;
  struct libnode **dest;
  
  if( lib->root == NULL ) /* leere lib */
    dest = &(lib->root);
  else
    {
      node = lib->root;
      while( 0 != ( comp = strcmp(node->string,string)))
	{
	  if ( comp > 0 )
	    {
	      if ( node->left )
		{
		  node = node->left;
		  continue;
		}
	      else
		{
		  dest = &(node->left);
		  break;
		}
	    }
	  else if( comp < 0 ) 
	    {
	      if ( node->right )
		{
		  node = node->right;
		  continue;
		}
	      else
		{
		  dest = &(node->right);
		  break;
		}
	    }
	}
    }
  if ( comp == 0 ) 
    {
      node->how_often_occurred += occurrences_so_far;
      return( 1 );		      
    }
  
  *dest = heap_allocate( lib->lib_on_which_heap, sizeof(struct libnode)+ strlen(string)+1 );
  if(!*dest ) return( 0 );
  (*dest)->left = NULL;
  (*dest)->right = NULL;
  (*dest)->string = (unsigned char *)( *dest + 1 ); /* da soll der string hin (hinter libnode) */
  (*dest)->how_often_occurred = occurrences_so_far;
  strcpy((*dest)->string, string );
  lib->number_of_entries++;
  return( 1 );
}								/* library_enter_entry */

int library_find_entry( library *lib, const unsigned char *string )
{
  struct libnode *node;
  int comp;
  
  if ( lib->root == NULL ) return( 0 );
  node = lib->root;
  while( 0 != ( comp = strcmp(node->string,string)))
    {
      if (( comp > 0 )&&(node->left))
	{
	  node = node->left;
	  continue;
	}
      else if(( comp < 0 )&&( node->right )) 
	{
	  node = node->right;
	  continue;
	}
      return( 0 );
    }
  return( 1 );
}			/* find_entry liefert pointer auf libnode */


void library_fill_hist(library *lib,long tree_hist[],long weight_hist[],long histlenght,long *deepest_branch)
{
  
  fill_tree_hist( lib->root,tree_hist,weight_hist,histlenght,deepest_branch,0);  
}

static void fill_tree_hist(struct libnode *node,long tree_hist[],long weight_hist[],long histlenght,long *deepest_branch,int depth)
     
{
  if (node)
    {
      fill_tree_hist(node->left,tree_hist,weight_hist,histlenght,deepest_branch,depth+1);  
      if (depth < histlenght)
	{
	  tree_hist[depth]++;
	  weight_hist[depth] += node->how_often_occurred;
	}
      else
	tree_hist[histlenght]++;
      if (depth > *deepest_branch) *deepest_branch = depth;
      fill_tree_hist(node->right,tree_hist,weight_hist,histlenght,deepest_branch,depth+1);  
    }
}

int library_reorganize(library *lib)
{
  long depth = 0;
  
  lib->root = reorder(lib->root,&depth,0);
return((int)lib->root);
}

static struct libnode *reorder(struct libnode *node, long *depth, long level)
{
  if (node)
    {
      long left_depth;
      long right_depth;
      long newdepth;
      struct libnode *help;

      help = node;
      newdepth = *depth;
      node->left = reorder(node->left, depth, level + 1);
      left_depth = *depth;
      *depth = newdepth;
      node->right = reorder(node->right, depth, level + 1);
      right_depth = *depth;
      *depth = right_depth > left_depth ? right_depth : left_depth;

      if (right_depth > left_depth)
	{   /* rechts rotieren */
	  if (node->right)
	    {
	      if (node->right->how_often_occurred == node->how_often_occurred)
		{
		  help = node->right;
		  node->right = help->left;
		  help->left = node;
		};
	    };
	}   /* links rotieren */
      else if (left_depth > right_depth)
	{
	  if (node->left)
	    {
	      if (node->left->how_often_occurred == node->how_often_occurred)
		{
		  help = node->left;
		  node->left = help->right;
		  help->right = node;
		};
	    };
	};
      return(help);
    };
  if (level > *depth) *depth = level;
  return(node);
}
