#!/usr/bin/env python 
#import CORBA, sys, string, random, time, bsddb, os 
import CORBA, sys, string, random, time, anydbm, os 
from string import split, strip, joinfields
from random import randint
from time import localtime, strftime, time
###  Connect Associative arrays to Files
SHDISKS=anydbm.open("disks.db", 'c')
SHMEMBERS=anydbm.open("members.db", 'c')
SHRENTALS=anydbm.open("rentals.db", 'c')
SHRESERVATIONS=anydbm.open("reservations.db", 'c')
SHTITLES=anydbm.open("titles.db", 'c')

class setupCounters:
    def maxforall(self):
	self.maxfordisks()
	self.maxformembers()
	self.maxfortitles()
    def maxfordisks(self):
	if DBMAX.has_key("disks"):
	    max = DBMAX["disks"]
	else:
	    max = 1
	try:
	    i=SHDISKS.first()
	    while i != None:
		iint = string.atoi(i)
		if iint > max:
		    max=iint+1
		i=SHDISKS.next(i)
	except:
	    DBMAX["disks"] = max
    def maxformembers(self):
	if DBMAX.has_key("members"):
	    max = DBMAX["members"]
	else:
	    max = 1    
	try:
	    i=SHMEMBERS.first()
	    while i != None:
		iint = string.atoi(i)
		if iint > max:
		    max=iint+1
		i=SHMEMBERS.next(i)
	except:
	    DBMAX["members"] = max
    def maxfortitles(self):
	if DBMAX.has_key("titles"):
	    max = DBMAX["titles"]
	else:
	    max = 1
	try:
	    i=SHTITLES.first()
	    while i != None:
		iint = string.atoi(i)
		if iint > max:
		    max=iint+1
		i=SHTITLES.next(i)
	except:
	    DBMAX["titles"] = max

### Now, for utilities...
def idtostring (id):
    return "%d" % id

uname = os.uname()
hostname = uname[1]
def logit(type, info):
    try:
	LOGORB.addlog(LOG.loginfo(hostname=hostname, 
				  userid="%d" % os.getuid(),
				  application="dvd-server",
				  messagetype=type,
				  shortmessage=info))
    except: 
	print "logging server broken!"
### Initialization of non-ORB stuff...
FACTORYOBJECT = {}
DBMAX = {}
ERRNDICT = {
     0 : "DVD_SUCCESS"             ,
    -1 : "DVD_ERR_NO_FILE"         ,
    -2 : "DVD_ERR_BAD_TABLE"       ,
    -3 : "DVD_ERR_NO_MEMBER_TABLE" ,
    -4 : "DVD_ERR_BAD_MEMBER_TABLE",
    -5 : "DVD_ERR_BAD_TITLE_TABLE" ,
    -6 : "DVD_ERR_BAD_DISK_TABLE"  ,
    -7 : "DVD_ERR_BAD_SEEK"        ,
    -8 : "DVD_ERR_NULL_POINTER"    ,
    -9 : "DVD_ERR_BAD_WRITE"       ,
    -10 : "DVD_ERR_BAD_READ"       ,
    -11 : "DVD_ERR_NOT_FOUND"      ,
    -12 : "DVD_ERR_NO_MEMORY"      ,
    -13 : "DVD_ERR_BAD_RENTAL_TABLE" ,
    -14 : "DVD_ERR_BAD_RESERVE_TABLE" }

ERRMSGDICT = {
    "DVD_SUCCESS" : "no error",
    "DVD_ERR_NO_FILE" : "cannot open file",
    "DVD_ERR_BAD_TABLE" : "corrupt table file",
    "DVD_ERR_NO_MEMBER_TABLE" : "no member table",
    "DVD_ERR_BAD_MEMBER_TABLE" : "corrupt member table",
    "DVD_ERR_BAD_TITLE_TABLE" : "corrupt title table",
    "DVD_ERR_BAD_DISK_TABLE" : "corrupt disk table",
    "DVD_ERR_BAD_RENTAL_TABLE" : "corrupt rental table",
    "DVD_ERR_BAD_RESERVE_TABLE" : "corrupt reserve table",
    "DVD_ERR_BAD_SEEK" : "cannot seek in file",
    "DVD_ERR_NULL_POINTER" : "null data pointer",
    "DVD_ERR_BAD_WRITE" : "cannot write to file",
    "DVD_ERR_BAD_READ" : "cannot read file",
    "DVD_ERR_NOT_FOUND" : "no match found",
    "DVD_ERR_NO_MEMORY" : "out of memory"}


