#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);
}