/*
Lab 7(21/2 to 27/2)
MM:25
Model the following application as a set of classes with suitable inheritance
relationships, features and operations. Consider the Central Library,
which has the following kinds of library materials: books, journals, reference books,
textbooks. The library also has different classes of users: UG students, Mtech students,
PhD students, faculty, institute staff, project staff. The table below gives details of
which library materials can be borrowed, for how long and the limit on the maximum number
of items of each type that can be borrowed.
 
    Library
    Material	UG		Mtech		Phd		Faculty		Staff	Prj
    		Time    								Staff
		Max	
Books		14  10		28  10		28 10		6  20		14  5   14  2
Journal					        1  No limit     1  No Limit 
Ref Books	-   - 		-   -   	-  -		-  -		-   -	-   -
Text Books	2hrs 2		2hrs 2		2hrs 2
		
Each user has an issue/return record. Typical operations include issue - issues a library item,return - returns a library item, if the return is after the due date then a fine is charged at
the following rates: book - 20p/day, journal - Re 1/day, textbook - Re 1/hr.
 
Note: The above information does not purport to be an accurate description of our central
library.
*/
class date
{
	private int h,d,m,y;
	
	public date()
	{
		java.util.GregorianCalendar todayDate = new java.util.GregorianCalendar();
		h = todayDate.get(java.util.Calendar.HOUR_OF_DAY);
		d = todayDate.get(java.util.Calendar.DAY_OF_MONTH);
		m = todayDate.get(java.util.Calendar.MONTH)+1;
		y = todayDate.get(java.util.Calendar.YEAR);
	}
	public date(int h, int d, int m, int y){
		this.h = h;
		this.d = d;
		this.m = m;
		this.y = y;
	}
	public int getHour() { return h; }
	public int getDay() { return d; }
	public int getMonth() { return m; }
	public int getYear() { return y; }
	public String toString(){
		String s = d + "/" + m + "/" + y + " Hours = " + h; 
		return s;
	}
	
	//Returns the number of days in the given month of the given year
	private static int Days(int month, int year)
	{
		int x=0;
		switch(month)
		{
			case 1:
			case 3:
			case 5:
			case 7:
			case 8:
			case 10:
			case 12: x = 31;break;
			case 4:
			case 6:
			case 9:
			case 11: x = 30;break;
			case 2: if((year % 4) == 0) x = 29;
				else x = 28;
				break;
		}
		return x;
	}
	
	//Returns the number of hours between the time now and the issue time
	public static int getNumHours(int h, int d, int m, int y)
	{
		int hd,dd,md,yd;
		int hours,days,cm,cy;
		java.util.GregorianCalendar todayDate = new java.util.GregorianCalendar();
		int th = todayDate.get(java.util.Calendar.HOUR_OF_DAY);
		int td = todayDate.get(java.util.Calendar.DAY_OF_MONTH);
		int tm = todayDate.get(java.util.Calendar.MONTH)+1;
		int ty = todayDate.get(java.util.Calendar.YEAR);
		//Get the diff in years. 
		yd = ty - y;
		if (yd < 0) return -1;
		
		//Get the diff in months.
		if (m > tm)
		{
			//If given month exceeds today's month, year must be
			//an earlier year.	
			yd = yd - 1;
			if (yd < 0) return -1;
			md = 12 - m + tm;
		}
		else md = tm - m;
		
		//Get the diff in days.
		if (d > td)
		{
			//If given day_of_month falls after today's day_of_month,
			//either month and/or year must be an earlier one.
			if (md == 0)
			{
				yd = yd - 1;
				if (yd < 0) return -1;
				md = 11;
			}
			else md = md - 1;
			dd = Days(m,y) - d + td;
		}
		else dd = td - d;
		//Get the diff in hours
		if (h > th)
		{
			if (dd == 0)
			{
				if (md == 0)
				{
					yd = yd - 1;
					if (yd < 0) return -1;
					md = 11;
				}
				else md = md - 1;
				dd = Days(m,y) - d + td;    
			}
			dd--;
			hd = 24 - h + th;
		}	
		else hd = th - h;
		
		hours = hd;
		days = dd;
		cm = m;
		cy = y;
		
		while(md > 0)
		{
			days = days + Days(cm,cy);
			cm++;
			if(cm > 12) { cm = 1; cy++; yd--;}
			md--;
		}
		if (yd > 0) days = days + yd*365 + (yd/4);
		hours += 24 * days;
		return hours;
	}
	