SETUP=setupCounters()
SETUP.maxforall()

class Factory:
    def generateFactory(self, name, instance):
        new_instance = instance
        poa.activate_object(new_instance)
        FACTORYOBJECT[name] = poa.servant_to_reference(new_instance)
        return FACTORYOBJECT[name]
    def DISKSFactory(self):
        if FACTORYOBJECT.has_key("disks"):
            return FACTORYOBJECT["disks"]
        else:
            logit("Factory", "Create DISKS Interface")
            return self.generateFactory("disks", POA.DVD.DISKS(Disks()))
    def UTILITIESFactory(self):
        if FACTORYOBJECT.has_key("utilities"):
            return FACTORYOBJECT["utilities"]
        else:
            logit("Factory", "Create utilities interface")
            return self.generateFactory("utilities",
                                        POA.DVD.UTILITIES(Utilities())) 
    def MEMBERSHIPFactory(self):
        if FACTORYOBJECT.has_key("membership"):
            return FACTORYOBJECT["membership"]
        else:
            logit("Factory", "Create membership interface")
            return self.generateFactory("membership",
                                        POA.DVD.MEMBERSHIP(Membership())) 
    def TITLINGFactory(self):
        if FACTORYOBJECT.has_key("titling"):
            return FACTORYOBJECT["titling"]
        else:
            logit("Factory", "Create titling interface")
            return self.generateFactory("titling",
                                        POA.DVD.TITLING(Titling())) 
    def DISKSFactory(self):
        if FACTORYOBJECT.has_key("disks"):
            return FACTORYOBJECT["disks"]
        else:
            logit("Factory", "Create disks interface")
            return self.generateFactory("disks",
                                        POA.DVD.DISKS(Disks())) 
    def RENTALFactory(self):
        if FACTORYOBJECT.has_key("rental"):
            return FACTORYOBJECT["rental"]
        else:
            logit("Factory", "Create rental interface")
            return self.generateFactory("rental",
                                        POA.DVD.RENTAL(Rental())) 
    def RESERVATIONSFactory(self):
        if FACTORYOBJECT.has_key("reservations"):
            return FACTORYOBJECT["reservations"]
        else:
            logit("Factory", "Create reservations interface")
            return self.generateFactory("reservations",
                                        POA.DVD.RESERVATIONS(Reservations())) 

### Functions to pack/unpack the DBM file information
def destringizereservationinfo(sres):
    rout=DVD.RESERVATIONS.reservation
    (mbr, ttl, dd)=string.split(sres, "\\")
    rout.memberid=string.atoi(mbr)
    rout.titleid=string.atoi(ttl)
    rout.dwanted=dd
    return rout
def stringizereservation(res):
    return string.join(("%d"%res.memberid, "%d"%res.titleid, res.dwanted), "\\")
def destringizerentinfo(srental):
    rout=DVD.RENTAL.rentinfo
    (dsk, mbr, dd) = string.split(srental, "\\")
    rout.diskid=string.atoi(dsk)
    rout.memberid=string.atoi(mbr)
    rout.drented=dd
    return rout
def stringizerentinfo(rrec):
    return string.join(("%d"%rrec.diskid, "%d"%rrec.memberid, rrec.drented), "\\")
def destringizedisk(sdisk):
    disk=DVD.DISKS.dvddisks()
    (sd, st) = string.split(sdisk, "\\")
    disk.diskid = string.atoi(sd)
    disk.titleid = string.atoi(st)
    return disk
def stringizedisk(disk):
    return string.join(("%d"%disk.diskid, "%d"%disk.titleid), "\\")
