// complex_struct_clean.c - complex structs can also be written as
//                          clean code.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//------------------------------------------------------------
// useful helper functions
void exitOnFatalErrorOutOfMemory();
char *cloneString(char* str);


//------------------------------------------------------------
// an office number in our company consists of a building letter, a
// floor-number and an office number on this floor. E.g.: D 1.15
struct OfficeNumber
{
  char building;          // the single letter for the building
  short floor;            // the floor number
  short office_number;    // the office number on the floor
};

// access functions

struct OfficeNumber *newOfficeNumber(char building_id, 
                                     short floor_num,
                                     short office_num);

void deleteOfficeNumber(struct OfficeNumber *office_num);

struct OfficeNumber *cloneOfficeNumber(struct OfficeNumber 
                                       *office_num);

//------------------------------------------------------------
// a typical address
struct Address
{
  char *country;   // the country, e.g. Austria
  char *state;     // the state in the country if applicable
                   // e.g. Minnesota for addresses in the USA
                   // NULL if not applicable
  char *zip_code;  // the zip code, e.g. A-8010
  char *city;      // the city, e.g. Graz
  char *street;    // the streetname
  char *number;    // the number of the building in the 
                   // street. This is a string, because there
                   // can also be numbers containing letters!
};

// access functions

struct Address *newAddress(char *country, char *state, 
                           char *zip_code, char *city,
                           char *street, char *number);

void deleteAddress(struct Address *addr);

struct Address *cloneAddress(struct Address *addr);

void setCountryInAddress(struct Address *addr, char *country);

void setStateInAddress(struct Address *addr, char *state);

void setZipCodeInAddress(struct Address *addr, char *zip);

void setCityInAddress(struct Address *addr, char *city);

void setStreetInAddress(struct Address *addr, char *street);

void setNumberInAddress(struct Address *addr, char *number);

//------------------------------------------------------------
// an office is defined like that
struct Office
{
  struct Address *building_address; // address of the building
  struct OfficeNumber *office_num;  // the office in the building
};

// access functions

struct Office *newOffice(struct Address *address, 
                         struct OfficeNumber *office_num);

void deleteOffice(struct Office *office);

struct Office *cloneOffice(struct Office *office);

void setAddressInOffice(struct Office *office,
                        struct Address *address);

void setNumberInOffice(struct Office *office,
                       struct OfficeNumber *office_num);

//------------------------------------------------------------

// constant for extension in struct PhoneNumber below

#define NO_EXTENSION -1L

// a telephone number
struct PhoneNumber
{
  unsigned short country_prefix; // the prefix for the country, 
                                 // e.g. 43 for Austria
  unsigned short region_prefix;  // the prefix for the region,
                                 // e.g. 316 for Graz
  unsigned long phone_number;    // the actual phone number. Please
                                 // note that we assume here that
                                 // a long has to be long enough to
                                 // store a number. For demo only!
  long extension;                // the extension, if applicable
                                 // please note that we take a 
                                 // signed long here, because a
                                 // neg. number signals us that
                                 //  there is no extension 
                                 // available.
};

// access functions

struct PhoneNumber *newPhoneNumber(unsigned short country_prefix,
                                   unsigned short region_prefix,
                                   unsigned long phone_number,
                                   long extension);

void deletePhoneNumber(struct PhoneNumber *phone_number);

struct PhoneNumber *clonePhoneNumber(struct PhoneNumber *phone_num);

void setCountryPrefixInPhoneNumber(struct PhoneNumber *phone_num,
                                   unsigned short prefix);

void setRegionPrefixInPhoneNumber(struct PhoneNumber *phone_num,
                                  unsigned short prefix);

void setPhoneNumInPhoneNumber(struct PhoneNumber *phone_num,
                              unsigned long phone_number);

void setExtensionInPhoneNumber(struct PhoneNumber *phone_num,
                               long extension);


//------------------------------------------------------------

// employees in a company
struct Employee
{
  char *first_names;         // the first name(s) of the employee
  char *second_name;         // the name of the employee
  struct Office *office;     // the office of the employee
  struct PhoneNumber *phone; // the phone number of the employee
  struct PhoneNumber *fax;   // the fax number of the employee
  char *email;               // the email address of the employee
                             // or NULL if not available
};


// access functions


struct Employee *newEmployee(char *first_name,
                             char *second_name,
                             struct Office *office,
                             struct PhoneNumber *phone,
                             struct PhoneNumber *fax,
                             char *email);

void deleteEmployee(struct Employee *employee);

struct Employee *cloneEmployee(struct Employee *employee);

void setFirstNamesInEmployee(struct Employee *employee,
                             char *names);

void setSecondNameInEmployee(struct Employee *employee,
                             char *name);