	//Returns the number of days between now and issue date
	public static int getNumDays(int d, int m, int y)
	{
		int dd,md,yd;
		int days,cm,cy;
		java.util.GregorianCalendar todayDate = new java.util.GregorianCalendar();
		int td = todayDate.get(java.util.Calendar.DAY_OF_MONTH);
		int tm = todayDate.get(java.util.Calendar.MONTH)+1;
		int ty = todayDate.get(java.util.Calendar.YEAR);
		yd = ty - y;
		if (yd < 0) return -1;
		if (m > tm)
		{
			yd = yd - 1;
			if (yd < 0) return -1;
			md = 12 - m + tm;
		}
		else md = tm - m;	
		if (d > td)
		{
			if (md == 0)
			{
				yd = yd - 1;
				if (yd < 0) return -1;
				md = 11;
			}
			else md = md - 1;
			dd = Days(tm,ty) - d + td;
		}
		else dd = td - d;
		days = dd;
		cm = m;
		cy = y;
		while(md > 0)
		{
			days = days + Days(cm,cy);
			cm++;
			if(cm > 12) { cm = 1; cy++; yd--;}
			md--;
		}
		if (yd > 0) days = days + yd*365 + (yd/4);
		return days;
	}
}
class enums
{
	public static final int GENERALBOOK   = 0;
	public static final int JOURNAL       = 1;
	public static final int REFERENCEBOOK = 2;
	public static final int TEXTBOOK      = 3;
	public static final int UGSTUDENT  = 0;
	public static final int PGSTUDENT  = 1;
	public static final int PHDSTUDENT = 2;
	public static final int FACULTY    = 3;
	public static final int ACADSTAFF  = 4;
	public static final int PROJSTAFF  = 5;
}
abstract class user
{
	protected int id;
	protected String name;
	protected int type;
	
	//General Books
	protected int numgenbooks; // Number of gen books taken by user
	protected int genbooksmax; // Max number of gen books user can borrow 
	protected int genbookstime; // Max time(in days) user can borrow gen books
	
	//Journals
	protected int numjournals; // Number of journals taken by user
	protected int journalsmax; // Max number of journals user can borrow 
	protected int journalstime;// Max time(in days) user can borrow journals
	//Reference Books
	protected int numrefbooks;// Number of Reference Books taken by user
	protected int refbooksmax;// Max number of Reference Books user can borrow 
	protected int refbookstime;// Max time(in days) user can borrow Reference Books
	
	//Text Books
	protected int numtextbooks;// Number of Text Books taken by user
	protected int textbooksmax;// Max number of Text Books user can borrow 
	protected int textbookstime;// Max time(in hours) user can borrow Text Books.
	
	public int getID() { return id; }
	public String getName() { return name; }
	public int getType() { return type; }
	