def stringizetitle(title):
    return string.join(("%d" % title.titleid, title.titletext, title.asin, title.director, title.genre, title.classification, title.actor1, title.actor2, title.releasedate, title.rentalcost, title.image), "\\")
def destringizetitle(stitle):
    title=DVD.TITLING.dvdtitles()
    (mttl, title.titletext, title.asin, title.director, title.genre, title.classification, title.actor1, title.actor2, title.releasedate, title.rentalcost, title.image) = string.split(stitle, "\\")
    title.titleid=string.atoi(mttl)
    return title
	
def stringizemember(member):
    return string.join(("%d" % member.memberid, member.memberno,
			member.title, member.fname, member.lname,
			member.houseflatref, member.address1,
			member.address2, member.town, member.state,
			member.phone, member.zipcode), "\\")
def destringizemember(smember):
    member=DVD.MEMBERSHIP.storemembers()
    (mid, member.memberno, member.title, member.fname, member.lname,
     member.houseflatref, member.address1, member.address2,
     member.town, member.state, member.phone, member.zipcode) = string.split(smember, "\\")  
    member.memberid=string.atoi(mid)
    return member

class Membership:
    def set (self, recordtoupdate):
	logit("Membership", "Set contents for %d" %
	      recordtoupdate.memberid) 
	SHMEMBERS[idtostring(recordtoupdate.memberid)]= stringizemember(recordtoupdate)
	SHMEMBERS.sync()
    def get (self, memberid):
	try:
	    record=SHMEMBERS[idtostring(memberid)]
	except:
	    logit("Membership", "Failure of get() contents for member %d" % memberid)
	    print "Couldn't get member", memberid
	    raise DVD.MEMBERSHIP.NOSUCHMEMBER
	logit("Membership", "Success of get() contents for member %d"
	      % memberid) 
	return destringizemember(record)
    def delete (self, memberid):
	try:
	    del SHMEMBERS[idtostring(memberid)]
	    logit("Membership", "delete contents for %d" % memberid)
	    SHMEMBERS.sync()
	except:
	    raise DVD.MEMBERSHIP.NOSUCHMEMBER
    def create (self, recordtoadd):
	lastid = DBMAX["members"]
	lastid = lastid + 1
	logit("Membership", "Create new member record - %d" % lastid)
	DBMAX["members"] = lastid
	recordtoadd.memberid = lastid
	recordtoadd.memberno = "%d" % lastid
	SHMEMBERS[idtostring(lastid)]=stringizemember(recordtoadd)
	SHMEMBERS.sync()
	logit("Membership", "Create new member  for %d" % lastid)
	return lastid
    def search (self, lname):
	rseq = []
	try:
	    (key,value)=SHMEMBERS.first()
	    while 1 == 1:
		lst=string.split(value, "\\")
		surname=lst[4]
		if string.upper(surname) == string.upper(lname):
		    rseq.append (string.atoi(key))
		(key,value)=SHMEMBERS.next()
	except:
	    done = ""
	logit("Membership", "Search for %s" % lname)
	rseq.sort()
	return rseq
    def idfromnumber (self, memberno):
	logit("Membership", "id-to-number for %s" % memberno)
	try:
	    (key,value)=SHMEMBERS.first()
	    while 1 == 1:
		lst = string.split(value, "\\")
		no = lst[1]
		if no == memberno:
		    return string.atoi(key)
		(key,value) = SHMEMBERS.next()
	except:
	    raise DVD.MEMBERSHIP.NOSUCHMEMBER
