#!/bin/env python
# This is a short object orientated information retrieval system - it will
# collect information about a computer magazine collection and index
# the articles inside. 
# (c) 2004 Adam Cripps


#    This file is part of Newmag.
#
#       Newmag is free software; you can redistribute it and/or modify
#	it under the terms of the GNU General Public License as published by
#	the Free Software Foundation; either version 2 of the License, or
#	(at your option) any later version.
#
#	Newmag is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#	GNU General Public License for more details.
#
#	You should have received a copy of the GNU General Public License
#	along with Newmag; if not, write to the Free Software
#	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

# Please see the end of this file for brief developer documentation

import pickle # Will be using cpickle to save all the object
import sys
import newmag_documentation

class MagazineCollection:
	"""This class will instantiate a new collection - this is meant to represent a topic or a subject. 
	For my own personal use, I subscribe to 2 linux magazines and so they will fall in to this collection.
	The name of this object will be determined by the user from the method newcollection."""         
	def search(self):
		"""This will cycle through all instances of articles to find a keyword"""
		my_find = -1 # Assume  that nothing has been found
		searchfor = raw_input("What would you like to search for?")
		for collection_iter in self.titles:
			title_object = collection_iter
			for title_iter in title_object.issuesheld:
				issue_object = title_iter
				for issue_iter in issue_object.articlesheld:
					article_object = issue_iter
					#print article_object.name
					name_find = article_object.name.find(searchfor)
					author_find = article_object.author.find(searchfor)
					keywords_find = article_object.keywords.find(searchfor)
					if my_find <> -1 or author_find <> -1 or keywords_find <> -1:
						print "Search result", article_object.name #, "my_find = ", my_find
						print
						print "Magazine Title:  ", title_object.name
						print "Issue:           ", issue_object.name
						print "Front cover:     ", issue_object.cover
						print
						print

	def export_to_html(self):
		"""This method will export all the data variables to an html file """
		html = raw_input("Filename (.html)?")
		try:
			htmlfile = open(html, 'w')
		except IOError:
			print "that file doesn't exist"
			self.export_to_html(self)
		#htmlfile.write('''Content-type: text/html\n\n''')
		htmlfile.write("""<html>\n<head>\n	<title>""")
		htmlfile.write(self.name)
		htmlfile.write('''</title> \n</head>\n<body>''')
		htmlfile.write("\n<h1>")
		htmlfile.write(self.name)
		htmlfile.write("</h1>")
		# Loop through all instance objects
		counter = 1
		for collection_iter in self.titles:
		# print the headers 
			htmlfile.write("""\n<h2>""")
			htmlfile.write (collection_iter.name)
			htmlfile.write("""</h2>""")
			title_object = collection_iter
			for title_iter in title_object.issuesheld:
				# Need some code in here to put all this info in html#
				htmlfile.write("""\n <p><b>""")
				htmlfile.write (title_iter.name)
				htmlfile.write ("<br />Cover: ")
				htmlfile.write (title_iter.cover)
				htmlfile.write("""</b></p>""")
				issue_object = title_iter
				for issue_iter in issue_object.articlesheld:
					# Need some code to add html info
					htmlfile.write("""\n<p>""")
					htmlfile.write("""<b>(""")
					htmlfile.write(str(counter))
					htmlfile.write(""")</b>""")
					htmlfile.write("""\nTitle: """)
					htmlfile.write(issue_iter.name)
					htmlfile.write("""\n<br />Author: """)
					htmlfile.write(issue_iter.author)
					htmlfile.write("""\n<br />Keywords: """)
					htmlfile.write(issue_iter.keywords)
					htmlfile.write("""\n</p>""")
					counter = counter + 1
		htmlfile.write("\n</body>")
		htmlfile.write("\n</html>")
		htmlfile.close()

	def stats(self):
		"""This method runs statistics on the current object"""
		title_count = 0
		issue_count = 0 
		article_count = 0
		for collection_iter in self.titles:
			title_count = title_count + 1
			for issue_iter in collection_iter.issuesheld:
				issue_count = issue_count + 1
				for article_iter in issue_iter.articlesheld:
					article_count = article_count + 1
		print "There are ", title_count, " titles\n"
		print "and ", issue_count, " issues\n"
		print "and ", article_count, " articles."
		return

	def newtitle(self):
		"""This method is called from the Magazine Collection menu. The user specified that they want a new title and this is their chance to name one. """
		newtitlename = raw_input("What is the title of the magazine?")
		atitle = Title(newtitlename)
		print "This New magazine title is ", atitle.name
		#atitle.savetitle() # Will save the atitle
		self.titles.append(atitle) # titles is a list attached to this object
		# which holds all the other subclasses from Title
		#print self.title 
                
	def __init__(self, name):
		self.name = name
		self.titles = [ ]
		#print self.name # Just a debug to check the name is being passed correctly.
		#print "it did get here"
        
	def addnewissue(self):
		"""This method will present the user with all the title objects for this collection object and ask which one they want to work with This is now defunct. """
		itnum = 1
		print '''Which magazine do you wish to add a new title to?'''
		for i in self.titles:
			print "Choice", itnum, "Title:", i.name
			itnum = itnum + 1
		choice = raw_input("Please enter your choice")
		choice = int(choice) # Cast it to integer
		itnum = 1
		for i in self.titles:
			#print "itnum is: ", itnum, "choice is: ", choice, "i is :", i.name
			#print "itnum is type:", type(itnum), "choice type is: ", type(choice)

			if choice == itnum:
				#Shoot off to add something to that object
				i.addissue() # go off to Title and add an issue
			else:
				itnum = itnum +1
				print "your choice was not recognised - please try again"
			self.addnewissue()
		self.issuesmenu()

	def add_to_existing_issue(self):
		'''This will allow user to chose an issue and enter a new issue
		into that article. We'll loop through the objects, presenting 
		each one as an option within a menu and then collect some
		input from the user which will identify that object, right
		down to the issue level'''
		count = 0
		# Loop through the titles in this collection
		for titles_iterator in self.titles:
			print "(", count,") ", titles_iterator.name
			count = count + 1
		response = raw_input("First Which one would you like to add to? ")
		int_response = int(response)
		count = 0
		# Make the comparison between the response and the particular title
		int_break = 0 # This will flag whether we've found the one we need
		for titles_iterator in self.titles:
			if int_response == count:
				# We need to check for the issue
				print "Second Which one would you like to add to?"
				new_count = 0
				for issue_iterator in titles_iterator.issuesheld: 
					print "(", count,") ", issue_iterator.name
				issue_response = raw_input("?")
				int_issue_response = int(issue_response)
				#And now we need to loop round to compare choice with objects
				issue_count = 0
				for issue_iterator in titles_iterator.issuesheld:
					if int_issue_response == issue_count:
						issue_iterator.addnewarticle() ######## We're gonna add an article to this issue
						# Somehow I need to jump out of this loop
						# otherwise the program continues with the second which one would you like
						break # My first ever use of break! 
					else: 
						issue_count = issue_count + 1
				break # This break is vital - if we don't have it, we loop round looking for more issues	
			else:
				count = count + 1
		print "I didn't understand your response! Please try again"
		#self.add_to_existing_issue()
	
	def printdetails (self, chosen_article):
		print "Name :", chosen_article.name
		print "Author :", chosen_article.author
		print "Keywords: ", chosen_article.keywords
		return
	
	def return_object_list(self,level):
		list_returned=[]
		# print "list_returned is ", type(list_returned)
		"""Unlike browse, this function will return a list of objects which can then be acted upon"""
		if level == 0:
			level= raw_input("""What would you like to act upon? (Title (t), Issue (i), Article (a)? 
You may want to choose issue if you want to delete an article from that issue. """)
		if level == "t" or level == "i" or level == "a":
			if level == "t":
				#object_returned = chosen_title_object
				for i in self.titles: # The user knows what they want - take each title and stuff it
					list_returned.append(i) # in to the list
				return (list_returned)
			elif level == "i" or level =="a": 
				chosen_title_object = self.choosetitle() # We need to select a title for the object or article
				if level == "i": # OK they want an issue-go through that title and enter each issue in to the list
					for i in chosen_title_object.issuesheld:
						list_returned.append(i)
					return(list_returned)
				elif level== "a":
					chosen_article_object = self.chooseissue(chosen_title_object)
					for i in chosen_article_object.articlesheld:
						list_returned.append(i)
					return (list_returned)
				if level == "i" or level == "a":
					#chosen_issue_object = self.choosetitle(chosen_title_object)
					object_returned.append(chosen_issue_object)
				if level == "i":
					list_returned = chosen_issue_object
					return (list_returned)
				if level == "a" :
					chosen_article_object = self.choosearticle(chosen_issue_object)
					list_returned = chosen_article_object
					return (list_returned)
		else:
			print "I did not recognise your choice, please try again"
			return
					
	def browse(self,level):
		"""This will quickly browse through the catalogue to particular parts - I don't see this
		as being something that will be here forever; rather this is useful to test some of the latest
		code"""
		if level == 0 :
			level = raw_input("""What is it you want to look for? (Title (t), Issue(i) or Article (a)) """)
		if level == "t" or level == "i" or level == "a":
			chosen_title = self.choosetitle()
			if level == "i" or level == "a":
				chosen_issue = self.chooseissue(chosen_title)
				if level == "a":
					chosen_article = self.choosearticle(chosen_issue)
					return (chosen_article)
				return (chosen_issue)
			return (chosen_title)
	
	def choosetitle(self):
		"""This method allows the user to choose a title that they want to work with
        """
		iter = 0
		for i in self.titles: # This loop simply displays all the options - nothing spectial here
			print iter, ":", i.name
			iter = iter + 1
		choice = raw_input("What is your choice?")
		intchoice = int(choice)
		if intchoice > iter: # Have they chosen more than the options? 
			# print "You've chosen an incorrect choice - please try again"
			self.choosetitle().titlemenu
		else: # This little loop goes through and selects the one to run titlemenu on.
			iter_count = 0
			for iter in self.titles:
				if intchoice == iter_count:
					print "choosetitle is saying that iter is type :", type(iter)
					return iter
					#iter.titlemenu()	# Deprecated - why not just return a title, so that this can be reused for lots of things?
				iter_count = iter_count +1 
       				 

	def chooseissue(self, title): # Let's pass a title so we know which title to look through
		"""This function will take a title and will check which issue of that title we want to use"""
		iter = 0
		for i in title.issuesheld: # I need to change this reference to titles
			print iter, ":", i.name
			iter = iter + 1
		choice = raw_input("What is title do you chose?")
		intchoice = int(choice)
		if intchoice > iter:
			print "You've chosen an incorrect choice - please try again"
			self.choose.article() # This won't work
		else: # This little loop goes through and selects the one to run titlemenu on. 
			iter_count = 0 
			for issue_iter in title.issuesheld: # This will fail too 
				if intchoice == iter_count:
					return issue_iter
					#return issue_iter
				iter_count = iter_count + 1


	def choosearticle(self, issue): # An issue will be passed
		"""This method allows the user to navigate through all the data round down to an article, 
		which could be deleted, or whatever. It will return a list of articles in a particular issue"""
		new_returned_list = []
		iter = 0
		#print "this is articlesheld - what does the data look like?"
		for i in issue.articlesheld: # this should be the correct reference
			print iter, ":", i.name
			iter = iter + 1
		choice = raw_input("What title do you chose?")
		intchoice = int(choice)
		if intchoice > iter:
			print "You've chosen an incorrect choice - please try again"
			self.choosearticle(issue) # By including issue here, we should restart with issue object
		else: # This little loop goes through and selects the one to run titlemenu on. 
			iter_count = 0 
			for iter in issue.articlesheld:
				if intchoice == iter_count:
					print iter.name, " chosen."
					return (iter) # This iter is an article from the issue
				iter_count = iter_count + 1


	# This next method will allow the user to choose what they want to edit, and 
	# uses a similar algorithm as the browse, but returns a list
	
	def return_a_list(self, level):
		"""this is an attempt to try an dresolve the deleting issues""" 
		if level == 0:
			level = raw_input("What would you like to search for? a - article / i - issue / t - title ")
		else: 
			if level == "t" or level == "i" or level == "a": # Only doing a at the moment
				print " I got this far"
				chosen_title_object = self.choosetitle()
				chosen_issue_object = self.chooseissue(chosen_title_object)
				chosen_article_object = self.choosearticle(chosen_issue_object)
				returned_list = [chosen_issue_object.articlesheld, chosen_article_object]
				#print returned_list
				return [returned_list]
						
		
	def choose_what_to_edit(self):
		# New attempt to try this one now
		level = 0 
		print " I got to choose_what_to_edit"
		
		returned_list = self.return_a_list("a")
		print returned_list 
		raw_input("OK?")
		confirm = raw_input("Do you wish to delete this article? (y/n)")
		if confirm == "y":
			print "will be deleted"
			for list, object in returned_list:
				list.remove(object)
				
		else: 
			print " will not be deleted "


	def newdelete(list_to_delete):
		"""This function will have another stab at deleting an item, by fetching
		the list information of where the object is actually held - here goes!"""
		for val, lst in list_to_delete:
			lst.remove(val)
			
		return (list_to_delete)
		

	def collectionmenu(self):
		"""The user has chosen to create a new collection.
        This is their initial choice set. """
		print ''' 
	#################################################
	# Collection Menu                               #
	#################################################
	
	
	What would you like to do? 
	(a)  Add a new magazine
	(b)  Browse and edit
	(c)  Add an issue to a title
	(ac) Add an article to an already entered issue
	(d)	Delete an article
	(s) Run stats on the collection
	(l)  Leave this collection and go back to main menu
        '''
		response = raw_input()
		if response == "a":
			self.newtitle()
			self.collectionmenu() # Return back to this menu
		elif response =="b":
			self.choose_what_to_edit()
		elif response == "c":
			self.choosetitle().titlemenu()
		elif response =="ac":
			self.add_to_existing_issue()
		elif response =="d":
			print "instance of self is ", self # Debug
			self.deletearticle() # OK - this is it - we're going to delete stuff! 
			self.collectionmenu() # Return back to this menu
		elif response == "s":
			self.stats()
		elif response == "l":
			confirm = raw_input("Really leave? (y/n)") # Confirm of quit
			if confirm == "y":
                #self.savecollection()#  - don't save it here - it could be an issue
                # Need to call the save from the level above
				return() ##### This is causing a problem when adding a new article after already adding one
		elif confirm == "n":
			self.collectionmenu() # Loop back to the mainmenu
		else: 
			print ("I didn't recognise your response")
			self.collectionmenu() 
            
	def changename():
		"""This will allow you to change the name of any particular object.
		"""
		newname = raw_input("What would you like the issue to be called?")
		self.name = newname
    # This is the last line of the Class MagazineCollection
    