	//Get and Set For General Books
	public int getNumGenBooks() { return numgenbooks; }
	public void incNumGenBooks() { numgenbooks++; }
	public void decNumGenBooks() { numgenbooks--; }
	public int getGenBooksMax() { return genbooksmax; }
	public int getGenBooksTime() { return genbookstime; }
	//Get and Set For Journals
	public int getNumJournals() { return numjournals; }
	public void incNumJournals() { numjournals++; }
	public void decNumJournals() { numjournals--; }
	public int getJournalsMax() { return journalsmax; }
	public int getJournalsTime() { return journalstime; }
	//Get and Set For Reference Books
	public int getNumRefBooks() { return numrefbooks; }
	public void incNumRefBooks() { numrefbooks++; }
	public void decNumRefBooks() { numrefbooks--; }
	public int getRefBooksMax() { return refbooksmax; }
	public int getRefBooksTime() { return refbookstime; }
	//Get and Set For Text Books
	public int getNumTextBooks() { return numtextbooks; }
	public void incNumTextBooks() { numtextbooks++; }
	public void decNumTextBooks() { numtextbooks--; }
	public int getTextBooksMax() { return textbooksmax; }
	public int getTextBooksTime() { return textbookstime; }
	public String toString(){
		String s = id + " " + name + " " + type + " " + numgenbooks + " " + genbooksmax +
		" " + genbookstime + " " + numjournals + " " + journalsmax + " " + journalstime +
		numrefbooks + " " + refbooksmax + " " + refbookstime + " " + numtextbooks + " " +
		textbooksmax + " " + textbookstime;
		return s;
	}	
}
class ugStudent extends user
{
	protected String rollno;
	
	public ugStudent(int i, String rn, String n)
	{
		id = i;
		name = n;
		type = enums.UGSTUDENT;
		numgenbooks  = 0;
		genbooksmax  = 10;
		genbookstime = 14;//in days
		numjournals  = 0;
		journalsmax  = 0;
		journalstime = 0;
		numrefbooks  = 0;
		refbooksmax  = 0;
		refbookstime = 0;
		numtextbooks  = 0;
		textbooksmax  = 2;
		textbookstime = 2;//in hours
		rollno = rn;
	}
	
	public String toString(){
		String s = super.toString() + rollno;
		return s;
	}
}
class pgStudent extends user
{
	protected String rollno;
	public pgStudent(int i, String rn, String n)
	{
		id            = i;
		name          = n;
		type          = enums.PGSTUDENT;
		numgenbooks   = 0;
		genbooksmax   = 10;
		genbookstime  = 28;//in days
		numjournals   = 0;
		journalsmax   = 0;
		journalstime  = 0;
		numrefbooks   = 0;
		refbooksmax   = 0;
		refbookstime  = 0;
		numtextbooks  = 0;
		textbooksmax  = 2;
		textbookstime = 2;//in hours
		rollno        = rn;
	}
	
