#include "folderP.h"

/*****************************************************************************/
/*********  MACRO :: Set an Error Code for a Failed Folder Operation  ********/
/*****************************************************************************/

#define AlFolder_set_error_code(the_folder, error_number) \
         ((the_folder->error_code) = error_number)

/*****************************************************************************/
/***********   Get an Error Code for a Failed Folder Operation  **************/
/*****************************************************************************/

       /* This function is publicly accessible */

int AlFolder_get_error_code(the_folder)
     AlFolder the_folder;
 { assert(the_folder != NULL);  /* Folder exists */
   return(the_folder->error_code);
  };                  /* end of AlFolder_get_error_code()  */

/*****************************************************************************/
/********   Get an Error-Message for a Failed Folder Operation  **************/
/*****************************************************************************/

       /* This function is publicly accessible */

char *AlFolder_get_error_msg(the_folder)
     AlFolder the_folder;
 { assert(the_folder != NULL);  /* Folder exists */
   return(AlFolder_error_strings[the_folder->error_code]);
  };                   /* end of AlFolder_get_error_msg()  */

/*****************************************************************************/
/***************   Clear the Error Code in a Folder Object  ******************/
/*****************************************************************************/

       /* This function is publicly accessible */

NORET AlFolder_clear_error_code(the_folder)
     AlFolder the_folder;
 { 
   AlFolder_debug("Folder >> Entering AlFolder_clear_error_code()");
   assert(the_folder != NULL);  /* Folder exists */

   the_folder->error_code = 0;
   AlFolder_debug("Folder << Exiting AlFolder_clear_error_code()");

  };                   /* end of AlFolder_clear_error_msg()  */

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

NORET AlFolder_from_Msg_error_code(the_folder, msg_error_code)
     AlFolder the_folder;
     int msg_error_code;
{
  AlFolder_set_error_code(the_folder, msg_error_code);
};

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

Bool AlFolder_default_state_suppress()
 {
  if (AlProfile_has_value(ALPROF_SAVSTAT) == Bool_TRUE)
    return(Bool_not(AlProfile_get_BL_val(ALPROF_SAVSTAT)));
  else
    return(Bool_TRUE);
 };
/*****************************************************************************/

Bool AlFolder_default_uprop_suppress()
 {
  if (AlProfile_has_value(ALPROF_SAVPROP) == Bool_TRUE)
    return(Bool_not(AlProfile_get_BL_val(ALPROF_SAVPROP)));
  else
    return(Bool_FALSE);
 };

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

char *AlFolder_get_name(the_folder)
     AlFolder the_folder;
 {
   return(the_folder->name);
 };

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

NORET AlFolder_reset_name(the_folder, name)
     AlFolder the_folder;
     char *name;
 {
   assert(name != NULL);

   if (the_folder->name != NULL)
     Memory_free((VOIDP) the_folder->name);
   the_folder->name = (char *) Memory_allocate((long) strlen(name) + 1);
   strcpy(the_folder->name, name);
 };               /* end of AlFolder_reset_name()  */

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

