#include "addressbook.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>


#define LOCAL_DATA_FILE	1


#ifndef LOCAL_DATA_FILE
#define	FILE_FOLDER	".addressbook"
#endif
#define FILE_NAME	"addressbook.dat"
#define CLOSE_FILE	""
#define OPEN_RDONLY	"r"
#define OPEN_RDWR	"r+"

#define BUFFER_SIZE	512
#define HEADER_SIZE     16
#define DATA_OFFSET     22



static gint num_recs;



/******** External function declarations ********/




/******** Internal function declarations ********/

static FILE *prepare_file(gchar *name, gchar *mode);
static void write_header(FILE *fp);
static gint get_directory(gchar **path);
static gint insert_record(FILE *fin, FILE *fout, addr_info *aip);
static void save_record(FILE *fd, gchar *key, addr_info *aip);
static char *get_field_lengths(gchar *s, gint *lens);
static char *get_record_key(gchar *s, gint len, gchar *buf);
static gint get_names(gchar *s, gchar **lname, gchar **fname);
static gchar *get_field_value(gchar *s, gint len, gchar **rtn);
static gchar *set_field_value(gchar *s, gint *len, gchar *buf);
static char *get_token(char *s, char *buf);



/******** External function definitions ********/

gint read_address_book(GtkWidget *clist)
{
   FILE *fp;
   gchar *p, *q, *strval[2], key[200], buf[BUFFER_SIZE];
   gint lens[NUM_FIELDS];

   if((fp = prepare_file(FILE_NAME, OPEN_RDONLY)) == NULL)
      return 0;

   num_recs = 0;

   strval[NAME_INX] = key;

   while(fgets(buf, BUFFER_SIZE, fp) != NULL)
   {
      p = get_field_lengths(buf, lens);

      /* record key value */
      p = get_record_key(p, lens[NAME], key);

      /* take office phone no only */
      p = get_field_value(p, lens[TITLE], NULL);
      p = get_field_value(p, lens[COMPANY], NULL);
      p = get_field_value(p, lens[WORK], &q);

      strval[PHONE_INX] = q;

      /* append a row to the column list */
      gtk_clist_append(GTK_CLIST(clist), strval);

      g_free(q);

      num_recs++;
   }

   return num_recs;
}



gint read_record_details(gchar *key, addr_info *aip)
{
   FILE *fp;
   gchar *p, dummy[100], buf[BUFFER_SIZE];
   gint len, lens[NUM_FIELDS];

   if((fp = prepare_file(FILE_NAME, OPEN_RDONLY)) == NULL)
      return 0;

   len = strlen(key);

   while(fgets(buf, BUFFER_SIZE, fp) != NULL)
   {
      if(strncmp(buf + DATA_OFFSET, key, len) == 0)
      {
         p = get_field_lengths(buf, lens);

         p = get_record_key(p, lens[NAME], dummy);

         get_names(dummy, &aip->lname, &aip->fname);

         p = get_field_value(p, lens[TITLE], &aip->title);
         p = get_field_value(p, lens[COMPANY], &aip->cname);
         p = get_field_value(p, lens[WORK], &aip->work);
         p = get_field_value(p, lens[HOME], &aip->home);
         p = get_field_value(p, lens[FAX], &aip->fax);
         p = get_field_value(p, lens[OTHER], &aip->other);
         p = get_field_value(p, lens[EMAIL], &aip->email);
         p = get_field_value(p, lens[ADDRESS1], &aip->address1);
         p = get_field_value(p, lens[ADDRESS2], &aip->address2);

         return 1;
      }
   }

   return 0;
}



gint save_record_details(addr_info *aip)
{
   FILE *fin, *fout;
   gchar cmd[256], name[128], temp[128];

   strcpy(name, FILE_NAME);
   sprintf(temp, "/tmp/addressbook.%d", getpid());

   /* allow NULL in fin */
   fin = prepare_file(FILE_NAME, OPEN_RDWR);

   /* Must not use prepare_file to open the output file */
   if((fout = fopen(temp, "w+")) == NULL)
      return 0;

   write_header(fout);

   if((fin == NULL) || insert_record(fin, fout, aip))
   {
      /* Close the output file "temp" */
      fclose(fout);

      if(fin != NULL)
      {
         /* Close the input file "name" */
         prepare_file(name, CLOSE_FILE);
      }

      /* Remove temp file if renaming output file failed */
      if(rename(temp, name) != 0)
      {
         /* Try again using system command */
         sprintf(cmd, "mv %s %s", temp, name);

         if(system(cmd) != 0)
            unlink(temp);
         else
            return 1;
      }
      else
         return 1;
   }

   return 0;
}