class Title: 
    """This class is a subclass of Magazine Collection and holds
    the title of a magazine - in my example, Linux Format. Any
    instances of this class takes it's name from the newtitle method.
    """
    def __init__(self, titlename):
        self.name = titlename
        self.issuesheld = [ ] # Will hold the issue objects - there's inconsistency here - later I refer to issuesheld (s)

    def viewissue(self):
        iter = 1
        for i in self.issuesheld:
            print iter, "Issue: ", i.name
        self.titlemenu()
        
    def titlemenu(self):
        print """
	#########################################
	# Title Menu                            #
	#########################################
	
	
	What would you like to do?
        (a) Add an issue to this title
        (v) View issues that this title has
        (d) Delete issues that this title has (not yet implemented)
        """
        response = raw_input()
        if response == "a":
            self.addissue()
        elif response == "v":
            self.viewissue()
        #elif response == "d": # Must go on the TODO list
        #    self.deleteissue()
        else: 
            print "I'm sorry, I didn't understand your response - please try again"
            self.titlemenu()
            
    def savetitle(self):
        """This function will save the collection as a set of objects"""
        file = raw_input("What is the filename?")

        myfile  = open(file, 'w')


        pickle.dump(self, myfile) # This self seems to refer to an Issue 
        # this is abug - it should be a collection
        print "finished"
    
    def addissue(self):
        """This will add an issue to the Title object
        """
        # They've selected this from the MagazineCollection class and have
        # selected a title object  
        print "you chose ", self.name
        issuename = raw_input("What is the date of the issue?")
        issuecover = raw_input("What is on the cover?")
        issueinstance = Issue(issuename, issuecover)
        self.issuesheld.append(issueinstance)
        # self.collectionmenu()
        # OK - this will have to create a list for each of the Titles and
        # add Issue objects into that list - issues will eventually
        # hold articles