AlFolder AlFolder_create(name, folder_type, turn_on_recursion,
			 suppress_external_state_usage,
			 suppress_external_uprop_usage)
     char *name;
     AlFolderType folder_type;
     Bool turn_on_recursion;
     Bool suppress_external_state_usage;
     Bool suppress_external_uprop_usage;
 {
   AlFolder the_folder;
   char *name_copy;
   AlFolder_MsgPackette msg_pack;

   the_folder = (AlFolder) Memory_allocate(sizeof(AlFolderObject));

   /* If folder came from stdin, then name == NULL, which is ok. */
   if (name != NULL)
     { name_copy = (char *) Memory_allocate((long) strlen(name) + 1);
       strcpy(name_copy, name);
       the_folder->name = name_copy;
     }
   else
     the_folder->name = NULL;

   /* setup a registry for folder/mailer specific info about msg    */
   the_folder->fs_state_registry = Registry_create(Registry_strcmp, 
						   Registry_strhash);
   /* setup a registry for folder/mailer specific info about folder */
   the_folder->folder_prop_registry = Registry_create(Registry_strcmp, 
						      Registry_strhash);
   the_folder->do_nested_folders = turn_on_recursion;
   the_folder->error_code = 0;
   the_folder->num_rules_fired = 0;
   the_folder->mailer_type_name = AlFolderT_name(folder_type);
   the_folder->mailer_type = folder_type;
   /* Create an empty current msg packette, to be filled by get_next_msg */
   msg_pack = (AlFolder_MsgPackette)
                             Memory_allocate(sizeof(AlFolder_MsgPackette_Ob));
   msg_pack->msg_buffer = NULL;
   msg_pack->uprop_buffer = NULL;
   msg_pack->state_buffer = NULL;
   msg_pack->no_external_state_usage = suppress_external_state_usage;
   msg_pack->no_external_uprop_usage =suppress_external_uprop_usage;
   msg_pack->the_msg = NULL;
   the_folder->current_msg_packette = msg_pack;
   return(the_folder);
 };                 /* end of AlFolder_create()  */

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

AlFolder AlFolder_create_from_file(folder_string, folder_type,
				   turn_on_recursion,
				   suppress_external_state_usage,
				   suppress_external_uprop_usage,
				   open_status)
     char *folder_string;         /* filename or directory name */
     AlFolderType folder_type;
     Bool turn_on_recursion;
     Bool suppress_external_state_usage;
     Bool suppress_external_uprop_usage;
     int open_status;
 {
   AlFolder the_folder;
   VOIDP data_ob;

   AlFolder_debug(" Entering >> AlFolder_create_from_file()");

   /* First, make sure that the folder can be opened ok */
   /* ------------------------------------------------- */
   if ((data_ob = (VOIDP) 
                   AlFolderT_eval_create_from_file(folder_type, 
						   folder_string,
						   open_status))
       == NULL)
     { AlFolder_debug(" Exiting << AlFolder_create_from_file() : failing");
       return(NULL);    /* Can't open the folder for some reason */
     };

   /* Looks safe, allocate and init all memory for the folder object */
   /* -------------------------------------------------------------- */
   the_folder = AlFolder_create(folder_string, folder_type, turn_on_recursion,
			 suppress_external_state_usage,
			 suppress_external_uprop_usage);
   the_folder->data = (VOIDP) data_ob;

   AlFolder_debug(" Exiting << AlFolder_create_from_file()");
   return(the_folder);
 };                 /* end of AlFolder_create_from_file()  */

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

AlFolder AlFolder_create_from_stdin(folder_type, turn_on_recursion,
				    suppress_external_state_usage,
				    suppress_external_uprop_usage)
     AlFolderType folder_type;
     Bool turn_on_recursion;
     Bool suppress_external_state_usage;
     Bool suppress_external_uprop_usage;
 {
   AlFolder the_folder;
   VOIDP data_ob;

   AlFolder_debug(" Entering >> AlFolder_create_from_stdin()");

   /* First, make sure that the folder can be opened ok */
   /* ------------------------------------------------- */
   if ((data_ob = (VOIDP) 
                       AlFolderT_eval_create_from_stdin(folder_type))
       == NULL)
     { AlFolder_debug(" Exiting << AlFolder_create_from_stdin() : failing");
       return(NULL);    /* Can't open the folder for some reason */
     };

   /* Looks safe, allocate and init all memory for the folder object */
   /* -------------------------------------------------------------- */
   the_folder = AlFolder_create(NULL, folder_type, turn_on_recursion,
			 suppress_external_state_usage,
			 suppress_external_uprop_usage);
   the_folder->data = data_ob;

   AlFolder_debug(" Exiting << AlFolder_create_from_stdin()");
   return(the_folder);
 };                 /* end of AlFolder_create_from_stdin()  */

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