	public String toString(){
		String s = super.toString() + rollno;
		return s;
	}
}
class phdStudent extends user
{
	protected String rollno;
	public phdStudent(int i, String rn, String n)
	{
		id = i;
		name = n;
		type = enums.PHDSTUDENT;
		numgenbooks  = 0;
		genbooksmax  = 10;
		genbookstime = 28;
		numjournals  = 0;
		journalsmax  = 100000;//100000 can be considered as No limit. 
		journalstime = 1; //in days
		numrefbooks  = 0;
		refbooksmax  = 0;
		refbookstime = 0;
		numtextbooks  = 0;
		textbooksmax  = 2;
		textbookstime = 2; // in hours
		rollno = rn;
	}
	public String toString(){
		String s = super.toString() + rollno;
		return s;
	}
}
class faculty extends user
{
	faculty(int i, String n)
	{
		id = i;
		name = n;
		type = enums.FACULTY;
		numgenbooks  = 0;
		genbooksmax  = 20;
		genbookstime = 6;//in days
		numjournals  = 0;
		journalsmax  = 100000; //100000 can be considered as No limit. 
		journalstime = 1;
		numrefbooks  = 0;
		refbooksmax  = 0;
		refbookstime = 0;
		numtextbooks  = 0;
		textbooksmax  = 0;
		textbookstime = 0;
	}
}
class acadStaff extends user
{
	acadStaff(int i, String n)
	{
		id = i;
		name = n;
		type = enums.ACADSTAFF;
		numgenbooks  = 0;
		genbooksmax  = 5;
		genbookstime = 14;//in days
		numjournals  = 0;
		journalsmax  = 0;
		journalstime = 0;
		numrefbooks  = 0;
		refbooksmax  = 0;
		refbookstime = 0;
		numtextbooks  = 0;
		textbooksmax  = 0;
		textbookstime = 0;
	}
}
class projStaff extends user
{
	projStaff(int i, String n)
	{
		id = i;
		name = n;
		type = enums.PROJSTAFF;
		numgenbooks  = 0;
		genbooksmax  = 2;
		genbookstime = 14;
		numjournals  = 0;
		journalsmax  = 0;
		journalstime = 0;
		numrefbooks  = 0;
		refbooksmax  = 0;
		refbookstime = 0;
		numtextbooks  = 0;
		textbooksmax  = 0;
		textbookstime = 0;
	}
}
class UserCollection extends java.util.Hashtable
{
	boolean putUser(user u)
	{
		Integer i = new Integer(u.getID());
		user usr = (user)get(i);
		if(usr == null)
		{
			put(i,u);
			return true;
		}
		else return false;
	}
	user getUser(int id)
	{
		Integer i = new Integer(id);
		return (user)get(i);
	}
}
abstract class book
{
	protected int accno;
	protected int callno;
	protected String title;
	protected String authors[];
	protected String publisher;
	protected int year;
	protected double price;
	protected int type;
	protected boolean issued;
	protected boolean reserved;
	protected boolean issuable;
	protected user person;
	protected date issueDate;
	protected double fineRate;
	protected boolean bookLimitExists;
	abstract double getFine();
	public String toString(){
		String sAuth = "";
		for(int i = 0; i < authors.length; i++)
			sAuth += authors[i] + ",";
			
		String s = accno + " " + callno + " " + title + " " + sAuth + " " + publisher +
		" " + year + " " + price + " " +  type + " " + issued + " " + reserved + " " +
		issuable + " " + person + " " + issueDate + " " + fineRate + " " + bookLimitExists;
		return s;
	}
	
	public final int getAccno() { return accno; }
	public final int getCallno() { return callno; }
	public final String getTitle() { return title; }
	public final String[] getAuthors() { return authors; }
	public final String getPublisher() { return publisher; }
	public final int getYear() { return year; }
	public final double getPrice() { return price; }
	public final int getType() { return type; }
	public final user getIssuee()
	{
		if (issued) return person;
		else return null;
	}
	public final user getReservee()
	{
		if (reserved) return person;
		else return null;
	}
	public final date getIssueDate()
	{ 
		if (issued) return issueDate; 
		else return null;
	}
	protected boolean canIssue(user p)
	{
		return true;
	}
	
	//Issue the book to the given user
	protected abstract boolean issue(user p);
	
	//Return the book to library
	protected abstract double returnBook();
	//Reserve a book
	public final boolean reserve(user p)
	{
		if (issued || reserved) return false;
		reserved = true;
		person = p;
		return true;
	}
	
	//UnReserve the book
	public final boolean unreserve()
	{
		if (!reserved) return false;
		reserved = false;
		return true;
	}
}
class genBook extends book
{
	public genBook(int a, int c, String t, String as[], String p, int y, double pr)
	{
		accno = a;
		callno = c;
		title = t;
		authors = as;
		publisher = p;
		year = y;
		price = pr;
		issued = false;
		reserved = false;
		issuable = true;
		fineRate = 0.20;
		type = enums.GENERALBOOK;
		bookLimitExists = true;
	}
	public boolean issue(user p) 
	{
		if (issued || !issuable || reserved) return false;
		if (!canIssue(p)) return false;
		if (bookLimitExists)
		{
			if (p.getNumGenBooks() >= p.getGenBooksMax()) return false;
			p.incNumGenBooks();
		}
		issued = true;
		person = p;
		issueDate = new date();
		return true;
	}
	
