#!/usr/bin/python

from smurf.cookie import cookies
from optparse import OptionParser
from sys import exit,stdin,stdout,stderr
from os import getenv,spawnvp,P_WAIT,getpid,remove,environ

parser = OptionParser(conflict_handler="resolve")
parser.add_option("-h","--help","-?", action="help",
                    help="Print this help message and exit")
parser.add_option("-t", "--type", dest="typ",
                    help="Use this cookie class", default="en")
parser.add_option("-d", "--delim", dest="delim",
                    help="How to delimit cookies", default="%%")
parser.add_option("-i", "--ids", dest="ids", action="store_true",
                    help="Print ID values with the cookies")
parser.add_option("-j", "--pathlen", dest="pathlen", action="store_true",
                    help="Print path length values with the cookies")
parser.add_option("-D", "--drop", dest="drop", action="store_true",
                    help="Delete found cookies")
parser.add_option("-J", "--pathmin", dest="pathmin", action="store", type="int",
                    metavar="MIN", help="Print cookies with unique path longer than MIN")
parser.add_option("-L", "--linemax", dest="maxlines", action="store", type="int",
                    default=5, metavar="MAX", help="Print cookies with at most MAX lines")
parser.add_option("-f", "--file", dest="file", action="store",
                    help="The file to use")
parser.add_option("-m", "--mode", dest="mode", default="one",
                    help="Tell this program what to do:")
parser.add_option("--one", action="store_const", dest="mode", const="one",
					help="get a random cookie, or by <arg> ID numbers")
parser.add_option("--del", action="store_const", dest="mode", const="del",
					help="delete cookie #<arg>")
parser.add_option("--all", action="store_const", dest="mode", const="all",
					help="list all cookies, sorted by ID")
parser.add_option("--count", action="store_const", dest="mode", const="count",
					help="display the number of cookies")
parser.add_option("--store","--add", action="store_const", dest="mode", const="store",
					help="store data from STDIN as one cookie")
parser.add_option("--replace", action="store_const", dest="mode", const="replace",
					help="update cookies from STDIN")
parser.add_option("--import", action="store_const", dest="mode", const="import",
					help="import delimited cookies from STDIN")
parser.add_option("--edit", action="store_const", dest="mode", const="edit",
					help="edit cookie starting with <arg>")
parser.add_option("--edit-all", action="store_const", dest="mode", const="editall",
					help="edit all cookies")
parser.add_option("--id", action="store_const", dest="mode", const="id",
					help="get cookie starting with <arg>")
parser.add_option("--prefix", action="store_const", dest="mode", const="prefix",
					help="get all data starting with <arg>")
parser.add_option("-p","--port", action="store", dest="port", type="int",
					help="create a server which listens on <port>")

(options, args) = parser.parse_args()

db = cookies(options.typ)
drops=[]

printed=0
outfd=stdout
def out(id):
	global printed

	if options.pathmin and db.pathlen(id) < options.pathmin:
		return
	if not printed:
		if options.file is not None:
			global outfd
			outfd=open(options.file,"w")

	if options.drop:
		drops.append(id)
		return
	outs=[]
	if options.delim != "":
		outs.append(options.delim)
	if options.ids:
		outs.append(str(id))
	if options.pathlen:
		outs.append(str(db.pathlen(id)))
	if len(outs) == 1 and options.delim != "" and not printed:
		outs=[]
	if outs:
		print >>outfd, " ".join(outs)
	print >>outfd, db[id]
	printed = 1

def edit(id):
	ed=getenv("EDITOR")
	fn="/tmp/cookie."+str(getpid())
	try:
		open(fn,"w").write(db[id])
		res=spawnvp(P_WAIT, ed, (ed,fn))
		if res == 0:
			data = open(fn,"r").read().rstrip()
			if len(data) > len(db[id])/3:
				del db[id]
				db.save(data, id=id)
				db.commit()
	finally:
		remove(fn)

