#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* maximum number of employees that can be stored at once (relevant only
   to storage using an array) */
#define MAX_EMPLOYEES 200
#define MAX_NAME_LENGTH 100
#define MAX_JOB_LENGTH  100
#define MAX_NUMBER_LENGTH  3
int employees = 0,i,e;
static struct Employee *employee_list = NULL;
static struct Employee *first = NULL;
struct Employee *old = NULL, *old1 = NULL;


/* Employee structure
 */
struct Employee
{
   /* Employee details */
   char name[MAX_NAME_LENGTH+1]; /* name string */
   char sex;                     /* sex identifier, either 'M' or 'F' */
   int  age;                     /* age */
   char job[MAX_JOB_LENGTH+1];   /* job string */
   
   /* pointers to previous and next employee structures in the linked list
      (for if you use a linked list instead of an array) */
   struct Employee *prev, *next, *old;
};
/* Sorts out the employees as a function so it can be called from anywhere
 * in the program.
 */
static void sort(void)
{
   /* Set up the structures and integer needed */
   struct Employee *one, *two, *change;
   int a;
   /* Allocate a memory space for them */
   one = (struct Employee *) malloc ( sizeof(struct Employee) );
   two = (struct Employee *) malloc ( sizeof(struct Employee) );
   change = (struct Employee *) malloc ( sizeof(struct Employee) );
   /* Check if the first two are the right way round or not, if not swap then
    * over. Move on to the next two and repeat through to the end of the list
    */
   for(;;)
   {
    a = 0;
    one = first;
    two = one->next;
    for(i = 1; i < employees; i++)
    {
     if (strcmp(one->name, two->name) > 0)
     {
      strcpy(change->name, one->name);
      change->sex = one->sex;
      change->age = one->age;
      strcpy(change->job, one->job);
  
      strcpy(one->name, two->name);
      one->sex = two->sex;
      one->age = two->age;
      strcpy(one->job, two->job);
      
      strcpy(two->name, change->name);
      two->sex = change->sex;
      two->age = change->age;
      strcpy(two->job, change->job);
     }
     one = one->next;
     two = two->next;
    }
    /* Go back to the start of the linked list */
    one = first;
    two = one->next;
    /* Check to see if they are in the right order if */
    for(i = 1; i < employees; i++)
    {
     if (strcmp(one->name, two->name) > 0)
     {
      a = 1;
      break;
     }
     one = one->next;
     two = two->next;
    }
    /* If they were in the right order exit if not go back and sort again */
    if(a == 0)
     return;
    else
     continue;
   }
}


/* read_line():
 *
 * Read line of characters from file pointer "fp", copying the characters
 * into the "line" string, up to a maximum of "max_length" characters, plus
 * one for the string termination character '\0'. Reading stops upon
 * encountering the end-of-line character '\n', for which '\0' is substituted
 * in the string. If the end of file character EOF is reached before the end
 * of the line, the failure condition (-1) is returned. If the line is longer
 * than the maximum length "max_length" of the string, the extra characters
 * are read but ignored. Success is returned (0) on successfully reading
 * a line.
 */
static int read_line ( FILE *fp, char *line, int max_length )
{
   int i;
   char ch;

   /* initialize index to string character */
   i = 0;

   /* read to end of line, filling in characters in string up to its
      maximum length, and ignoring the rest, if any */
   for(;;)
   {
      /* read next character */
      ch = fgetc(fp);

      /* check for end of file error */
      if ( ch == EOF )
	 return -1;

      if ( ch == '\n' && i == 0)
      {
       return -1;
      }
         
      /* check for end of line */
      if ( ch == '\n' )
      {
	 /* terminate string and return */
	 line[i] = '\0';
	 return 0;
      }

      /* fill character in string if it is not already full*/
      if ( i < max_length )
	 line[i++] = ch;
   }

   /* the program should never reach here */
   return -1;
}

/* read_string():
 *
 * Reads a line from the input file pointer "fp", starting with the "prefix"
 * string, and filling the string "string" with the remainder of the contents
 * of the line. If the start of the line does not match the "prefix" string,
 * the error condition (-1) is returned. Having read the prefix string,
 * read_string() calls read_line() to read the remainder of the line into
 * "string", up to a maximum length "max_length", and returns the result.
 */
static int read_string ( FILE *fp,
			 char *prefix, char *string, int max_length )
{
   int i;

   /* read prefix string */
   for ( i = 0; i < strlen(prefix); i++ )
      if ( fgetc(fp) != prefix[i] )
	 /* file input doesn't match prefix */
	 return -1;

   /* read remaining part of line of input into string */
   return ( read_line ( fp, string, max_length ) );
}