Msg AlFolder_get_next_msg(the_folder)
     AlFolder the_folder;
 {
   AlFolder_MsgPackette msg_pack;

   AlFolder_debug(" Entering >> AlFolder_get_next_msg()");

   msg_pack = the_folder->current_msg_packette;

   AlFolder_debug("    In AlFolder_get_next_msg() : about to eval next msg");

   /* Invoke the folder type's function to get the next msg's buffers. */
   /* ---------------------------------------------------------------- */
   AlFolderT_eval_next_msg(the_folder->mailer_type,
			   the_folder->data,
			   &(msg_pack->msg_buffer),
			   &(msg_pack->state_buffer),
			   &(msg_pack->uprop_buffer),
			   the_folder->fs_state_registry,
			   the_folder->do_nested_folders,
			   msg_pack->no_external_state_usage,
			   msg_pack->no_external_uprop_usage,
                           AlFolder_get_current_msg(the_folder));

   AlFolder_debug("   In AlFolder_get_next_msg() : about to destroy prev msg");
   if (msg_pack != NULL)
     { if (msg_pack->the_msg != NULL)
	 AlMsg_destroy(msg_pack->the_msg);
     };

   /* If no valid msg is to be had, then return NULL to flag completion */
   /* ----------------------------------------------------------------- */
   if (msg_pack == NULL)
     { AlFolder_debug(" Exiting << AlFolder_get_next_msg() : failing");
       return(NULL);         /* No msgs or packette, not likely, but... */
     };
   if (msg_pack->msg_buffer == NULL)
     { AlFolder_debug(" Exiting << AlFolder_get_next_msg() : done");
       return(NULL);         /* No msgs left to process, all done */
     };

   AlFolder_debug("   In AlFolder_get_next_msg() : about to create new Msg");

   msg_pack->the_msg = AlMsg_create(msg_pack->msg_buffer,
				    msg_pack->state_buffer,
				    msg_pack->uprop_buffer,
				    the_folder->fs_state_registry,
				    the_folder);
   if (msg_pack->the_msg != NULL)
     { /* Set the flags for what should be output information on writes */
       AlMsg_set_suppress_states_out(msg_pack->the_msg, 
				     msg_pack->no_external_state_usage);
       AlMsg_set_suppress_uprops_out(msg_pack->the_msg, 
				     msg_pack->no_external_uprop_usage);
     };
   AlFolder_debug(" Exiting << AlFolder_get_next_msg()");
   return(msg_pack->the_msg);
 };       /* end of AlFolder_get_next_msg()  */

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

Bool AlFolder_remove_cur_msg(the_folder)
     AlFolder the_folder;
 {
   Bool flag;

   AlFolder_debug(" Entering >> AlFolder_remove_cur_msg()");

   /* First, check and insure that there is a msg to remove. */
   /* ------------------------------------------------------ */
   if (the_folder->current_msg_packette == NULL)
     return(Bool_FALSE);   /* haven't started reading folder yet */
   if (the_folder->current_msg_packette->the_msg == NULL)
     return(Bool_FALSE);   /* haven't read first msg yet */

   /* Remove the msg file from the folder. */
   /* ------------------------------------ */
   flag = AlFolderT_remove_msg(the_folder->mailer_type,
			the_folder->data,
			the_folder->current_msg_packette->the_msg);

   /* Mark Msg as deleted. */
   /* -------------------- */
   AlMsg_set_removed_flag(the_folder->current_msg_packette->the_msg,
			  Bool_TRUE);

   AlFolder_debug(" Exiting << AlFolder_remove_cur_msg()");
   return(flag);
 };                 /* end of AlFolder_remove_cur_msg()  */

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