if options.port is not None:
	import socket
	from SocketServer import TCPServer, StreamRequestHandler, ThreadingMixIn
	from sys import exc_info
	from traceback import print_exception

	class LengthError(ValueError):
		"""data too long"""
		pass
	class server(StreamRequestHandler): # (, ThreadingMixIn):
		def handle(self):
			try:
				for i in range(20):
					try:
						id = db.random()
						data = db[id]
						if len(data.split("\n")) > options.maxlines:
							raise LengthError
						elif len(data) < 5:
							raise LengthError

						try: open("/tmp/cookie.last."+environ['USER']+"."+db.art,"w").write(str(id)+'\n'+data+'\n')
						except: pass
						
						print >>self.wfile, data
						return
					except LengthError: ## too long/short
						continue
					except KeyError: ## not found
						print >>self.wfile,"[Cookie #%d not found]" % (id,)
						continue
					except:
						x,y,z=exc_info()
						print_exception(x,y,z, file=self.wfile)
					return
						# pass
			finally:
				db.abort()
			print >>self.wfile, "* No cookie today *"
	class listener(TCPServer):
		allow_reuse_address = True
	s=listener(("",options.port),server)
	s.serve_forever()

elif options.mode == "one": # get one cookie, random or by ID
	if args:
		printed=0
		for i in args:
			out(int(i))
	else:
		id = db.random()
		out(id)
		try: open("/tmp/cookie.last."+environ['USER']+"."+db.art,"w").write(str(id)+'\n')
		except: pass

elif options.mode == "prefix": # get all cookies starting with this prefix
	pathlen=options.pathmin
	if pathlen is None:
		pathlen=0
	for p in args:
		for id in db.prefix(p,pathlen):
			out(id)

elif options.mode == "all": # print all cookies numerically
	for id in db.cookies():
		out(id)

elif options.mode == "store": # Get one cookie from stdin
	if options.file is None:
		f=stdin
	else:
		f=open(options.file,"r")
	db.save(f.read().rstrip())
	db.commit()

elif options.mode == "import" or \
		options.mode == "replace": # get multiple cookies from stdin
	seq=1
	line=1
	sline=1

	if options.file is None:
		f=stdin
	else:
		f=open(options.file,"r")

	def store_one(c):
		c = c.rstrip()
		if c == "":
			return
		try:
			db.save(c)
		except:
			stderr.write("-"+str(sline)+" ")
		else:
			db.commit()
			stderr.write("+")
	c=""; cn=""
	if options.mode == "replace":
		for id in args:
			id=int(id)
			del db[id]
	else:
		if args:
			print >>stderr,"No arguments allowed"
			exit(1)



	while True:
		l = f.readline()
		if l == "":
			break
		line=line+1
		l = l.rstrip()
		if options.mode == "replace" and \
				l.startswith(options.delim+" "):
			try:
				id=int(l[len(options.delim)+1:])
			except ValueError:
				raise ValueError,"Not a number at line %d: %s" % \
					(line, l[len(options.delim)+1:],)
			del db[id]

			seq = seq + 1
			store_one(c)
			sline=line
			c=""; cn=""
		elif l == options.delim:
			seq = seq + 1
			store_one(c)
			sline=line
			c=""; cn=""
		else:
			c += cn+l
			cn = "\n"

	store_one(c)
	stderr.write(" %d\n" % seq)
	db.commit()

elif options.mode == "edit": # run editor on cookie
	for id in args:
		id=int(id)
		edit(id)

elif options.mode == "editall": # run editor on cookie
	for id in db.__iter__():
		if options.ids:
			print id
		edit(id)

elif options.mode == "id": # find ID of text on stdin
	try:
		print db.id(stdin.read().rstrip())
	except KeyError:
		print >>stderr,"... not found."
	except ValueError:
		print >>stderr,"... not unique."
	
elif options.mode == "count": # count 'em
	print len(db)

elif options.mode == "del": # delete some cookies
	for id in args:
		del db[int(id)]
	db.commit()

else:
	print >>stderr,"Unknown mode (one,all,store,import,id,prefix)"
	exit(1)

if drops:
	for c in drops:
		stderr.write(str(c)+" ")
		del db[c]
	db.commit()
	stderr.write("\n")

#from deb import pp
#pp(options)