/* menu_add_employee():
 *
 * Add new employee to database
 */
static void menu_add_employee(void)
{
   /* First part sets up the variables to be used and the new structure */
   int olda, how_old;
   char line[301],sex[1];
   struct Employee *new;
   new = (struct Employee *) malloc ( sizeof(struct Employee) );
   
   /* Takes in the name of the employee and checks for correct data */
   for(;;)
   {
    fprintf(stderr, "Enter name :-\n");
    if(read_line(stdin, line, MAX_NAME_LENGTH) == -1)
    {
     fprintf(stderr, "Need some data\n");
     continue;
    }
    else
    {
     strcpy(new->name, line);
     break;
    }
   }
   
   /* Takes in the sex of the employee and checks for correct data */
   for(;;)
   {
    fprintf(stderr, "Enter sex :-\n");
    /*sex = fgetc(stdin);*/
    read_line(stdin,sex,2);
    if ((sex[0] == 'm' || sex[0] == 'M') && sex[1] == '\0')
       new->sex = 'M';
    if ((sex[0] == 'f' || sex[0] == 'F') && sex[1] == '\0')
       new->sex = 'F';
    if ( new->sex == 'M' ||  new->sex == 'F')
     {
     fflush(stdin);
     break;
     }
    else
    {
     fprintf ( stderr, "Please use M/F\n" );
     fflush(stdin);
     continue;
    }
   }
      
   /* Takes in the age of the employee and checks for correct data */      
   for(;;)
   { 
    fprintf(stderr, "Enter age :-\n");
    read_line ( stdin, line, 300 );
       
    olda = sscanf ( line, "%d", &how_old );
    if ( olda != 1 )
    {
     fprintf ( stderr, "Corrupted age\n" );
     continue;
    }
    if ( how_old <= 0)
    {
     fprintf ( stderr, "Too young\n" );
     continue;
    }
    if (how_old > 0)    
    {
    new->age = how_old;
    break;
    }
   }
   
   /* Takes in the job of the employee and checks for correct data */   
   for(;;)
   {
    fprintf(stderr, "Enter job :-\n");
    if(read_line(stdin, line, MAX_JOB_LENGTH) == -1)
    {
     fprintf(stderr, "Need some data\n");
     continue;
    }
    else
    {
     strcpy(new->job, line);
     break;
    }
   }
   
   /* Sets up the next and previous pointers */
   new->prev = NULL;
   new->next = employee_list;
   if (employees > 0)
    employee_list->prev = new;
   employee_list = new;
   if (employees == 0)
    first = employee_list;
   

   employees++;
}

/* menu_print_database():
 *
 * Print database of employees to standard output.
 */
static void menu_print_database(void)
{
   /* Set up the structure */
   struct Employee *out;
   
   /* If there is no employees then dont print anything */
   if(employees < 1)
   {
    fprintf(stderr, "Empty employee database\n");
    return;
   }
   /* Set up first position */
   out = first;
   out->prev = NULL;
   out->next = employee_list;
   /* If there is more than one employee then sort them */
   if(employees > 1)
    sort();   
   /* Sets up previous entries */
   if(employees > 1)
   {
    for(i = 0; i < employees; i++)
    {
     old = out;
     out = out->next;
     out->prev = old;
    }
   } 
   out = first;
   /* Prints out the database, in the format requested */
   for(i = 0; i < employees; i++)
   {
   printf("Name: %s\n", out->name);
   printf("Sex: %c\n", out->sex);
   printf("Age: %d\n", out->age);
   printf("Job: %s\n\n", out->job);
   out = out->next;
   }
}	    

/* menu_delete_employee():
 *
 * Delete new employee from database.
 */