class Titling:
    def set (self, recordtoupdate):
	try:
	    SHTITLES[idtostring(recordtoupdate.titleid)]= stringizetitle(recordtoupdate) 
	    SHTITLES.sync()
	except:
	    logit("TITLING", "Failure of set() contents for title %d"
		  % recordtoupdate.titleid) 
	logit("TITLING", "Success of set() contents for title %d" %
	      recordtoupdate.titleid) 
    def get (self, titleid):
	try:
	    record=SHTITLES[idtostring(titleid)]
	except:
	    logit("TITLING", "failure of get() contents for title %d"
		  % titleid) 
	    raise DVD.TITLING.NOSUCHTITLE
	logit("TITLING", "Success of get() contents for title %d" % titleid)
	return destringizetitle(record)
    def delete (self, titleid):
	try:
	    del SHTITLES[idtostring(titleid)]
	    SHTITLES.sync()
	    logit("Titling", "delete contents for %d" % titleid)
	except:
	    raise DVD.TITLING.NOSUCHTITLE
    def create (self, recordtoadd):
	lastid = DBMAX["titles"]
	lastid = lastid + 1
	logit("Titling", "Create new title record - %d" % lastid)
	DBMAX["titles"] = lastid
	recordtoadd.memberid = lastid
	SHTITLES[idtostring(lastid)]=stringizetitle(recordtoadd)
	SHTITLES.sync()
	return lastid
    def search (self, title, name):
	rseq = []
	try:
	    (key,value)=SHTITLES.first()
	    while 1 == 1:
		lst=string.split(value, "\\")
		titletext=lst[1]
		director=lst[3]
		actor1=lst[6]
		actor2=lst[7]
		keep="NO"
		if string.upper(director) == string.upper(name):
		    keep = "YES"
		if string.upper(actor1) == string.upper(name):
		    keep = "YES"
		if string.upper(actor2) == string.upper(name):
		    keep = "YES"
		if string.upper(titletext) == string.upper(title):
		    keep = "YES"
		if keep == "YES":
		    rseq.append (string.atoi(key))
		(key,value)=SHTITLES.next()
	except:
	    done = ""
	logit("Titling", "Search for %s" % title)
	#rseq.sort(lambda x, y: x-y)
	rseq.sort()
	return rseq
class Disks:
    def set (self, recordtoupdate):
	try:
	    SHDISKS[idtostring(recordtoupdate.diskid)]= stringizedisk(recordtoupdate)
	    SHDISKS.sync()
	except:
	    logit("DISKS", "Failure to set %d" % recordtoupdate.diskid)
	logit("DISKS", "Success of set() contents for disk %d" % recordtoupdate.diskid)
    def get (self, diskid):
	try:
	    record=SHDISKS[idtostring(diskid)]
	except:
	    logit("DISKS", "Failure of get() contents for disk %d" % diskid)
	    raise DVD.DISKS.NOSUCHDISK
	logit("DISKS", "Success of get() contents for disk %d" % diskid)
	ds=destringizedisk(record)
	print "Disk: ", ds.diskid, ds.titleid
	return ds
    def create (self, recordtoadd):
	lastid = DBMAX["disks"]
	lastid = lastid + 1
	logit("DISKS", "Create new disk record - %d" % lastid)
	DBMAX["disks"] = lastid
	recordtoadd.diskid = lastid
	SHDISKS[idtostring(lastid)]=stringizedisk(recordtoadd)
	SHDISKS.sync()
	logit("DISKS", "Create new disk for %d" % lastid)
	return lastid
    def delete (self, diskid):
	try:
	    del SHDISKS[idtostring(diskid)]
	    SHDISKS.sync()
	    logit("Disks", "delete contents for %d" % diskid)
	except:
	    raise DVD.DISKS.NOSUCHDISK
    def search (self, titleid):
	rseq = []
	try:
	    (key,value)= SHDISKS.first()
	    while 1 == 1:
		lst=string.split(value, "\\")
		if string.atoi(lst[1]) == titleid:
		    rseq.append (string.atoi(key))
		(key,value)= SHDISKS.next()
	except:
	    done = ""
	logit("DISKS", "Search for %s" % titleid)
	rseq.sort(lambda x, y: x-y)
	return rseq
