#!/usr/bin/python
""" a simple gui or command line app
to view and create/edit file comments

comments are stored in xattr user.xdg.comment

depends on python-pyxattr

because files are so often over-written, save a copy
of the comments in a database ~/.dirnotes.db

these comments stick to the symlink

"""

import sys,os,argparse
from dirWidget2 import DirWidget
from Tkinter import *
from ttk import *
import xattr, sqlite3, time

VERSION = "0.2"
COMMENT_KEY = "user.xdg.comment"
DATABASE_NAME = "~/.dirnotes.db"
# convert the ~/ form to a fully qualified path
DATABASE_NAME = os.path.expanduser(DATABASE_NAME)

class DataBase:
	''' the database is flat
		fileName: fully qualified name
		st_mtime: a float
		size: a long
		comment: a string
		comment_time: a float, the time of the comment save
		this is effectively a log file, as well as a resource for a restore
			(in case a file-move is done without comment)
		the database is associated with a user, in the $HOME dir
	'''
	def __init__(self):
		'''try to open the database; if not found, create it'''
		try:
			self.db = sqlite3.connect(DATABASE_NAME)
		except sqlite3.OperationalError:
			print("Database %s not found" % DATABASE_NAME)
			raise OperationalError
		c = self.db.cursor()
		try:
			c.execute("select * from dirnotes")
		except sqlite3.OperationalError:
			print("Table %s created" % ("dirnotes"))
			c.execute("create table dirnotes (name TEXT, date DATETIME, size INTEGER, comment TEXT, comment_date DATETIME)")
		
	def getData(self, fileName):
		c = self.db.cursor()
		c.execute("select * from dirnotes where name=? order by comment_date desc",(os.path.abspath(fileName),))
		return c.fetchone()
	def setData(self, fileName, _date, _size, comment):
		c = self.db.cursor()
		c.execute("insert into dirnotes values (?,?,?,?,?)",
			(fileName, _date, _size, comment, time.time()))
		self.db.commit()
		return True
	def log(self, fileName, comment):
		''' TODO: convert filename to canonical '''
		c = self.db.cursor()
		s = os.stat(fileName)
		print ("params: %s %f %d %s %f" % ((os.path.abspath(fileName), s.st_mtime, s.st_size, comment, time.time())))
		c.execute("insert into dirnotes values (?,datetime(?,'unixepoch','localtime'),?,?,datetime(?,'unixepoch','localtime'))",
			(os.path.abspath(fileName), s.st_mtime, s.st_size, str(comment), time.time()))
		self.db.commit()

def parse():
	parser = argparse.ArgumentParser(description='dirnotes application')
	parser.add_argument('dirname',metavar='dirname',type=str,
		help='directory [default=current dir]',default=".",nargs='?')
	parser.add_argument('dirname2',help='comparison directory, shows two-dirs side-by-side',nargs='?')
	parser.add_argument('-n','--nogui',action="store_const",const="1",
		help='use text base interface')
	parser.add_argument('-v','--version',action='version',version='%(prog)s '+VERSION)
	group = parser.add_mutually_exclusive_group()
	group.add_argument('-s','--sort-by-name',metavar='sort',action="store_const",const='n')
	group.add_argument('-m','--sort-by-date',metavar='sort',action='store_const',const='d')
	return parser.parse_args()


class FileObj():
	def __init__(self, fileName):
		self.fileName = fileName
		# also get the date, directory or not, etc
		self.comment = ''
		try:
			self.comment = xattr.getxattr(fileName,COMMENT_KEY)
		except Exception as e:
			#print("comment read on %s failed, execption %s" % (self.fileName,e)) 
			pass
	def getName(self):
		return self.fileName
	def getShortName(self):
		if self.fileName[-1]=='/':	#do show dirs, they can have comments
			return os.path.basename(self.fileName[:-1])+'/'
		else:
			return os.path.basename(self.fileName)
	def getComment(self):
		return self.comment
	def setComment(self,newComment):
		self.comment = newComment
		try:
			xattr.setxattr(self.fileName,COMMENT_KEY,self.comment)
			return True
		# we need to move these cases out to a handler 
		except Exception as e:
			print("problem setting the comment on file %s" % (self.fileName,))
			if os.access(self.fileName, os.W_OK)!=True:
				print("you don't appear to have write permissions on this file")
				# change the listbox background to yellow
				self.displayBox.notifyUnchanged()				
			elif "Errno 95" in str(e):
				print("is this a VFAT or EXFAT volume? these don't allow comments")
			return False