	public double returnBook()
	{
		if (!issued) return -1.0;
		double fine = getFine();
		if (bookLimitExists) person.decNumGenBooks();
		issued = false;
		return fine;
	}
	
	protected boolean canIssue(user p)
	{
		return true;
	}
	
	public double getFine()
	{
		double fine = 0.0;
		if (!issued) return -1.0;
		int days = date.getNumDays(issueDate.getDay(),issueDate.getMonth(),issueDate.getYear());
		if (days < 0) return -1.0;
		if ( days > person.getGenBooksTime() )
			fine = fineRate * (days - person.getGenBooksTime());
		return fine;
	}
}
class journal extends book
{
	public journal(int a, int c, String t, String as[], String p, int y, double pr)
	{
		accno = a;
		callno = c;
		title = t;
		authors = as;
		publisher = p;
		year = y;
		price = pr;
		issued = false;
		reserved = false;
		issuable = true;
		fineRate = 1.0;
		type = enums.JOURNAL;
		bookLimitExists = false;
	}
	public boolean issue(user p) 
	{
		if (issued || !issuable || reserved) return false;
		if (!canIssue(p)) return false;
		if (bookLimitExists)
		{
			if (p.getNumJournals() >= p.getJournalsMax()) return false;
			p.incNumJournals();
		}
		issued = true;
		person = p;
		issueDate = new date();
		return true;
	}
	
	public double returnBook()
	{
		if (!issued) return -1.0;
		double fine = getFine();
		if (bookLimitExists) person.decNumJournals();
		issued = false;
		return fine;
	}
	
	protected boolean canIssue(user p)
	{
		if ( (p.getType() == enums.PHDSTUDENT) || (p.getType() == enums.FACULTY) )
			return true;
		else
			return false;
	}
	
	public double getFine()
	{
		double fine = 0.0;
		if (!issued) return -1.0;
		int days = date.getNumDays(issueDate.getDay(),issueDate.getMonth(),issueDate.getYear());
		if (days < 0) return -1.0;
		if ( days > person.getJournalsTime() )
			fine = fineRate * (days - person.getJournalsTime());
		return fine;
	}
}
class refBook extends book
{
	public refBook(int a, int c, String t, String as[], String p, int y, double pr)
	{
		accno = a;
		callno = c;
		title = t;
		authors = as;
		publisher = p;
		year = y;
		price = pr;
		issued = false;
		reserved = false;
		issuable = false;
		fineRate = 0.0;
		type = enums.REFERENCEBOOK;
		bookLimitExists = true;
	}
	public boolean issue(user p) 
	{
		if (issued || !issuable || reserved) return false;
		if (!canIssue(p)) return false;
		if (bookLimitExists)
		{
			if (p.getNumRefBooks() >= p.getRefBooksMax()) return false;
			p.incNumRefBooks();
		}
		issued = true;
		person = p;
		issueDate = new date();
		return true;
	}
	
	public double returnBook()
	{
		if (!issued) return -1.0;
		double fine = getFine();
		if (bookLimitExists) person.decNumRefBooks();
		issued = false;
		return fine;
	}
	
	protected boolean canIssue(user p)
	{
		return true;
	}
	
	public double getFine()
	{
		if (!issued) return -1.0;
		else return 0.0;	
	}
}	
class textBook extends book
{
	public textBook(int a, int c, String t, String as[], String p, int y, double pr)
	{
		accno = a;
		callno = c;
		title = t;
		authors = as;
		publisher = p;
		year = y;
		price = pr;
		issued = false;
		reserved = false;
		issuable = true;
		fineRate = 1.0; // Re 1 per hour
		type = enums.TEXTBOOK;
		bookLimitExists = true;
	}
	public boolean issue(user p) 
	{
		if (issued || !issuable || reserved) return false;
		if (!canIssue(p)) return false;
		if (bookLimitExists)
		{
			if (p.getNumTextBooks() >= p.getTextBooksMax()) return false;
			p.incNumTextBooks();
		}
		issued = true;
		person = p;
		issueDate = new date();
		return true;
	}
	