class Issue:
    """This class will create the instance of each issue - issues will hold 
    articles which will contain the data. 
    """
    def __init__(self,issuename, issuecover):
        self.name = issuename
        self.articlesheld = [ ] # Will hold all the articles for this issue
        self.cover = issuecover # What appears on the cover - this is handy for 
        # finding issues
        #print "On this cover you will find..>", self.cover
        self.issuemenu() # The user has created a new issue (from Title) 
        #- now time to take them to the issue menu
        
    def issuechangename():
        print "The issue is currently entitled:", self.name
        self.changename()
    
    def addnewarticle(self):
        newarticle = raw_input("What is the title of the article?")
        aninstance = Article(newarticle)
        self.articlesheld.append(aninstance)
        self.issuemenu()
        
    def viewarticles(self):
        for i in self.articlesheld:
            print i.name
        self.issuemenu()

    def issuemenu(self):    
        print """
	#################################
	# Issues Menu                   #
	#################################
	
	What would you like to do?
        (a) add article
        (v) view articles
        (d) delete an article
		
        (g) Go back to Magazine menu
        """ # (d) delete article - not yet implemented - although it has started
        response = raw_input()
        if response == "a":
            self.addnewarticle()
        elif response == "v":
            self.viewarticles()
        elif response == "d":
            self.deletearticles()
        elif response =="g":
            #self.collectionmenu() # Go back to a higher level of menu
            # Currently throws an error due to this menu expecting a Title object
            # rather than an issue object. 
            return() # Using a break here instead of the code above takes you back to entry menu
            
        else:
            print "I did not understand your response"
            self.issuemenu()
            