gint delete_record_details(addr_info *aip)
{
   /* If both first and last names are empty, the record will *
    * not be saved in the data file.                          */

   aip->status = RECORD_DELETE;

   return save_record_details(aip);
}


#if (LOOKUP_FILE == 1)
gint lookup_from_file(const gchar *s, gchar *key, gint start_index)
{
   FILE *fp;
   gchar *p, buf[BUFFER_SIZE];
   gchar *lower_s = g_strdup(s);
   gint i, len;

   if((fp = prepare_file(FILE_NAME, OPEN_RDONLY)) == NULL)
      return 0;

   buf[3] = '\0';

   p = buf + DATA_OFFSET;
   i = 0;

   g_strdown(lower_s);

   while(fgets(buf, BUFFER_SIZE, fp) != NULL)
   {
	  if (i >= start_index)
      {
         /* save the key before covert buffer to lower case */
         sscanf(buf, "%d", &len);
         get_record_key(p, len, key);

         g_strdown(p);

         /* if found, return record index */
         if(strstr(p, lower_s) != NULL)
            return i;
	  }

      i++;
   }

   g_free(lower_s);

   return -1;
}
#endif


/******** Internal function definitions ********/

static FILE *prepare_file(gchar *name, gchar *mode)
{
   static FILE *fp;
   static gchar *dir, *fmode, fname[128];
   gchar path[256];

   if(mode == CLOSE_FILE)
   {
      /* Close the opened file */
      fclose(fp);
      sprintf(name, "%s/%s", dir, fname);
      strcpy(fname, "");
      fp = NULL;
      return fp;
   }

   if(fp && (strcmp(fname, name) == 0))
   {
      /* Determine whether we need to reopen the file for writing */
      if((fmode != mode) && (mode == OPEN_RDWR))
      {
         fmode = CLOSE_FILE;
         strcpy(fname, "");
         fclose(fp);
      }
      else
      {
         /* access mode is ok so just reset i/o file beginning */
         fseek(fp, (long)HEADER_SIZE, SEEK_SET);
         return fp;
      }
   }

   if(dir == NULL)
      get_directory(&dir);

   /* Need to open the file so set the full path first */
   sprintf(path, "%s/%s", dir, name);

   if((fp = fopen(path, mode)) == NULL)
   {
      /* Create the file if it is in writing mode */
      if(mode == OPEN_RDWR && (fp = fopen(path, "w+")) != NULL)
         write_header(fp);
   }
   else
      fseek(fp, (long)HEADER_SIZE, SEEK_SET);

   if(fp != NULL)
   {
      fmode = mode;
      strcpy(fname, name);
   }

   return fp;
}



static void write_header(FILE *fp)
{
   /* go to the beginning of the file */
   fseek(fp, 0L, SEEK_SET);

   /* string written must be the same size of HEADER_SIZE */
   fprintf(fp, "%4d %10s\n", num_recs, "*RESERVED*");
}



static gint get_directory(gchar **rtn)
{
   gchar path[256];

#ifdef LOCAL_DATA_FILE
#ifdef PDA
   strcpy(path, "/buffer");
#else
   getcwd(path, 256);
#endif

   *rtn = g_strdup(path);

   return 1;
#else
   struct stat buf;
   gint f;

   sprintf(path, "%s/%s", getenv("HOME"), FILE_FOLDER);

   /* Allocate memory and copy the path to it */
   *rtn = g_strdup(path);

   if((f = stat(path, &buf)) != 0)
   {
      /* Folder not exists so create it */
      f = mkdir(path, 600);
   }

   return (f == 0);
#endif
}