static void menu_delete_employee(void)
{
   /* Set up the structures and arrays */
   char line[301];
   struct Employee *out, *new, *one, *two, *change;
   
   /* If there is no employees then dont delete anything */
   if(employees < 1)
   {
    fprintf(stderr, "Empty employee database\n");
    return;
   }   
   /* Set up where the first employee is */
   out = first;
   out->prev = NULL;
   out->next = employee_list;
   /* Allocate memory for all the structures */
   new = (struct Employee *) malloc ( sizeof(struct Employee) );
   one = (struct Employee *) malloc ( sizeof(struct Employee) );
   two = (struct Employee *) malloc ( sizeof(struct Employee) );
   change = (struct Employee *) malloc ( sizeof(struct Employee) );
   
   /* If there is more than one employee then sort them */
   if(employees > 1)
    sort();   
    /* Sets up previous entries */
   if(employees > 1)
   {
    for(i = 0; i < employees; i++)
    {
     old = out;
     out = out->next;
     out->prev = old;
    }
   }
   out = first;
   /* Takes in the name */
   for(;;)
   {
    fprintf(stderr, "Enter name :-\n");
    if(read_line(stdin, line, MAX_NAME_LENGTH) == -1)
    {
     fprintf(stderr, "Need some data\n");
     continue;
    }
    else
    {
     strcpy(new->name, line);
     break;
    }
   }
   fprintf(stderr, "Searching....\n");
   /* Repeat the amount of times there is employees and count from one */
   e = 0;
   for(i = 0; i < employees; i++)
   {
    /* Searches for the name */
    if(strcmp(new->name, out->name) == 0)
    {
     fprintf(stderr, "Name: %s\n", out->name);
     fprintf(stderr, "Sex: %c\n", out->sex);
     fprintf(stderr, "Age: %d\n", out->age);
     fprintf(stderr, "Job: %s\n\n", out->job);
     /* If its the last employee then start again */
     if(employees == 1)
     {
      
      first = NULL;
      employee_list = NULL;
      employees = 0;
      free(first);
      fprintf(stderr, "Deleted\n");
      return;
     }
     /* If its the first employee then make the next the first */
     if( i == 0 && employees > 1)
     {
      free(first);
      first = out->next;
      out = out->prev;
      out->next = first;
      old = out;
      out = out->next;
      out->prev = old;
      employee_list = out->next;
     }
     else
     /* Move the employee to the back then take one off employees which
      * removes it from the print list
      */
     {
      one = out;
      two = out->next;
      for(e = (i+1); e < employees; e++)
      {
       strcpy(change->name, one->name);
       change->sex = one->sex;
       change->age = one->age;
       strcpy(change->job, one->job);
  
       strcpy(one->name, two->name);
       one->sex = two->sex;
       one->age = two->age;
       strcpy(one->job, two->job);
      
       strcpy(two->name, change->name);
       two->sex = change->sex;
       two->age = change->age;
       strcpy(two->job, change->job);
       one = one->next;
       two = two->next;
      }
     }
     free(two);
     employees--;
     fprintf(stderr, "Deleted\n");
     return;
    }
    else
    /* If not found go on to the next employee. 'e' is to find out when
     * the program should stop moving the employee to the back
     */
    {
     e++;
     out = out->next;
    }
   }
   /* After going through all the employees and you've got past all the
    * returns, then the employee does not exist
    */
   fprintf(stderr, "Not found\n");
   return;
   
}

/* read file containing database of employees */
static void read_employee_database ( char *file_name )
{
   /* fill in the code here in part 3, and add any extra functions you need */
   
}


/* codes for menu */
#define ADD_CODE    0
#define DELETE_CODE 1
#define PRINT_CODE  2
#define EXIT_CODE   3

int main ( int argc, char *argv[] )
{
   /* check arguments */
   if ( argc != 1 && argc != 2 )
   {
      fprintf ( stderr, "Usage: %s [<database-file>]\n", argv[0] );
      exit(-1);
   }

   /* read database file if provided, or start with empty database */
   if ( argc == 2 )
      read_employee_database ( argv[1] );

   for(;;)
   {
      int choice, result;
      char line[301];

      /* print menu to standard error */
      fprintf ( stderr, "\nOptions:\n" );
      fprintf ( stderr, "%d: Add new employee to database\n", ADD_CODE );
      fprintf ( stderr, "%d: Delete employee from database\n", DELETE_CODE );
      fprintf ( stderr, "%d: Print database to screen\n", PRINT_CODE );
      fprintf ( stderr, "%d: Exit database program\n", EXIT_CODE );
      fprintf ( stderr, "\nEnter option: " );

      if ( read_line ( stdin, line, 300 ) != 0 ) continue;

      result = sscanf ( line, "%d", &choice );
      if ( result != 1 )
      {
	 fprintf ( stderr, "corrupted menu choice\n" );
	 continue;
      }

      switch ( choice )
      {
         case ADD_CODE: /* add employee to database */
	 menu_add_employee();
	 break;

         case DELETE_CODE: /* delete employee from database */
	 menu_delete_employee();
	 break;

         case PRINT_CODE: /* print database contents to screen
			     (standard output) */
	 menu_print_database();
	 break;

	 /* exit */
         case EXIT_CODE:
	 break;

         default:
	 fprintf ( stderr, "illegal choice %d\n", choice );
	 break;
      }

      /* check for exit menu choice */
      if ( choice == EXIT_CODE )
	 break;
   }

   return 0;   
}