class Article:
	"""This is a subclass of Issue and creates the actual article information."""

	def __init__(self, articlename):
		self.name = articlename
		articleinformation = [ ]
		self.collectinformation(articleinformation)

	def collectinformation(self, articleinformation):
		self.author = raw_input("What is the name of the author?")
		self.keywords = raw_input("Any keywords?")
        #articleinformation.append(author)
        #articleinformation.append(keywords)

def savecollection(aninstance):
    """This will save the collection and all it's sub objects"""
    filename = raw_input("Filename?")
    file = open (filename, 'w')
    pickle.dump(aninstance, file) 
    print "Finished"
    return()

def newcollection():
    """New collection was called from the first menu that the
    user was presented with. 
    """
    name = raw_input("What is the collection of magazines called?")
    aninstance = MagazineCollection(name)
    print "The object's name is ", aninstance.name
    aninstance.collectionmenu() # This calls the method for the menu for this 
    # magazine collection object.    
    # I've returned now - and does the user want to save? 
    #print "Ok, I've returned and self is ", aninstance.name
    save(aninstance)
    
def save(aninstance):
    response = raw_input("Do you want to save y/n?")
    if response == "n":
        mainmenu()
    elif response == "y":
        savecollection(aninstance)
        mainmenu()
    else: 
        print "I didn't understand your response - please try again"
        save(aninstance)
    