Bool AlFolder_copy_cur_msg(from_folder, to_folder)
     AlFolder from_folder;
     AlFolder to_folder;
 {
   Bool flag;
   int num_times_moved;
   Msg the_msg;
   char *moved_from, *temp;

   AlFolder_debug(" Entering >> AlFolder_copy_cur_msg()");

   assert(to_folder->data != NULL);

   /* First, check and insure that there is a msg to remove. */
   /* ------------------------------------------------------ */
   if (from_folder->current_msg_packette == NULL)
     return(Bool_FALSE);   /* haven't started reading "from" folder yet */
   if (from_folder->current_msg_packette->the_msg == NULL)
     return(Bool_FALSE);   /* "from" folder hasn't read first msg yet */

   /* Store and alter some States of the msg */
   /* -------------------------------------- */
   the_msg = from_folder->current_msg_packette->the_msg;
   num_times_moved = AlMsg_get_how_many_times_moved(the_msg);
   AlMsg_set_how_many_times_moved(the_msg, (num_times_moved + 1));
   temp = AlMsg_get_moved_from_state(the_msg);
   if (temp != NULL)
     { moved_from = (char *) Memory_allocate(strlen(temp) + 1);
       strcpy(moved_from, temp);
     }
   else
     moved_from = NULL;


   /* Remove the msg file from the folder. */
   /* ------------------------------------ */
   flag = AlFolderT_add_msg(to_folder->mailer_type,
			    to_folder->data,
			    from_folder->current_msg_packette->the_msg);

   /* Restore and alter some States of the msg */
   /* ---------------------------------------- */
   AlMsg_set_how_many_times_moved(the_msg, num_times_moved);
   AlMsg_set_moved_from_state(the_msg, moved_from);
   if (moved_from != NULL)
     Memory_free((VOIDP) moved_from);

   AlFolder_debug(" Exiting << AlFolder_copy_cur_msg()");
   return(flag);
 };                 /* end of AlFolder_copy_cur_msg()  */

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

Bool AlFolder_move_cur_msg(from_folder, to_folder)
     AlFolder from_folder;
     AlFolder to_folder;
 {
   Bool add_flag, remove_flag;

   AlFolder_debug(" Entering >> AlFolder_copy_cur_msg()");

   assert(to_folder->data != NULL);

   /* First, check and insure that there is a msg to remove. */
   /* ------------------------------------------------------ */
   if (from_folder->current_msg_packette == NULL)
     return(Bool_FALSE);   /* haven't started reading "from" folder yet */
   if (from_folder->current_msg_packette->the_msg == NULL)
     return(Bool_FALSE);   /* "from" folder hasn't read first msg yet */

   /* Add current Msg to the to_folder, and remove it from the from_folder */
   /* -------------------------------------------------------------------- */
   if ((add_flag = AlFolder_copy_cur_msg(from_folder, to_folder))
       == Bool_TRUE)
     remove_flag = AlFolder_remove_cur_msg(from_folder);
   else
     remove_flag = Bool_FALSE;

   /* Flag any errors */
   /* --------------- */
   if (add_flag == Bool_FALSE)
     AlFolder_set_error_code(to_folder, CANT_ADD_FILE);
   else
     AlFolder_clear_error_code(to_folder);
   if (remove_flag == Bool_FALSE)
     AlFolder_set_error_code(from_folder, CANT_REMOVE_FILE);
   else
     AlFolder_clear_error_code(from_folder);

   return(Bool_cand(add_flag, remove_flag));

 };                /* end of AlFolder_move_cur_msg()  */

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

NORET AlFolder_print_self(the_folder, outfile)
     AlFolder the_folder;
     FILE *outfile;
  { /* Print the contents of the folder to the file specified. */
    /* ------------------------------------------------------- */

     fprintf(outfile, "Folder (#%d) contains:\n", (int) the_folder);
     fprintf(outfile, "   name = \"%s\"\n", the_folder->name);
     fprintf(outfile, "       Name Spaces\n");
     fprintf(outfile, "       -----------\n");
     fprintf(outfile, "   fs_state_registry = & %d\n",
             (int) the_folder->fs_state_registry);
     fprintf(outfile, "   folder_prop_registry = & %d\n",
             (int) the_folder->folder_prop_registry);
     fprintf(outfile, "       States\n");
     fprintf(outfile, "       ------\n");
     fprintf(outfile, "  do_nested_folders  = \"%s\"\n",
             AlFolder_bool_str(the_folder->do_nested_folders));
     fprintf(outfile, "   current_msg_packette = & %d\n",
             (int) the_folder->current_msg_packette);
     fprintf(outfile, "       Error Info\n");
     fprintf(outfile, "       ----------\n");
     fprintf(outfile, "   error code = %d\n", the_folder->error_code);
     fprintf(outfile, "       States that are purely for Data Collection\n");
     fprintf(outfile, "       ------------------------------------------\n");
     fprintf(outfile, "   # of rules fired = %d\n",
	     the_folder->num_rules_fired);
     fprintf(outfile, 
	     "       Mailer/Folder Type specific functions and info\n");
     fprintf(outfile,
	     "       ----------------------------------------------\n");
     fprintf(outfile, "   folder/mailer type = \"%s\"\n",
	     the_folder->mailer_type_name);
     fprintf(outfile, "   data object address = %d >> data object follows:\n",
             (int) the_folder->data);

     /* Lastly, invoke the folder type object's function to print out itself */
     AlFolderT_print_self(the_folder->mailer_type, the_folder->data, outfile);

  };              /* end of AlFolder_print_self()  */

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