void setOfficeInEmployee(struct Employee *employee,
                         struct Office *office);

void setPhoneNumberInEmployee(struct Employee *employee,
                              struct PhoneNumber *phone_num);

void setFaxNumberInEmployee(struct Employee *employee,
                            struct PhoneNumber *fax_num);

void setEmailInEmployee(struct Employee *employee,char *email);

//------------------------------------------------------------
int main()
{
  struct Employee *test_employee = NULL;

  // first create all the other structs necessary for an employee

  struct Address *office_building = newAddress("Austria",
                                               NULL,
                                               "A-8010",
                                               "Graz",
                                               "Inffeldgasse",
                                               "16c");
  struct OfficeNumber *office_num = newOfficeNumber('D',1,15);
  struct Office *office = newOffice(office_building,office_num);
  struct PhoneNumber *phone = newPhoneNumber(43,316,87341,3208);
  struct PhoneNumber *fax = newPhoneNumber(43,316,873461,
                                           NO_EXTENSION);

  // now create the employee
  test_employee = newEmployee("Otto","Muster",office,phone,fax,
                              "otto@nowhere.nocom");

  // print the data of the newly created employee. It is easy
  // to see that you have to know all involved structures here.
  // So rather than accessing these members directly it would
  // be nice to have some handy "get..." Functions that make
  // our lives easier. For example something like
  // getFirstNameFromEmployee(...) or also something like
  // getNameFromEmployee(...) that immediately returns the first
  // and last name together, etc. What you need is up to your
  // phantasy, just play around with it a little.

  printf("Employee: %s %s\n",test_employee->first_names,
                             test_employee->second_name);
  printf("Office:   %s %s\n          %s %s\n",
         test_employee->office->building_address->street,
         test_employee->office->building_address->number,
         test_employee->office->building_address->zip_code,
         test_employee->office->building_address->city);

  // here a nice "get..." function that makes checking for state,
  // etc. and returns the result would make life much easier and
  // code much better readable.

  if (test_employee->office->building_address->state)
    printf("          %s, %s\n",
           test_employee->office->building_address->state,
           test_employee->office->building_address->country);
  else
    printf("          %s\n",
           test_employee->office->building_address->country);
  printf("          Room: %c %d %d\n",
                    test_employee->office->office_num->building,
                    test_employee->office->office_num->floor,
                    test_employee->office->office_num->office_number);
  printf("Phone:    +%d-%d-%d",
                    test_employee->phone->country_prefix,
                    test_employee->phone->region_prefix,
                    test_employee->phone->phone_number);
  if (test_employee->phone->extension != NO_EXTENSION)
    printf("-%d\n",test_employee->phone->extension);
  else
    printf("\n");
  printf("Fax:      +%d-%d-%d",
                    test_employee->fax->country_prefix,
                    test_employee->fax->region_prefix,
                    test_employee->fax->phone_number);
  if (test_employee->fax->extension != NO_EXTENSION)
    printf("-%d\n",test_employee->fax->extension);
  else
    printf("\n");
  printf("Email:    %s\n",test_employee->email);
}

//============================================================
// Here come the access functions for the structs
//============================================================

//------------------------------------------------------------
// alloc the memory needed and initialize the struct
struct OfficeNumber *newOfficeNumber(char building_id, 
                                     short floor_num,
                                     short office_num)
{
  struct OfficeNumber *off_num = malloc(sizeof(struct OfficeNumber));
  if (!off_num) // malloc failed
    exitOnFatalErrorOutOfMemory();
  off_num->building = building_id;
  off_num->floor = floor_num;
  off_num->office_number = office_num;
  return(off_num);
}

//------------------------------------------------------------
// in our case there is no dynamic memory involved, just free it
void deleteOfficeNumber(struct OfficeNumber *office_num)
{
  if (office_num)
    free(office_num);
}

//------------------------------------------------------------
// in our case deep- and shallow copy are the same, because
// there are no dynamic memory blocks involved
struct OfficeNumber *cloneOfficeNumber(struct OfficeNumber 
                                       *office_num)
{
  if (!office_num)
    return(NULL);
  return(newOfficeNumber(office_num->building,
                         office_num->floor,
                         office_num->office_number));
}

//------------------------------------------------------------
// Clone all strings, to make them robust against unwanted
// changes from outside.
struct Address *newAddress(char *country, char *state,
                           char *zip_code, char *city,
                           char *street, char *number)
{
  struct Address *addr = malloc(sizeof(struct Address));
  if (!addr) 
    exitOnFatalErrorOutOfMemory();
  addr->country = cloneString(country);
  addr->state = cloneString(state);
  addr->zip_code = cloneString(zip_code);
  addr->city = cloneString(city);
  addr->street = cloneString(street);
  addr->number = cloneString(number);
  return(addr);
}

