#include <stdio.h>

#include <string.h>
#include <dirent.h>		/* Directory information.       */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>


#include "global.h"

#include <ClearSilver.h>

#define MAX_HDF_NAME 256

typedef struct hdf_node
   {
    char name[MAX_HDF_NAME + 1];
    char *value;
    int handled;
    struct hdf_node *next;
   } HDF_NODE;
   
HDF_NODE *working_hdf_head = (HDF_NODE *) 0;


typedef struct f_node
  {
   char filename[MAX_FILENAME + 1];
   long filesize;
   int handled;
   struct f_node *next;
  } F_NODE;

F_NODE *old_head = (F_NODE *) 0;
F_NODE *new_head = (F_NODE *) 0;


static long
file_size(const char *file_path)
{
  struct stat file_stat;

  if(stat(file_path, &file_stat))
          return 0;
  else
          return file_stat.st_size;   
}

          



static
F_NODE *build_file_list(const char *dirname)
{

  DIR *dir_p;
  struct dirent *dir_entry_p;
  char fname[MAX_PATH + 1];

  F_NODE *head = (F_NODE *) 0;
  F_NODE *a_node;
  F_NODE *last = head;
  
  dir_p = opendir (dirname);
  if (!dir_p)
    {
      puts(dirname);
      perror("Unable to open directory for reading.");
      exit(1);
    }
    
    
  /*process all files that end in '.lang' */
  while (NULL != (dir_entry_p = readdir (dir_p)))
    {
      if (strcmp (dir_entry_p->d_name, ".")
	  && strcmp (dir_entry_p->d_name, "..")
	  && strstr(dir_entry_p->d_name, ".lang")                         
	  && (*(strstr(dir_entry_p->d_name, ".lang") + 5) == '\0') )
	{
	  snprintf (fname, MAX_PATH + 1, "%s/%s", dirname,
		    dir_entry_p->d_name);

           
           a_node = (F_NODE *)malloc(sizeof(F_NODE));
           if(!a_node)
            {
             perror("malloc() failed. Out of memory?");
             exit(1);
            }
            strncpy(a_node ->filename, dir_entry_p->d_name, MAX_FILENAME);
            a_node->filesize = file_size(fname);
            a_node->handled = 0;
            a_node->next = (F_NODE *) 0;
            
            if(!head)
             {
               head = a_node;
               last = head;
             }
            else
              {
                last->next = a_node;
                last = last->next;
              }  
	}
    }

  closedir (dir_p);
  return head;
}



/******************
static 
void dump_list(F_NODE *head, const char *path)
{
 F_NODE *ptr;
 
 printf("\nDumping: %s\n\n", path);
 for(ptr = head; ptr; ptr = ptr->next)
   printf("%s:%ld\n", ptr->filename, ptr->filesize);
   
}

*************/


static 
int node_count (F_NODE *head)
{
 F_NODE *ptr;
 int count;
 
 for(count = 0, ptr=head; ptr; ptr=ptr->next, count++);
 
 return count;
 
}




static int
fname_exists(const char *fname, F_NODE *head)
{
 int found = 0;
 F_NODE *ptr;
 
 for(ptr=head; ptr && !found; ptr=ptr->next)
     found = !strcmp(fname, ptr->filename);
     
 return found? 1 : 0;
}

    




