#! /usr/bin/env python

# This software carries the following license:
#
# Modified BSD license
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Steinar Knutsen

"""NB 1.1
"""
import DCLMatcher, LineInterpreter, tempfile, cPickle, sys, string, time, os

class commands:
	def __init__(self, database):
		self.database = database
		datafile = open(database, 'r')
		self.notes = cPickle.load(datafile)
		datafile.close()
	def exit(self, interp, args):
		"""Save database and exit. Use ^D to exit without save.
		"""
		self.save(interp, args)
		interp.writeline("Exiting.\n")
		raise EOFError
	def list(self, interp, args):
		"""List notes in database.
		"""
		output = ["Notes:"]
		output.append(string.join(self.notes.keys(), ', '))
		output = interp.formatlines(output)
		interp.writelines(output)
	def delete(self, interp, args):
		"""Delete note(s) in database.
		"""
		for arg in args:
			del self.notes[arg]
	def read(self, interp, args):
		"""Read note(s) in database.
		"""
		for arg in args:
			try:
				interp.writelines(self.notes[arg].text)
				LineInterpreter.KeyPressWait()
			except DCLMatcher.AmbiguiousQuery:
				interp.writeline("Error: Ambiguious key %s.\n" % arg)
			except (DCLMatcher.KeyMismatch, KeyError):
				interp.writeline("Error: No note matching %s.\n" % arg)
	def save(self, interp, args):
		"""Save database to disk.
		"""
		interp.writeline("Saving database.\n")
		datafile = open(self.database, 'w')
		cPickle.dump(self.notes, datafile)
		datafile.close()
	def jot(self, interp, args):
		"""Add or overwrite note(s) to/in database.
		"""
		for arg in args:
			note = []
			line = raw_input(': ')+'\n'
			while string.strip(line) != '.':
				note.append(line)
				line = raw_input(': ')+'\n'
			interp.writeline("Adding/Overwriting note.\n")
			try:
				self.notes[arg] = Note(note)
			except DCLMatcher.AmbiguiousKeytable:
				interp.writeline(
					"Error: Would make keytable ambiguious.\n")
			except DCLMatcher.AmbiguiousQuery:
				interp.writeline("Error: Ambiguious key.\n")
	def help(self, interp, args):
		"""Show help texts for commands.
		"""
		if len(args) == 0:
			functions = interp.functiontable.keys()
			interp.writeline(string.join(functions, ', ')+'\n')
		else:
			for arg in args:
				doc = interp.functiontable[arg].__doc__
				doc = map(lambda x: x+'\n', string.split(doc, '\n')[:-1])
				interp.writelines(doc)
	def time(self, interp, args):
		"""Show the timestamp(s) of (a) note(s).
		"""
		for arg in args:
			t = self.notes[arg].timestamp
			key = self.notes.findref(arg).realkey
			t = time.strftime('%A, %Y-%m-%d %H:%M:%S', time.localtime(t))
			interp.writeline("%s: %s\n" % (key, t))
	def vim(self, interp, args):
		"""Edit an existing note with Vim. New notes must be made with jot.
		"""
		for arg in args:
			interp.writeline("Editing %s\n" % self.notes.findref(arg).realkey)
			buffer = self.notes[arg]
			tmpname = tempfile.mktemp()
			tmp = open(tmpname, 'w')
			tmp.writelines(buffer.text)
			tmp.close()
			os.system("vim %s" % tmpname)
			tmp = open(tmpname, 'r')
			buffer.text = tmp.readlines()
			buffer.newtime()
			tmp.close()
			os.unlink(tmpname)

class Note:
	def __init__(self, text):
		self.text = text
		self.timestamp = time.time()
	def newtime(self):
		self.timestamp = time.time()

AmbiguiousCommand = "Error: Ambiguious command."
UnknownCommand = "Error: Unknown command."

def main(commanddict):
	commandtable = DCLMatcher.DCLMatcher(commanddict)
	interp = LineInterpreter.LineInterpreter(commandtable)
	nouserexit = 1
	while nouserexit:
		try:
			interp.mainloop()
		except EOFError:
			nouserexit = 0
		except DCLMatcher.AmbiguiousQuery:
			print AmbiguiousCommand
		except (DCLMatcher.KeyMismatch, KeyError):
			print UnknownCommand
		else:
			nouserexit = 0

if __name__ == '__main__':
	if  len(sys.argv) > 1 and string.lower(sys.argv[1]) == 'init':
		if len(sys.argv) == 3:
			filename = sys.argv[2]
		else:
			filename = '.journal'
		datafile = open(filename, 'w')
		cPickle.dump(DCLMatcher.DCLMatcher({}), datafile)
		datafile.close()
	elif len(sys.argv) == 2 or len(sys.argv) == 1:
		if len(sys.argv) == 2:
			filename == sys.argv[1]
		else:
			filename = '.journal'
		engine = commands(filename)
		commanddict = {
		"exit":	engine.exit,
		"list":	engine.list,
		"delete":	engine.delete,
		"read":	engine.read,
		"save":	engine.save,
		"jot":	engine.jot,
		"help":	engine.help,
		"time":	engine.time,
		"vim":	engine.vim
		}
		main(commanddict)