//------------------------------------------------------------
// first free all the strings, then free the struct itself
void deleteAddress(struct Address *addr)
{
  if (!addr)
    return;
  if (addr->country)
    free(addr->country);
  if (addr->state)
    free(addr->state);
  if (addr->zip_code)
    free(addr->zip_code);
  if (addr->city)
    free(addr->city);
  if (addr->street)
    free(addr->street);
  if (addr->number)
    free(addr->number);
  free(addr);
}

//------------------------------------------------------------
// our newAddress function already copies all strings, so a
// deep copy can be obtained this way
struct Address *cloneAddress(struct Address *addr)
{
  if (!addr)
    return(NULL);
  return(newAddress(addr->country,addr->state,addr->zip_code,
                    addr->city,addr->street,addr->number));
}

//------------------------------------------------------------
void setCountryInAddress(struct Address *addr, char *country)
{
  if (!addr)
    return;
  if (addr->country)
    free(addr->country);
  addr->country = cloneString(country);
}

//------------------------------------------------------------
void setStateInAddress(struct Address *addr, char *state)
{
  // equivalent to setCountryInAddress - I leave this up to you :-)
}

//------------------------------------------------------------
void setZipCodeInAddress(struct Address *addr, char *zip)
{
  // equivalent to setCountryInAddress - I leave this up to you :-)
}

//------------------------------------------------------------
void setCityInAddress(struct Address *addr, char *city)
{
  // equivalent to setCountryInAddress - I leave this up to you :-)
}

//------------------------------------------------------------
void setStreetInAddress(struct Address *addr, char *street)
{
  // equivalent to setCountryInAddress - I leave this up to you :-)
}

//------------------------------------------------------------
void setNumberInAddress(struct Address *addr, char *number)
{
  // equivalent to setCountryInAddress - I leave this up to you :-)
}

//------------------------------------------------------------
// This function does NOT create a deep clone of address and
// office  number, it assumes that nobody from the outside
// accesses these internals after constructing this struct.
// It also assumes that only dynamically created structs are
// given as parameters, because deleteOffice (see below) frees
// the memory blocks.
// In case of doubt of how this function is used, it should 
// be implemented the other way round and make clones.
// Nevertheless the two objects are now under control of the
// struct and will also be freed when the struct is deleted.
// For the exact functionality, what is cloned and what isn't
// there has to be a standard that is valid for the whole code.
// Something like: "all primitive types and arrays of primitive
// types like strings are cloned, structs are not". The most
// important part here is uniformity! This means, whatever
// method you choose to create objects, they all have to do
// exactly the same. In our case they all have to clone
// primitive types and they all don't clone complex types.
struct Office *newOffice(struct Address *address, 
                         struct OfficeNumber *office_num)
{
  struct Office *office = malloc(sizeof(struct Office));
  if (!office)
    exitOnFatalErrorOutOfMemory();
  office->building_address = address;
  office->office_num = office_num;
  return(office);
}

//------------------------------------------------------------
// delete the members and then delete the struct itself. Don't
// call free directly on the members, because there is a
// delete... function.
void deleteOffice(struct Office *office)
{
  if (!office)
    return;
  deleteAddress(office->building_address);
  deleteOfficeNumber(office->office_num);
  free(office);
}

//------------------------------------------------------------
struct Office *cloneOffice(struct Office *office)
{
  struct Office *ret;
  if (!office)
    return(NULL);
  if (!(ret = malloc(sizeof(struct Office))))
    exitOnFatalErrorOutOfMemory();
  ret->building_address = cloneAddress(office->building_address);
  ret->office_num = cloneOfficeNumber(office->office_num);
  return(ret);
}

//------------------------------------------------------------
// there is no deep copy made from address, since it is
// assumed that nobody from the outside changes the contents of
// it. When the struct is deleted, the address stored in it is
// deleted too.
void inOfficeChangeAddress(struct Office *office,
                           struct Address *address)
{
  if (!office)
    return;
  if (office->building_address)
    deleteAddress(office->building_address);
  office->building_address = address;
}

//------------------------------------------------------------
void inOfficeChangeNumber(struct Office *office,
                          struct OfficeNumber *office_num)
{
  // equivalent to inOfficeChangeAddress - I leave this up to you
}

//------------------------------------------------------------
struct PhoneNumber *newPhoneNumber(unsigned short country_prefix,
                                   unsigned short region_prefix,
                                   unsigned long phone_number,
                                   long extension)
{
  struct PhoneNumber *num = malloc(sizeof(struct PhoneNumber));
  if (!num)
    exitOnFatalErrorOutOfMemory();
  num->country_prefix = country_prefix;
  num->region_prefix = region_prefix;
  num->phone_number = phone_number;
  num->extension = extension;
  return(num);
}

