/*
  DVD Store Application

  Main Header File
  Copyright (c) 2000 Wrox Press
*/

/* Error definitions */
#define DVD_SUCCESS                 0
#define DVD_ERR_NO_FILE            -1
#define DVD_ERR_BAD_TABLE          -2
#define DVD_ERR_NO_MEMBER_TABLE    -3
#define DVD_ERR_BAD_MEMBER_TABLE   -4
#define DVD_ERR_BAD_TITLE_TABLE    -5
#define DVD_ERR_BAD_DISK_TABLE     -6
#define DVD_ERR_BAD_SEEK           -7
#define DVD_ERR_NULL_POINTER       -8
#define DVD_ERR_BAD_WRITE          -9
#define DVD_ERR_BAD_READ          -10
#define DVD_ERR_NOT_FOUND         -11
#define DVD_ERR_NO_MEMORY         -12
#define DVD_ERR_BAD_RENTAL_TABLE  -13
#define DVD_ERR_BAD_RESERVE_TABLE -14
#define DVD_ERR_CORBA_EXCEPTION       -15
/*
APIs

[First rush to allow comments, later this all gets put back in 
Chapter 1 with the blurb about what we are doing (and why) on 
the app front. Actual implementation of these APIs will appear 
with the database in chapter ?3? There may be a flat file 'test bed'
implementation earlier]

For the graphical user interface we need to abstract a number of 
interfaces so that we can have a standard way to accessing data - 
before we know how the underlying data is managed. This is because 
we plan to deal with the data in a later iteration of development.

Let's start by defining the base static data for each of our objects, 
that we could pass around internal to the application. Since we know 
we intend to implement the system in 'C', the easiest way to do 
this is to simply define some C structs.

[Don't confuse these with D/B objects, 3rd normal form rules OK 
in the database - promise!]

All _id fields will have 0 reserved, so that it can be used as a 
sentinel value in arrays.
*/

/*
  Initialise the database
  MUST call dvd_open_db before any other accesses
  and dvd_close_db is polite before exiting
*/

int dvd_open_db(void);
int dvd_close_db(void);

/*
  The member structure
*/

typedef struct _store_member_struct  {
	int member_id; 			/* internal id [1..] */
	char member_no[6];		/* the number the member knows */
	char title[4];			/* Mr Mrs Ms Dr Sir */
	char fname[26];			/* first name */
	char lname[26];			/* last name */
	char house_flat_ref[26];	/* i.e. 5, or 'The birches' etc. */
	char address1[51];		/* Address line 1 */
	char address2[51];		/* Address line 2 */
	char town[51];			/* Town/City */
	char state[3];			/* well the readers are bound to be US based...*/
	char phone[31];			/* +44(0)123 456789 */
	char zipcode[11];		/* LE1 1AA or whatever */
} dvd_store_member;

/*
Notice that we separate the 'member number' that the member actually
knows, from an internal integer that we can generate and use
internally. This will be handy if we ever want to give the system to a
store using alpha-numeric membership codes. We will do the same for
disks and titles.

Here is the information for a DVD title.  
*/

typedef struct _title_struct {
	int title_id;			/* internal ID [1..] */
	char title_text[61];		/* 'The silence of the lambs' */
	char asin[11];			/* 10 digit reference number */
	char director[51];		/* restricted to a single name */
	char genre[21];			/* 'Horror', 'comedy', etc. API for standard list later */
	char classification[11];	/* API for standard list later */
	char actor1[51];		/*  'Jeremy Irons' */
	char actor2[51];		/* 'Ingmar Bergman' */
	char release_date[9];		/* YYYYMMDD plus the null */
	char rental_cost[7];		/* cost of a day rental for this title $$$.cc */
	char image_ref[128];		/* Do we want to do related images? */
} dvd_title;

/*
The information for a DVD disk is much simpler:
*/

typedef struct _disk_struct {
	int disk_id;			/* internal ID [1..] (not related to title_id) */
	int title_id;			/* the title_id of which this is an instance */
} dvd_disk;

/*

All dates will be stored in the format "YYYYMMDD".
All character arrays and strings passed will be null terminated.
enum will be defined for error codes (and 0 for success).
defines (or something) will be declared for fixed size strings.

Before we can consider the APIs for providing our functionality, we
need to make an important decision about how we handle results that
need to return multiple instances of data, for example searching on a
DVD title could return zero, one or many titles. For example a search
on member, that returned 12000 members is going to be roughly 2M of
data. Whilst this is manageable locally, it's not sensible to return
that much data to any sort of remote client in one go. On the other
hand, providing a scrolling mechanism that can cater for multiple
clients scrolling through different sub-sets of different query
results is going to result in a complex API that may not be very
flexible for the future.

Our solution is to decide that our API will simply return all the data
in one hit, and pass the responsibility for catering for remote
clients that might need per-client data management to any application
that need this responsibility.

Let's get the  simple get and set APIs defined first.  In all cases we
use int functions, so we can return 0 for success or a negative number
as  an error  code. [Will  define an  enum set  for the  return codes,
including a  zero for OK later].  Where the result size  is fixed, the
calling  party  is always  responsible  for  memory management,  where
result size is not fixed  the called routine allocates memory with malloc,
and the calling routine is then responsible for freeing it with free.
[Note that search routines return 0 for success, even if this is no data,
all search APIs that return multiple results provide a count output
to indicate the number of items in the returned array.]

[To make this all fall out in multi-user mode, all the APIs are
'single shot' type, so there is no session or content type information
needed in the back end API.]

Search APIs are not very generic, but make life simpler.

[In practice this will be a real pain to code in the back end. 
Need to decide which fields it's sensible to search on and restrict 
searching to those]

Deletion will do cascade deletes, so deleting a title deletes the
disks, rental records [etc. etc. ]

*/