	public double returnBook()
	{
		if (!issued) return -1.0;
		double fine = getFine();
		if (bookLimitExists) person.decNumTextBooks();
		issued = false;
		return fine;
	}
	
	protected boolean canIssue(user p)
	{
		if ( (p.getType() == enums.UGSTUDENT) || (p.getType() == enums.PGSTUDENT) ||			 (p.getType() == enums.PHDSTUDENT) )
			return true;
		else	
			return false;
	}
	
	public double getFine()
	{
		double fine = 0.0;
		int overtime;
		if (!issued) return -1.0;
		int hours = date.getNumHours(issueDate.getHour(), issueDate.getDay(),
			    issueDate.getMonth(), issueDate.getYear());
		if ( hours > person.getTextBooksTime() )
			fine = fineRate * (hours - person.getTextBooksTime());
		return fine;	
	}
}	
	
class BookCollection extends java.util.Hashtable
{
	public boolean putBook(book bk)
	{
		Integer i = new Integer(bk.getAccno());
		book b = (book)get(i);
		if (b == null)
		{
			put(i,bk);
			return true;
		}
		else return false;
	}
	public book getBook(int acc)
	{
		Integer i = new Integer(acc);
		return (book)get(i);
	}
}
class Lab7
{
	public static void main(String args[]) throws Exception
	{
		UserCollection userdb = new UserCollection();
		System.out.println("Creating User Database....");
		
		user ug = new ugStudent(1,"Y112232","UG");
		System.out.println(userdb.putUser(ug));
		
		user pg = new pgStudent(2,"Y436372","PG");
		System.out.println(userdb.putUser(pg));
		
		user phd = new phdStudent(3,"Y436372","PHD");
		System.out.println(userdb.putUser(phd));
		
		user fac = new faculty(4,"FACULTY");
		System.out.println(userdb.putUser(fac));
		
		user ast = new acadStaff(5,"AS");
		System.out.println(userdb.putUser(ast));
		
		user pst = new projStaff(6,"PS");
		System.out.println(userdb.putUser(pst));
		BookCollection bookdb = new BookCollection();
		System.out.println("Creating Book Database....");
		
		int a = 1;
		int c = 1;
		String t = new String("GENBOOK");
		String as[] = new String[2];
		as[0] = new String("author1");
		as[1] = new String("author2");
		String p = new String("publisher");
		int y = 2001;
		double pr = 80.45;
		book gb = new genBook(a,c,t,as,p,y,pr);
		System.out.println(bookdb.putBook(gb));
		a = 2;
		c = 2;
		t = new String("JOURNAL");
		as = new String[1];
		as[0] = new String("author");
		p = new String("pub");
		y = 1998;
		pr = 20.75;
		book jb = new journal(a,c,t,as,p,y,pr);
		System.out.println(bookdb.putBook(jb));
		a = 3;
		c = 2;
		t = new String("REFBOOK");
		as = new String[1];
		as[0] = new String("a");
		p = new String("p");
		y = 1998;
		pr = 10.05;
		book rb = new refBook(a,c,t,as,p,y,pr);
		System.out.println(bookdb.putBook(rb));
		a = 4;
		c = 3;
		t = new String("TEXTBOOK");
		as = new String[1];
		as[0] = new String("author");
		p = new String("publisher");
		y = 1999;
		pr = 00.75;
		book tb = new textBook(a,c,t,as,p,y,pr);
		System.out.println(bookdb.putBook(tb));
		System.out.println("Issuing a book.....");
		System.out.println(tb.issue(phd));
	
		System.out.println("Returning a book....");
		System.out.println("Fine = " + tb.getFine());
		System.out.println(tb.returnBook());
		System.out.println("Reserving a book....");
		System.out.println(rb.reserve(pg));
		System.out.println("Unreserving a book....");
		System.out.println(rb.unreserve());
	}
}