//------------------------------------------------------------
void deletePhoneNumber(struct PhoneNumber *phone_number)
{
  if (!phone_number)
    return;
  free(phone_number);
}

//------------------------------------------------------------
struct PhoneNumber *clonePhoneNumber(struct PhoneNumber *phone_num)
{
  struct PhoneNumber *ret;
  if (!phone_num)
    return(NULL);
  ret = malloc(sizeof(struct PhoneNumber));
  if (!ret)
    exitOnFatalErrorOutOfMemory();
  ret->country_prefix = phone_num->country_prefix;
  ret->region_prefix = phone_num->region_prefix;
  ret->phone_number = phone_num->phone_number;
  ret->extension = phone_num->extension;
  return(ret);
}

//------------------------------------------------------------
void setCountryPrefixInPhoneNumber(struct PhoneNumber *phone_num,
                                   unsigned short prefix)
{
  // equivalent to "set..." functions - I leave this up to you
}

//------------------------------------------------------------
void setRegionPrefixInPhoneNumber(struct PhoneNumber *phone_num,
                                  unsigned short prefix)
{
  // equivalent to "set..." functions - I leave this up to you
}

//------------------------------------------------------------
void setPhoneNumInPhoneNumber(struct PhoneNumber *phone_num,
                              unsigned long phone_number)
{
  // equivalent to "set..." functions - I leave this up to you
}

//------------------------------------------------------------
void setExtensionInPhoneNumber(struct PhoneNumber *phone_num,
                               long extension)
{
  // equivalent to set..." functions - I leave this up to you
}

//------------------------------------------------------------
// strings are cloned, structs are not (see also newOffice(...))
struct Employee *newEmployee(char *first_name, char *second_name,
                             struct Office *office,
                             struct PhoneNumber *phone,
                             struct PhoneNumber *fax,
                             char *email)
{
  struct Employee *employee = malloc(sizeof(struct Employee));
  if (!employee)
    exitOnFatalErrorOutOfMemory();
  employee->first_names = cloneString(first_name);
  employee->second_name = cloneString(second_name);
  employee->office = office;
  employee->phone = phone;
  employee->fax = fax;
  employee->email = cloneString(email);
  return(employee);
}

//------------------------------------------------------------
void deleteEmployee(struct Employee *employee)
{
  if (!employee)
    return;
  if (employee->first_names)
    free(employee->first_names);
  if (employee->second_name)
    free(employee->second_name);
  deleteOffice(employee->office);
  deletePhoneNumber(employee->phone);
  deletePhoneNumber(employee->fax);
  if (employee->email)
    free(employee->email);
}

//------------------------------------------------------------
struct Employee *cloneEmployee(struct Employee *employee)
{
// equivalent to the other "clone" functions - I leave this up to you
  return(NULL); // means not implemented :-)
}

//------------------------------------------------------------
void setFirstNamesInEmployee(struct Employee *employee, char *names)
{
// equivalent to the other "set" functions - I leave this up to you
}

//------------------------------------------------------------
void setSecondNameInEmployee(struct Employee *employee,char *name)
{
// equivalent to the other "set" functions - I leave this up to you
}

//------------------------------------------------------------
void setOfficeInEmployee(struct Employee *employee,
                         struct Office *office)
{
// equivalent to the other "set" functions - I leave this up to you
}

//------------------------------------------------------------
void setPhoneNumberInEmployee(struct Employee *employee,
                              struct PhoneNumber *phone_num)
{
// equivalent to the other "set" functions - I leave this up to you
}

//------------------------------------------------------------
void setFaxNumberInEmployee(struct Employee *employee,
                            struct PhoneNumber *fax_num)
{
// equivalent to the other "set" functions - I leave this up to you
}

//------------------------------------------------------------
void setEmailInEmployee(struct Employee *employee,char *email)
{
// equivalent to the other "set" functions - I leave this up to you
}

//------------------------------------------------------------
// helper function if the program runs out of memory
void exitOnFatalErrorOutOfMemory()
{
  printf("Fatal Error: Out of Memory\n");
  exit(-1);
}

//------------------------------------------------------------
// This function returns a clone of the given string. This
// means that it also allocates the memory and returns the
// dynamically allocated  block. Therefore the clone has to be
// freed if no longer needed!
char *cloneString(char* str)
{
  char* ret = NULL;
  if (str)
  {
    if (!(ret = malloc(sizeof(char) * (strlen(str) + 1))))
      exitOnFatalErrorOutOfMemory();
    strcpy(ret,str);
  }
  return(ret);
}