/*
  Member APIs

  To create a new member, pass a partially complete dvd_store_member
  to dvd_member_create. The member_id and member_no fields are not
  required. A freshly allocated member_id is passed back in the output
  parameter member_id. This is used to retrieve the member details
  thereafter. The membership "number" given to the new member (may be
  written on his membership card) is generated and stored in the member_no
  field - in the database. The passed-in structure is not altered by
  dvd_member_create. A typcal use of dvd_member_create would be followed
  by a dvd_member_get to fetch the now-complete member record, indexed
  by the returned (new) member_id.

  Member details can be updated by a dvd_member_get/set pair. Do not
  attempt to alter the member_id field.

  Members are deleted with a call to dvd_member_delete passing a member id.
*/

int dvd_member_set(dvd_store_member *member_record_to_update);
int dvd_member_get(int member_id, dvd_store_member *member_record_to_complete);
int dvd_member_create(dvd_store_member *member_record_to_add, int *member_id);
int dvd_member_delete(int member_id);

/*
  Member search APIs

  To find a member given the membership number (as on member's card)
  call dvd_member_get_id_from_number.

  To find a member given part of a last name use dvd_member_search.
  In this case the results are returned as a count and an array of
  member ids. The array must be deallocated by a call to free().
  The search performs a sub-string match on the lname member field.
*/ 

int dvd_member_search(char *lname, int *result_ids[], int *count);
int dvd_member_get_id_from_number(char *member_no, int *member_id);

/*
  DVD Title APIs

  These APIs function in an entirely similar way to members.
  The dvd_title_search is currently an either-or on substrings
  within film title text(title) and directors/actors(name).
*/

int dvd_title_set(dvd_title *title_record_to_update);
int dvd_title_get(int title_id, dvd_title *title_record_to_complete);
int dvd_title_create(dvd_title *title_record_to_add, int *title_id);
int dvd_title_delete(int title_id);
int dvd_title_search(char *title, char *name, int *result_ids[], int *count);

/*
  Disk APIs

  These APIs are used for manipulating the record of physical
  DVD disks, each of which is an instance of a title.
*/

int dvd_disk_set(dvd_disk *disk_record_to_update);
int dvd_disk_get(int disk_id, dvd_disk *disk_record_to_complete);
int dvd_disk_create(dvd_disk *disk_record_to_add, int *disk_id);
int dvd_disk_delete(int disk_id);
int dvd_disk_search(int title_id, int *result_ids[], int *count);

/*
We also need some utility type APIs, mostly to fetch 'fixed' data from
defining tables in the database. This data is 'read-only', the pointers
returned will address static data areas. They must not be freed by the caller! 
*/
/* return the valid of genres: comedy, thriller etc. */
int dvd_get_genre_list(char **genre_list[], int *count); 
/* return the valid of classes: U, PG etc. */
int dvd_get_classification_list(char **class_list[], int *count);
int dvd_err_text(int err_number, char **message_to_show); /* given error return from our API, get some text */
int dvd_today(char **date); /* get today's date as YYYYMMDD */

/*
Now we have the basic data access defined, we can worry about the
additional APIs we need to support our functionality.

The extra functions we need are:

Do a rental of one or more titles in a single sale, giving a total
cost Check the availability of a title on a particular date.  Do a
return of disks (one at a time is fine, don't care if they were rented
as a set) Reserve a title for a particular date.  Report overdue
returns
*/

/*
 Given a member ID and a title, rent out a disk and return
 a suitable physical disk ID to provide to the customer
*/
int dvd_rent_title(int member_id, int title_id, int *disk_id);

/* Return information about a disk, whether rented out and to whom */
int dvd_rented_disk_info(int disk_id, int *member_id, char *date_rented);

/* Return a disk - give the date it was rented in case it's overdue */
int dvd_disk_return(int disk_id, int *member_id, char *date);

/* Return a list of disks rented after (>=) date1 and before (<) date2 */
/* Null dates mean pre-history and tomorrow */
int dvd_overdue_disks(char *date1, char *date2, int *disk_ids[], int *count);

/* How many copies of a title are available on a given day */
int dvd_title_available(int title_id, char *date, int *count);

/* Make a reservation for a title on a given date */
/* Only one reservation per member at a time - will clobber a previous one */
int dvd_reserve_title(char date[], int title_id, int member_id);

/* Cancel a reservation - do this before renting a reserved title */
int dvd_reserve_title_cancel(int member_id);

/* Given a member, return which title he has reserved */
int dvd_reserve_title_query_by_member(int member_id, int *title_id);

/* 
   Return a list of members with reservations for a particular title on
   a given date
*/
int dvd_reserve_title_query_by_titledate(int title_id, char *date, int *member_ids[]);

/* 
Given a member ID and array of title_ids, return the list of disks to
give out, and the cost. If any title ids are unavailable the whole
call fails. [ignore tax? Adds complexity with no demonstration value.]
*/
/*
  NOT IMPLEMENTED
*/
int dvd_rent_titles(int member_id, int count, int title_ids[], int *disk_ids[], double *cost); 

/*
[Need more reporting APIs defining? Concerned that that's quite a lot
of APIs to implement, and we will get bogged down in an overly complex
application, if we haven't already? The application should just be a
vehicle for examples, not take over the book.]

[Do we need cron type processing? For example, to clear out reservations 
that never got rented? void dvds_do_cron_work(void); perhaps that we can call
from a cron job?]

[For now I've abandoned any idea of multi-store. It's not sensible, 
after all the DVD is in the store you are in or it isn't. If it was 
home delivery that might be different. The RPC/CORBA chapters are just 
going to have to be slightly more hypothetical, but can still sensibly 
demo how to remote the API we feel.]

[Walk some scenarios to check the API?]
*/