#def search(instance): # I'm pretty sure these are deprecated
#    print "you chose to search", instance.name
#def edit(instance):
#    print "you chose to edit", instance.name



def edit_or_search(aninstance):
	"""This function determines whether the user searches or edits"""
	response = raw_input("""Would you like to edit or search?
    (e) Edit
    (s) Search
    (b) Browse the catalogue
    (h) Export to HTML
    (sa) Save data
    (x) Exit program
	""")
	if response == "e":
		aninstance.collectionmenu()
		save(aninstance)
	elif response == "s":
		aninstance.search()
		mainmenu()
	elif response =="b":
		level =0 # Set this to zero, as the level of operating hasn't yet been set.
		aninstance.browse(level)
	elif response == "h":
		aninstance.export_to_html()
	elif response =="x":
		sure = raw_input("Are you sure?(y/n)")
		if sure == "y":
			print "bye"
			sys.exit(0)
	elif response == "sa":
		save(aninstance)
	else:
		print "Your response was not recognised - please try again"
	edit_or_search(aninstance)
        

def loadcollection():
    """This function will load a file which is a cpickled object of a 
    magazine collection. 
    """
    filename = raw_input("What is the filename?")
    try:

        file = open (filename, 'r')
    except IOError:
        print "That file was not found!" 
        loadcollection()
    instance = pickle.load(file)
    #instance.collectionmenu() This used to take you to the instances menu
    # but now we are going to a new edit/search menu  
    edit_or_search(instance)