NORET AlFolder_destroy(the_folder)
     AlFolder the_folder;
 {   
    int i, darray_size;
    Darray key_darray, value_darray;

  if (the_folder != NULL)
   {
    /* Destroy the folder object's spaces. (strings, buffers, & registries). */
    /* --------------------------------------------------------------------- */
    if (the_folder->name != NULL)
      Memory_free((VOIDP) the_folder->name);
    key_darray = Darray_create();
    value_darray = Darray_create();
    
    /* Destroy the Folder State Registry and its contents. */
    /* --------------------------------------------------- */
    if (the_folder->fs_state_registry != NULL)
      { Registry_fetch_contents(the_folder->fs_state_registry,
				key_darray, value_darray);
	Registry_destroy(the_folder->fs_state_registry);
	darray_size = Darray_len(value_darray);
	for (i = 0; i < darray_size; i++)
	  { /* free value string */
	    Memory_free((VOIDP) Darray_reml(value_darray));
	    /* free key string  */
	    Memory_free((VOIDP) Darray_reml(key_darray));
	  };
      };

    /* Destroy the Folder Property Registry and its contents. */
    /* ------------------------------------------------------ */
    if (the_folder->folder_prop_registry != NULL)
      { Registry_fetch_contents(the_folder->folder_prop_registry,
				key_darray, value_darray);
	Registry_destroy(the_folder->folder_prop_registry);
	darray_size = Darray_len(value_darray);
	for (i = 0; i < darray_size; i++)
	  { /* free value string */
	    Memory_free((VOIDP) Darray_reml(value_darray));
	    /* free key string  */
	    Memory_free((VOIDP) Darray_reml(key_darray));
	  };
      };

    /* Destroy the Darrays used while destroying the registries */
    /* -------------------------------------------------------- */
    Darray_destroy(key_darray);
    Darray_destroy(value_darray);

    /* Destroy the Msg Packette object. */
    /* -------------------------------- */
    if (the_folder->current_msg_packette != NULL)
      { Memory_free((VOIDP) the_folder->current_msg_packette);
	/* Other items in the msg packette are destroyed by the folder type */
      };

    /* Have the folder type destroy its data object and buffers. */
    /* --------------------------------------------------------- */
    AlFolderT_eval_destroy(the_folder->mailer_type,
			   (VOIDP) the_folder->data);

    /* Actually destroy the folder object itself. */
    /* ------------------------------------------ */
    Memory_free((VOIDP) the_folder);
  };

 };              /* end of AlFolder_destroy()  */

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

Msg AlFolder_get_current_msg(the_folder)
     AlFolder the_folder;
 {
   AlFolder_debug(" Entering >> AlFolder_get_current_msg()\n");

   /* First, check and insure that there is a msg. */
   /* -------------------------------------------- */
   if (the_folder->current_msg_packette == NULL)
     return(NULL);   /* haven't started reading folder yet */
   if (the_folder->current_msg_packette->the_msg == NULL)
     return(NULL);   /* haven't read first msg yet */

   AlFolder_debug(" Exiting << AlFolder_get_current_msg()\n");
   return(the_folder->current_msg_packette->the_msg);

 };                 /* end of AlFolder_get_current_msg()  */

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