static gint insert_record(FILE *fin, FILE *fout, addr_info *aip)
{
   gchar t[100], key[200], buf[BUFFER_SIZE];
   gint f, len, done;

   /* setup key from current record */
   sprintf(key, "%s, %s", aip->lname, aip->fname);

   done = 0;

   while(fgets(buf, BUFFER_SIZE, fin))
   {
      if(! done)
      {
         /* get record key from buffer */
         strncpy(t, buf, 2);
         sscanf(t, "%d", &len);
         get_record_key(buf + DATA_OFFSET, len, t);

         /* compare keys */
         f = strcasecmp(t, key);

         /* found an existing key as the specified one */
         if(f == 0)
         {
            /* duplicated key not allowed for new record */
            if(aip->status == RECORD_INSERT)
               return 0;

            /* don't save old record and if delete flag is set */
            if(aip->status == RECORD_DELETE)
            {
               num_recs--;
               aip->status = RECORD_DONE;
               done = 1;
            }

            /* insert record in the next position */
            continue;
         }

         if(f > 0)
         {
            /* got insertion point so save record now */
            save_record(fout, key, aip);
            done = 1;
         }
      }

      fputs(buf, fout);
   }

   /* If not done yet, save record at the end of the file */
   if(! (aip->status == RECORD_DELETE || done))
      save_record(fout, key, aip);

   if(aip->status == RECORD_INSERT)
      num_recs++;

   write_header(fout);

   return 1;
}


  
static void save_record(FILE *fp, gchar *key, addr_info *aip)
{
   gchar *p, val[BUFFER_SIZE];
   gint i, lens[NUM_FIELDS];

   /* do not save if key contains no last and first names */
   if(strcmp(key, ", ") != 0)
   {
      /* first name and last name are used as a key */
      lens[NAME] = strlen(key);

      /* set all fields except first name and last name */
      p = set_field_value(aip->title, &lens[TITLE], val);
      p = set_field_value(aip->cname, &lens[COMPANY], p);
      p = set_field_value(aip->work, &lens[WORK], p);
      p = set_field_value(aip->home, &lens[HOME], p);
      p = set_field_value(aip->fax, &lens[FAX], p);
      p = set_field_value(aip->other, &lens[OTHER], p);
      p = set_field_value(aip->email, &lens[EMAIL], p);
      p = set_field_value(aip->address1, &lens[ADDRESS1], p);
      set_field_value(aip->address2, &lens[ADDRESS2], p);

      /* pad trailing blanks */
      for(i = 0; i < NUM_FIELDS; i++)
         fprintf(fp, "%2d", lens[i]);

      /* reserved for future use */
      fprintf(fp, "  ");

      /* Write key and values to the output file */
      fprintf(fp, "%s%s\n", key, val);
   }
}



static char *get_field_lengths(gchar *s, gint *lens)
{
   gchar *p, num[3];
   gint i;

   p = s;

   num[2] = '\0';

   for(i = 0; i < NUM_FIELDS; i++, p += 2)
   {
      strncpy(num, p, 2);

      sscanf(num, "%d", &lens[i]);
   }

   return p = s + DATA_OFFSET;
}



static char *get_record_key(gchar *s, gint len, gchar *buf)
{
   strncpy(buf, s, len);

   buf[len] = '\0';

   return s + len;
}



static gint get_names(gchar *s, gchar **lname, gchar **fname)
{
   gchar s1[100], s2[100];

   get_token(get_token(s, s1), s2);

   *lname = g_strdup(s1);
   *fname = g_strdup(s2);

   return 1;
}



static gchar *get_field_value(gchar *s, gint len, gchar **rtn)
{
   gchar buf[BUFFER_SIZE];

   /* copy the required no. of characters */
   strncpy(buf, s, len);

   buf[len] = '\0';

   /* assign value only if buffer pointer is not NULL */
   if(rtn != NULL)
      *rtn = g_strdup(buf);

   /* return pointer to the next uncopied character */
   return s + len;
}



static gchar *set_field_value(gchar *s, gint *len, gchar *buf)
{
   gchar *p;

   p = buf + sprintf(buf, "%s", s);

   *len = strlen(s);

   return p;
}



static char *get_token(char *s, char *buf)
{
   char *p, *q;

   p = s;

   while(*p && isspace(*p))
      p++;

   q = p;

   while(*q && *q != ',')
      q++;

   strncpy(buf, p, q - p);

   buf[q - p] = '\0';

   return ++q;
}