class DirNotes(Frame):
	''' the main window of the app
		has 3 list boxes: dir_left, dir_right (may be invisible) and files
		
		'''
	def __init__(self, parent, filename, db):
		Frame.__init__(self,parent)
		self.db = db

		self.lb = lb = Treeview(self)
		lb['columns'] = ('comment')
		lb.heading('#0',text='Name')
		lb.heading('comment',text='Comment')
		
		# resize the comments column
		# and resize the parent window to match the directory size

		# allow multiple entries on the line at this point
		#d = os.listdir(p.filename[0])
		#d.sort()
		current, dirs, files = os.walk(filename,followlinks=True).next()
		dirs = map(lambda x:x+'/', dirs)
		dirs.sort()
		files.sort()
		
		d = dirs + files

		self.files = []
		for i in range(len(d)):
			this_file = FileObj(current+'/'+d[i])
			self.files = self.files + [this_file]
			lb.insert('','end',iid=str(i),text=this_file.getShortName(),)
			#lb.itemAt(i,0).setFlags(Qt.ItemIsEnabled) #NoItemFlags)
			comment = this_file.getComment()
			lb.set(item=str(i),column='comment',value=comment)

		
		e2 = Label(self,text="View and edit file comments stored in extended attributes user.xdg.comment")
		e1 = Label(self,text="Active Directory:")

		b1 = Button(self,text="restore from database")
		dirLeft = DirWidget(self,current)
		#dirLeft.setMaximumHeight(140)
		#dirLeft.setMaximumWidth(200)
		dirRight = DirWidget(self,current)
		#~ dirRight.setMaximumHeight(140)
		#~ dirRight.setMaximumWidth(200)
		#~ dirRight.setEnabled(False)
		
		
		#~ layout = QVBoxLayout()
		#~ upperLayout = QHBoxLayout()
		#~ layout.addWidget(e)
		#~ upperLayout.addWidget(dirLeft)
		#~ upperLayout.addWidget(b1)
		#~ upperLayout.addWidget(dirRight)
		#~ layout.addLayout(upperLayout)
		#~ layout.addWidget(lb)
		#~ win.setLayout(layout)
		
		#~ lb.itemChanged.connect(self.change)
		#~ b1.pressed.connect(self.restore_from_database)

		#~ QShortcut(QKeySequence("Ctrl+Q"), self, self.close)	
		#~ self.setWindowTitle("test")
		#~ self.setMinimumSize(600,400)
		e1.pack(anchor=W,padx=20)
		dirLeft.pack(anchor=W,padx=20,pady=5)
		e2.pack()
		lb.pack()
	def closeEvent(self,e):
		print("closing")
		
	def change(self,x):
		print("debugging " + x.text() + " r:" + str(x.row()) + " c:" + str(x.column()))
		the_file = dn.files[x.row()]
		r = the_file.setComment(str(x.text())) 
		if r:
			self.db.log(the_file.getName(),x.text())
	def restore_from_database(self):
		print("restore from database")
		fileName = str(self.lb.item(self.lb.currentRow(),0).text())
		fo_row = self.db.getData(fileName)
		if len(fo_row)>1:
			comment = fo_row[3]
			print(fileName,fo_row[0],comment)
		
if __name__=="__main__":
	p = parse()
	if p.dirname[-1]=='/':
		p.dirname = p.dirname[:-1]
	print(p.dirname)
	
	db = DataBase()

	tk_basis = Tk()
	tk_basis.title("DirNotes "+p.dirname)
	dn = DirNotes(tk_basis,p.dirname,db)
	dn.pack()
	
	mainloop()
	
	#xattr.setxattr(filename,COMMENT_KEY,commentText)


''' files from directories
use os.isfile()
os.isdir()
current, dirs, files = os.walk("path").next()
possible set folllowLinks=True'''

''' notes from the wdrm project
table showed 
filename, size, date size, date, desc

at start, fills the list of all the files
skip the . entry
'''

''' should we also do user.xdg.tags="TagA,TagB" ?
user.charset
user.creator=application_name or user.xdg.creator
user.xdg.origin.url
user.xdg.language=[RFC3066/ISO639]
user.xdg.publisher
'''

''' to allow column-sorting, you use the sortByColumn and set the Horiz-header to clickable
'''

''' TODO: also need a way to display-&-restore comments from the database '''

''' QFileDialog
	-make my own?
	-existing one has
		-history
		-back button
		-up button
		-but we don't need
			-directory date
			-icon option
			-url browser (unless we go network file system)
			-new folder button
			-file type chooser
			-text entry box
			-choose & cancel buttons
	
	'''
	
'''
http://stackoverflow.com/questions/18562123/how-to-make-ttk-treeviews-rows-editable
'''