class Rental:
    def set(self, disk, rentinfo):
	SHRENTALS[idtostring(disk)] = stringizerentinfo(rentinfo)
       # Powinno to obsługiwać wyjątki, lecz na razie tak się nie dzieje.
       # Korzystają z tego tylko inne funkcje wewnątrz serwera,
       # zaś kontrola błędów jest dokonywana wewnątrz nich.
    def get(self, disk):
	try:
	    record=SHRENTALS[idtostring(disk)]
	except:
	    logit("DISKS", "Failure of get() contents for disk %d" % disk)
	    raise DVD.DISKS.NOSUCHDISK
	logit("DISKS", "Success of get() contents for disk %d" % disk)
	return destringizerentinfo(record)
    def renttitle (self, memberid, titleid):
	MEM= FACTORYOBJECT["membership"]
	TTL= FACTORYOBJECT["titling"]
	DSK= FACTORYOBJECT["disks"]
	RNT= FACTORYOBJECT["rentals"]
	RES= FACTORYOBJECT["reservations"]
	try:
	    mbr=MEM.get(memberid)
	except:
	    raise DVD.MEMBERSHIP.NOSUCHMEMBER
	try:
	    ttl=TTL.get(titleid)
	except:
	    raise DVD.TITLING.NOSUCHTITLE
        ### Jeżeli zachowujemy informację o klasyfikacji dozwolonej 
        ### dla każdego klienta wypożyczalni, to możemy przetwarzać 
        ### DVD.RENTAL.FORBIDDENRATING w tym miejscu...
	dlist = DSK.search(titleid)  # Pobranie listy płyt...
        # Następnie sprawdzenie dostępności pozycji z listy
	availabledisk = 0
	for d in dlist:
            # Sprawdzenie wypożyczenia
            #    Jeśli wypożyczona, to kontynuacja
	    try:
		r=RNT.get(d)
		if r.datec != "":
		    skip="Y"
	    except: 
	         skip="N"
            # Wyszukiwanie rezerwacji tytułu
            # Jeśli zarezerwowany i pasuje ID klienta, to mamy wynik
	    if skip == "N":
	        try:
		    r=RES.get(d)
		    if (r.memberid == memberid) and (r.titleid == titleid):
		        founddisk = d
	        except: pass
            # Jeśli wszystko OK, to ustawiamy dostępność płyty i przerwa
	    if skip != "Y":
	        founddisk = d
	        break
        # Teraz wypożyczenie płyty...
	try:
	    logit("Rental", "rentdiskinfo - disk %d - failed" % diskid)
	    rrec=DVD.Rental.rentinfo(diskid=founddisk, memberid=memberid, 
		drented="20000801")
	    SRENTAL[idtostring(founddisk)]= stringizerentinfo(rrec)
	    logit("Rental", "rentdisk - disk %d member %d" % (founddisk, memberid))
            # Jeżeli klient zarezerwował ten tytuł, to anulowanie
            # tej operacji teraz...
	except:
	    logit("Rental", "rentdiskinfo - failed for %d" % titleid)
	    raise DVD.DISKS.NOSUCHDISK
	try:
	    rtitle = RES.queryreservationbymember(memberid)
	    if rtitle == titleid:
		RES.cancelreservation(memberid)
	except:
	    raise DVD.MEMBERSHIP.NOSUCHMEMBER
	return founddisk
    # Zwraca ID płyty, jeśli istnieje...
    def rentdiskinfo (self, diskid):
        print "Finish RENTAL::rentdiskinfo()"
	try:
	    rtl=destringizerentinfo(SRENTAL[idtostring(diskid)])
	    mbr=rtl.memberid
	    dt=rtl.drented
            logit("Rental", "rentdiskinfo - disk %d - Found member %d date %s" % (diskid, mbr, returndate))
	except:
	    logit("Rental", "rentdiskinfo - disk %d - failed" % diskid)
	    raise DVD.DISKS.NOSUCHDISK
	return mbr, dt
    def diskreturn (self, diskid, returndate):
	try:
	    dsk= destringizerentinfo(SRENTAL[idtostring(diskid)])
	    del SRENTAL[idtostring(diskid)]
            logit("Rental", "Return disk %d %s" % (diskid, returndate))
	    return dsk.memberid
        except:
	    raise DVD.DISKS.NOSUCHDISK
    def titleavailable (self, titleid, date):
        print "titleavailable not yet used, so no need..."
	return [2, 3, 4] 
    def overduedisks (self, fromdate, todate):
	print "overduedisks not yet used, so no need..."
        return []