static void
print_new(const char *old_path, const char *new_path)
{
 int count = 0;
 F_NODE *ptr;
 
 for(ptr = new_head; ptr; ptr = ptr->next)
   if(!fname_exists(ptr->filename, old_head))
      {
       if(!count)
          {
           puts("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
           printf("The following lang file(s) are new.\nThey appear in the NEW PATH %s,\nbut not in the OLD PATH %s\n", new_path, old_path);
           puts("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");           
          } 
       count++;
       puts(ptr->filename);
       ptr->handled = 1;
      }
 if(count)
   printf("\nFound %d new lang files.\n\n", count);
     
}






static void
print_obsolete(const char *old_path, const char *new_path)
{
 int count = 0;
 F_NODE *ptr;
 
 for(ptr = old_head; ptr; ptr = ptr->next)
   if(!fname_exists(ptr->filename, new_head))
      {
       if(!count)
          {
           puts("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
           printf("The following lang file(s) are NO LONGER USED.\nThey appear in the OLD PATH %s,\nbut not in the NEW PATH %s\n", old_path, new_path);
           puts("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");           
          } 
       count++;
       puts(ptr->filename);
       ptr->handled = 1;
      }
 if(count)
   printf("\nFound %d OLD lang files that are no longer used.\n\n", count);
     
}




void 
add_node(const char *name_parta, const char *name_partb, char *value)
{
 HDF_NODE *ptr;
 
 if(!working_hdf_head)
  {
    working_hdf_head = (HDF_NODE *) malloc(sizeof(HDF_NODE));
    ptr = working_hdf_head;
  }
 else
  {
    for(ptr = working_hdf_head; ptr->next; ptr=ptr->next);
    ptr -> next = (HDF_NODE *) malloc(sizeof(HDF_NODE));
    ptr = ptr->next;
  } 
 
 snprintf(ptr->name, MAX_HDF_NAME + 1, "%s%s%s", name_parta, *name_partb? "." : "", *name_partb? name_partb : "");
 ptr->value = value;
 ptr->handled = 0;
 ptr->next = (HDF_NODE *) 0; 

}





void add_tree_to_list(HDF *node, char *the_name)
{
  
 char  local_name[MAX_HDF_NAME + 1];
 char *ptr;
 
 strcpy(local_name, the_name);
 while(node)
 {

  if(node->value)
   {
    add_node(local_name, node->name? node->name: "",node->value);
   }
  else
  {
     if(node->child)
     {
      if(*local_name)
        strncat(local_name, ".", MAX_HDF_NAME + 1);
      
      if(node->name)    
      {
       strncat(local_name, node->name, MAX_HDF_NAME + 1);
      }

        add_tree_to_list(node->child, local_name);
     }
     
      {   
         ptr = strrchr(local_name, '.');
         if(ptr)
             *ptr = '\0';
      } 
  }      
   node = node->next;
 }      
  
        
}

    
  



static
HDF_NODE *
build_hdf_list(HDF *the_hdf)
{
 HDF  *ptr;
 
 working_hdf_head = (HDF_NODE *)0;
 ptr = the_hdf;
 if(ptr != NULL)
    ptr = ptr->child;   /* to skip past the root node? */

 add_tree_to_list(ptr, ""); 
 
 return (HDF_NODE *) working_hdf_head;
 
}








static void
free_hdf_list(HDF_NODE *head)
{
 HDF_NODE *ptr;
 while(head)
  {
   ptr = head;
   head = head->next;
   free(ptr);
  }
}



/*****************
static void
print_hdf_list(const char *filename, HDF_NODE *hdf_head)
{
HDF_NODE *ptr;

 printf("\n\n%s\n\n", filename);
 
 for(ptr=hdf_head; ptr; ptr=ptr->next)
    printf("%s = %s\n", ptr->name, ptr->value);
 
}
**********************/





static void
print_obsolete_items(HDF_NODE *old_hdf_head, HDF_NODE *new_hdf_head)
{

HDF_NODE *new_ptr;
HDF_NODE *old_ptr;
int found; 
int count = 0;

printf("********** OBSOLETE ITEMS (found in older version, but not in newer version): ");

for(old_ptr = old_hdf_head; old_ptr; old_ptr=old_ptr->next)
 {
    for(new_ptr = new_hdf_head, found = 0; !found && new_ptr; new_ptr=new_ptr->next)
      found = !strcmp(old_ptr->name, new_ptr->name);
      
    if(!found)
     {
      count++;
      printf("\n%s = %s\n", old_ptr -> name, old_ptr -> value);
     }

 }

if(!count)
    puts("NONE");
         
}     




static void
print_new_items(HDF_NODE *old_hdf_head, HDF_NODE *new_hdf_head)
{

HDF_NODE *new_ptr;
HDF_NODE *old_ptr;
int found; 
int count = 0;

printf("********** NEW ITEMS (found in newer version, but not in older version): ");

for(new_ptr = new_hdf_head; new_ptr; new_ptr=new_ptr->next)
 {
  if(!new_ptr->handled)
   {
    for(old_ptr = old_hdf_head, found = 0; !found && old_ptr; old_ptr=old_ptr->next)
      found = !strcmp(old_ptr->name, new_ptr->name);
      
    if(!found)
     {
      count++;
      printf("\n%s = %s\n", new_ptr -> name, new_ptr -> value);
      new_ptr -> handled = 1;
     }

   }
 }

if(!count)
    puts("NONE");
         
}     




static void
print_changed_items(HDF_NODE *old_hdf_head, HDF_NODE *new_hdf_head)
{

HDF_NODE *new_ptr;
HDF_NODE *old_ptr;
int found; 
int count = 0;

printf("********** CHANGED ITEMS (found in both versions, but new version is different from old version): ");

for(new_ptr = new_hdf_head; new_ptr; new_ptr=new_ptr->next)
 {
  if(!new_ptr->handled)
   {
    for(old_ptr = old_hdf_head, found = 0; !found && old_ptr; old_ptr=old_ptr->next)
     {
      found = !strcmp(old_ptr->name, new_ptr->name);
      if(found)
         break;
     }
    
    if(found && old_ptr && strcmp(new_ptr->value, old_ptr->value))
     {
      count++;
      puts("\n-------------------------");
      printf("OLD: %s = %s\n", old_ptr -> name, old_ptr -> value);
      printf("NEW: %s = %s\n", new_ptr -> name, new_ptr -> value);
      puts("-------------------------");      
      new_ptr -> handled = 1;
     }

   }
 }

if(!count)
    puts("NONE");
         
}     











void
print_comparisons(const char *old_path, const char *new_path)
{
 F_NODE *ptr;
 char new_fname[MAX_PATH + 1];
 char old_fname[MAX_PATH + 1];
 HDF_NODE *old_hdf_head = (HDF_NODE *) 0;
 HDF_NODE *new_hdf_head = (HDF_NODE *) 0;

 HDF *new_hdf;
 HDF *old_hdf;

 
 for(ptr = new_head; ptr; ptr = ptr->next)
  {
   if(!ptr->handled)
     {
       snprintf(new_fname, MAX_PATH + 1, "%s/%s", new_path, ptr->filename);
       snprintf(old_fname, MAX_PATH + 1, "%s/%s", old_path, ptr->filename);
       
       hdf_init(&new_hdf);
       hdf_init(&old_hdf);
       
       old_hdf_head = (HDF_NODE *) 0;
       new_hdf_head = (HDF_NODE *) 0;
       
       /* should check for error codes here */
       hdf_read_file(new_hdf, new_fname);
       hdf_read_file(old_hdf, old_fname);

       old_hdf_head = build_hdf_list(old_hdf);
       new_hdf_head = build_hdf_list(new_hdf);

//       print_hdf_list(old_fname, old_hdf_head);
//       print_hdf_list(new_fname, new_hdf_head);

       puts("\n------------------------------------------------------------------------------------");
       printf("Language File: %s\n", ptr->filename);
       print_new_items(old_hdf_head, new_hdf_head);
       print_obsolete_items(old_hdf_head, new_hdf_head);
       print_changed_items(old_hdf_head, new_hdf_head);
       
       hdf_destroy(&new_hdf);
       hdf_destroy(&old_hdf);

       free_hdf_list(old_hdf_head);
       free_hdf_list(new_hdf_head);       
     }
 }

    

     
}







static void
print_intro(const char *old_path,const char *new_path)
{
 puts("\nComparing 'Old' and 'New' Manhattan 'lang' files.");
 printf("'Old' lang directory: %s (found %d .lang files)\n", old_path, node_count(old_head));
 printf("'New' lang directory: %s (found %d .lang files)\n", new_path, node_count(new_head));
}

 
  




    
static void usage(char *program)
{
 puts("\n\nThis program lists differences in the  'lang' files between two releases of Manhattan");
 puts("It's useful for people who maintain translations.\n");
 printf("Usage: %s OLD_LANG_DIR NEW_LANG_DIR\n", program);
 puts("Where:");
 puts("\tOLD_LANG_DIR is the FULL PATH to the 'lang' directory of the older release of Manhattan");
 puts("\tNEW_LANG_DIR is the FULL PATH to the 'lang' directory of the newer release of Manhattan\n\n");
 exit(1);
}


static void free_list(F_NODE *head)
{
 F_NODE *ptr;
 
 while(head)
  {
   ptr = head;
   head = head->next;
   free(ptr);
  }

}

  


int
main ( int argc, char **argv)
{

  if(argc != 3)
     usage(argv[0]);
 
  old_head = build_file_list(argv[1]);
  new_head = build_file_list(argv[2]);

  print_intro(argv[1], argv[2]);

  print_new(argv[1], argv[2]);
  print_obsolete(argv[1], argv[2]);  
  
  print_comparisons(argv[1], argv[2]);
  
  free_list(old_head);
  free_list(new_head);


//  dump_list(old_head, argv[1]);
//  dump_list(new_head, argv[2]);  

  
  return 0;

}