def load_existing_collection(file):
	#print type (file)
	#print file
	try:
		openfile = open(file, 'r')
		instance = pickle.load(openfile)
		edit_or_search(instance)
	except IOError:
		print "That file doesn't exist, please try again"
		loadcollection()
	#instance = pickle.load(openfile)
	#edit_or_search(instance)


def mainmenu():
	"""This method is the main entry point method that the user will first see.
	It asks them whether they want to create a new collection, or load 
	an existing one"""

	if len(sys.argv) >1:
		filepassed = str(sys.argv[1])
	
		load_existing_collection(filepassed)
		

	else:
		print '''
	###################################
	#  Main Menu                      #
	###################################


	What would you like to do? 
	(n) Create a new collection
	(l) Load a collection
	(r) Read documentation
	(e) Exit program
		'''
		response = raw_input()
		if response == "n":
			newcollection()
		elif response == "l":
			loadcollection()
    #elif response == "s": 
    #    if aninstance: # Does a collection exist already for saving?
    #        savecollection(aninstance)
    #    else: 
    #        print"Sorry - you don't have anything to save"
		elif response =="e":
			confirm = raw_input("Really quit? (y/n)") # Confirm of quit
			if confirm == "y":
				return	
			elif confirm == "n":
				mainmenu() # Loop back to the mainmenu
			else:
				print "I'm sorry I didn't understand your response"
				mainmenu()
		elif response =="r":
			newmag_documentation.start()
			mainmenu()
		else:
			print "I didn't recognise your response, please try again"
			mainmenu()
        
mainmenu() # Kicks the whole thing off

# #############################################################
#                                                             #
#                Documentation                                #
#                                                             #
# #############################################################


# When items in the list are resolved, they are moved to the changelog to show that
# that issue has been addressed - this is kind of satisfying and is a main 
# motivator

# Couple of outstanding issues with this program:

# TODO

# When browsing articles, if there are no instances of titles, issues or articles, then 
# an empty choice is presented, which the user will never get out of without error
# Bug - when the collectionmenu is spawned more than once, leaving it proves difficult
# View by particular groups (magazine, issue, titles etc.)
# Create media package to avoid the large download every time. 
# Create a media package to avoid downloads
# Create a help module which opens up a browser with html content
# Have to fix deletearticles as I now have an unwanted article in the file
# Bug - When adding a new magazine title, the user has to add an article
# Bug arises when choosing integer for choice, if no answer is given at all
# Bug -once you leave this issue, it asks you to save, but then throws back to 
# edit-or-save  function ... the only way to exit is to export
# this might not actually be a bug if I put an exit at that menu - you've
# already specified what file you want to work with. 
# Edit data? No provision for this yet
# Sorting on certain data sets?
# Date validation for issue dates
# Exporting to HTML jumps straight out of the program
# Current Search is jumping out of program on exit
# Delete articles
# When asking for collection, input is on same line as query
# Use cpickle instead of pickle
# Add more meta-information to html export

# Changelog

# Now adds a number to each article when exporting to html
# How about passing filename as a command line parameter to start the whole shebang?
# Develop function to view everything and possibly export to HTML
# Bug - when entering articles and then pressing 'g' to go back, then adding another issue
# a bug is filed - AttributeError: Issue instance has no attribute 'titles'
# Need to be able to add articles to already added issues
# Need to break out of loop in add_to_existing_issue
# Spelling on view articles on titles menu (at least I think it's titles menu)
# Program now allows saving if loaded at beginning
# Search now jumps back to the mainmenu
# Search searches all data of an article now.
# Choosing which title is now correct regardless
# Bug - search doesn't seem to be working now


# Navigation


#|issuechangename|
#|return_object_list|
#|choosetitle|  |chooseissue|  |choosearticle|
#|choose_what_to_edit| 





#
#	MagazineCollection - filename
#	Title - name of the magazine
#	Issue - date of publication
#	Article - title of article
#
#
#
#
#