class Reservations:
    def set(self, mid, reserve):
	SHRESERVATIONS[idtostring(mid)] = stringizerentinfo(reserve)
    def get(self, mid):
	try:
	    record= SHRESERVATIONS[idtostring(mid)]
	except:
	    logit("RESERVATIONS", "Failure of get() contents for reservation %d" % mid)
	    raise DVD.RESERVATION.NOSUCHRESERVATION
	logit("RESERVATIONS", "Success of get() contents for reservation %d" % mid)
	return destringizereservationinfo(record)
    def reservetitle (self, needed, titleid, memberid):
        print "Finish RESERVATIONS::reservetitle()"
	#returns nothing
	#raise DVD.TITLING.NOSUCHTITLE
	#raise DVD.MEMBERSHIP.NOSUCHMEMBER
	#raise DVD.RENTAL.FORBIDDENRATING
    def cancelreservation (self, memberid):
        print "Finish RESERVATIONS::cancelreservation()"
	#returns nothing
	#raise DVD.MEMBERSHIP.NOSUCHMEMBER
    def queryreservationbymember (self, memberid):
        print "Finish RESERVATIONS::queryreservationbymember()"
	#returns titleid
	#raise DVD.MEMBERSHIP.NOSUCHMEMBER
	return 1
    def queryreservationbytitle (self, titleid, date): 
	print "Not used, so not yet implemented..."
        print "Finish RESERVATIONS::reservetitle()"

class Utilities:
    def getclassifications(self):
	logit("Utilities", "Query Classifications ")
        return ["E", "U", "PG", "12", "15", "18", "XXX"]
    def getgenres(self):
	logit("Utilities", "Query Genres")
        return ["Action", "Education", "Comedy", "Thriller",
        "Foreign", "Romance", "Science Fiction"]
    def errortext(self, errnumber):
	logit("Utilities", "Get Errnum for %d" % errnumber)
        # This uses dictionaries defined below...
        try: 
            errname=ERRNDICT[errnumber]
            errmsg=ERRMSGDICT[errname]
        except:
            errmsg="Unknown error type: %d " % errnumber
        return errmsg
    def today(self):
        return strftime("%Y%m%d", localtime(time()))

# Najpierw dokonamy rozbioru IDL. Dzięki temu uzyskamy relacje
# między operatorami zdefiniowanymi w IDL a klasami i metodami
# języka Python.
CORBA.load_idl("dvdc.idl")
CORBA.load_idl("logger.idl")
# Inicjacja ORBit i podłączenie do Portable Object Adaptor
orb = CORBA.ORB_init((), CORBA.ORB_ID)
poa = orb.resolve_initial_references("RootPOA")
# Teraz próba uzyskania odwołania do obiektu przechowywanego
# w LOGORB, dzięki czemu połaczymy się z serwerem obsługującym logi
try:
    logior = open("./logger.ior").readline()
    LOGORB = orb.string_to_object(logior)
    print LOGORB.__repo_id
    LOGORB.addlog(LOG.loginfo(hostname="knuth", userid="cbbrowne",
			      application="dvd-server",
			      messagetype="info",
			      shortmessage="Start up DVD Server"))
except:
    print "Could not open Logger!"
# Ustawienie usługobiorcy, który uruchomi usługi serwera DVD
servant = POA.DVD.FACTORY(Factory())
poa.activate_object(servant)
# Następnie potrzebujemy odniesienia do tego obiektu, aby przekazać je
# do innych procesów, które mogłyby skorzystać z jego usług.
# IOR jest zapisywany w pliku aby udostępnić sposób dostępu
# do serwera.
ref = poa.servant_to_reference(servant)
open("./dvd-server.ior", "w").write(orb.object_to_string(ref))
print "Starting up DVD Server"
# Ponieważ odniesienie jest już opublikowane, to uaktywniamy POA, 
# a potem zezwalamy, by ORBit zaczął działać, uruchamiając pętlę
poa.the_POAManager.activate()
orb.run()
