#! /usr/bin/env python
###############################################################################
#                                                                             #
#   Copyright 2005 University of Cambridge Computer Laboratory.               #
#                                                                             #
#   This file is part of Nprobe.                                              #
#                                                                             #
#   Nprobe 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.                                       #
#                                                                             #
#   Nprobe 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 Nprobe; if not, write to the Free Software                     #
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA #
#                                                                             #
###############################################################################


#############################################################################
## 
##
## Hardcopy np_plot
## 
##
## 

############################################################################

from Tkinter import *
import os
import sys
from signal import *
import string
from np_widgets import Dialogue, BetterEntry
import np_plot
import tempfile
import commands
import types
from warnings import filterwarnings

##############################################################################
##############################################################################

LPR_MONO_DEF = 'lpr -Ppalm'
LPR_COL_DEF = 'lpr -Ppalm'

##############################################################################
##############################################################################

#
# Hardcopy file already exists - over print, cancel, or save as other?
#

class OverPrint(Dialogue):
    
    def body(self, master):

	#print 'overprint'
	
	startrow = 0

	if self.msg:
	    Label(master, text=self.msg, bg='grey', fg='red').grid(row=0, columnspan = 2)
	    startrow = 1

	self.ents = []
	if self.entries:
	    for ent in self.entries:
		Label(master, text=ent[0], bg='grey').grid(row=startrow)
	
		e = BetterEntry(master)
		e.configure(bg='grey', width = len(ent[1]))
	
		e.grid(row=startrow, column=1)
		e.insert(INSERT, ent[1])
		self.ents.append(e)
	    return self.ents[0]
    
    def apply(self):
	if self.results != None:
	    for e in self.ents:
		self.results.append(e.get())

##############################################################################
##############################################################################

class PrintParams:
    pass

##############################################################################
##############################################################################

class PrintControl(Toplevel):

    def __init__(self, master, plot=None, params=None, title=None):

	Toplevel.__init__(self, master)
	self.transient(master)
	
	if title:
	    self.title(title)
	    
	self.master = master
	if params:
	    self.params = params
	if plot:
	    self.plot = plot
	    self.canv = plot.canvasses[plot.canv_curr]
	    self.make_params()
	
	self.configure(bg='grey')
	
	body = Frame(self, bg='grey')
	self.initial_focus = self.body(body)
	body.pack(padx=5, pady=5)
	body.configure(bg='grey')
	
	self.grab_set()
	
	if not self.initial_focus:
	    self.initial_focus = self
	    
	self.protocol("WM_DELETE_WINDOW", self.cancel)

	ypos = master.winfo_rooty()/2
	
	#self.geometry("+%d+%d" % (master.winfo_rootx(),ypos))
	self.initial_focus.focus_set()
	self.wait_window(self)


    def body(self, master):

	p = self.params
	self.set_ents = []
	row = 0

	entwi = max([len(p.title), len(p.xlabel), len(p.ylabel), len(p.lpr), 
		     len(p.file)])
	for set in p.sets:
	    entwi = max([entwi, len(set.path)])
	
	# Title entry
	Label(master, text='Title', bg='grey').grid(row=row, column=0, 
						    sticky=W)
	self.title_entry = BetterEntry(master)
	self.title_entry.configure(bg='grey', width=entwi)
	self.title_entry.grid(row=row, column=1, columnspan=6)
	self.title_entry.insert(INSERT, p.title)
	row = row + 1

	# Space
	Label(master, text='', bg='grey').grid(row=row, column=0)
	row = row + 1
	
	# Xlabel entry
	Label(master, text='x label', bg='grey').grid(row=row, column=0, 
						      sticky=W)
	self.xlabel_entry = BetterEntry(master)
	self.xlabel_entry.configure(bg='grey', width=entwi)
	self.xlabel_entry.grid(row=row, column=1, columnspan=5)
	self.xlabel_entry.insert(INSERT, p.xlabel)
	row = row + 1
	
	# Ylabel entry
	Label(master, text='y label', bg='grey').grid(row=row, column=0, 
						      sticky=W)
	self.ylabel_entry = BetterEntry(master)
	self.ylabel_entry.configure(bg='grey', width=entwi)
	self.ylabel_entry.grid(row=row, column=1, columnspan=5)
	self.ylabel_entry.insert(INSERT, p.ylabel)
	row = row + 1

	# Individual data set labels
	i = 0
	for set in p.sets:
	    Label(master, text='Plot %d' % (i), 
		  bg='grey').grid(row=row, column=0, sticky=W)
	    entry = BetterEntry(master)
	    entry.configure(bg='grey', width=entwi)
	    entry.grid(row=row, column=1, columnspan=5)
	    entry.insert(INSERT, set.path)
	    if set.hidden[-1]:
		entry.configure(state=DISABLED, fg='grey56')
	    self.set_ents.append(entry)
	    i = i + 1
	    row = row + 1

	# Space
	Label(master, text='', bg='grey').grid(row=row, column=0, sticky=W)
	row = row + 1
	
	# Hardcopy selector
	Label(master, text='Hardcopy', bg='grey').grid(row=row, column=0, 
						       sticky=W)
	self.dohard = IntVar()
	b = Checkbutton(master, text='Print', variable=self.dohard, bg='grey', 
			activeforeground='red', indicatoron=0, 
			activebackground='grey', selectcolor='grey', 
			command=self.hbcb)
	b.grid(row=row, column=1)
	
	#Label(master, text='  Lpr', bg='grey').grid(row=row, column=2)
	# Colour/mono print selector
	self.printmode = StringVar()
	self.printmode.set('mono')
	self.monoprint = Radiobutton(master, text='Mono', variable=self.printmode, 
		    value='mono', command=self.set_printmode, bg='grey',
		    indicatoron=0, selectcolor='grey')
	self.monoprint.grid(row=row, column=3, sticky=E)

	self.colprint = Radiobutton(master, text=' Col ', variable=self.printmode, value='col', 
		    command=self.set_printmode, bg='grey',
		    indicatoron=0, selectcolor='red')
	self.colprint.grid(row=row, column=4)

	# Lpr command
	self.lpr_entry = BetterEntry(master)
	self.lpr_entry.configure(bg='grey')
	self.lpr_entry.grid(row=row, column=5, columnspan=2, sticky=W)
	self.lpr_entry.insert(INSERT, p.lpr)

	row = row + 1
	
	# Save postscript selector
	Label(master, text='Postscript', bg='grey').grid(row=row, column=0, 
						       sticky=W)
	self.dosave = IntVar()
	b = Checkbutton(master, text='Save', variable=self.dosave, bg='grey', 
			onval=1, offval=0, indicatoron=0, 
			activebackground='grey',
			activeforeground='blue', selectcolor='grey', 
			command=self.sbcb)
	b.grid(row=row, column=1)
	
	Label(master, text='  File', bg='grey').grid(row=row, column=2)
	self.file_entry = BetterEntry(master)
	self.file_entry.configure(bg='grey', width=entwi)
	self.file_entry.grid(row=row, column=3, columnspan=4)
	self.file_entry.insert(INSERT, p.file)
	row = row + 1
	
	# Save raw data selector (only if not stand alone plotter
	self.dodata = IntVar()
	self.dodata.set(0)
	l = Label(master, text='Raw data', bg='grey')
	b = Checkbutton(master, text='Save', variable=self.dodata, 
			bg='grey', 
			indicatoron=0, 
			activebackground='grey',
			activeforeground='blue', selectcolor='grey', 
			command=self.dsbcb)

	L = Label(master, text='  File', bg='grey')
	e = self.datafile_entry = BetterEntry(master)
	self.datafile_entry.configure(bg='grey', width=entwi)

        basedir = os.path.dirname(self.strip_dot(self.plot.path))
        basename = os.path.basename(self.plot.path)
        if self.plot.title:
            dee = os.path.join(basedir, self.plot.title)
        else:
            dee = os.path.join(basedir, basename)
        if len(self.plot.sets) > 1:
            dee += '.*'
	self.datafile_entry.insert(INSERT, dee)
	# only show if appropriate
	#if not self.plot.raw_data:
	if 1:
	    l.grid(row=row, column=0, sticky=W)
	    b.grid(row=row, column=1)
	    L.grid(row=row, column=2)
	    e.grid(row=row, column=3, columnspan=4)
	    row = row + 1

	# Space
	Label(master, text='', bg='grey').grid(row=row, column=0, sticky=W)
	row = row + 1

	# OK/Cancel buttons
	self.okb = Button(master, text='  OK  ', bg='grey', fg='grey56', 
	       command = self.ok, state=DISABLED)
	self.okb.grid(row=row, column=0, columnspan=2)

	Button(master, text='Cancel', bg='grey', 
	       command=self.cancel).grid(row=row, column=2, columnspan=2)

	# Initialise
	self.dohard.set(0)
	self.dohard2 = 1
	self.hbcb() # Initialise disabled state
	self.dosave.set(0)
	self.dosave2 = 1
	self.sbcb() # Initialise disabled state
	
	self.dodata.set(0)
	self.dodata2 = 1
	self.dsbcb() # Initialise disabled state
	

	return self.title_entry
	     
##############################################################################

    def set_printmode(self):

	mode = self.printmode.get()
	if mode == 'mono':
	    self.params.lpr = LPR_MONO_DEF
	    self.lpr_entry.delete(0, INSERT)
	    self.lpr_entry.insert(INSERT, LPR_MONO_DEF)
	else:
	    self.params.lpr = LPR_COL_DEF
	    self.lpr_entry.delete(0, INSERT)
	    self.lpr_entry.insert(INSERT, LPR_COL_DEF)
	     
##############################################################################

    def hbcb(self):

	#
	# IntVar associated with button does not work in all circumstances
	# - patch by toggling normal variable, retain IntVar for button control
	#

	if self.dohard2 == 0:
	    self.dohard2 = 1
	else:
	    self.dohard2 = 0
	self.dohard.set(self.dohard2)

	val = self.dohard.get()
	if val == 0:
	    self.lpr_entry.configure(state=DISABLED, fg='grey56')
	    self.monoprint.configure(state=DISABLED, fg='grey56')
	    self.colprint.configure(state=DISABLED, fg='grey56', 
				    selectcolor='grey')
	else:
	    self.lpr_entry.configure(state=NORMAL, fg='black')
	    self.monoprint.configure(state=NORMAL, fg='black')
	    self.colprint.configure(state=NORMAL, fg='black', 
				    selectcolor='red')

	self.set_okb()
	    
	     
##############################################################################

    def sbcb(self):

	if self.dosave2 == 0:
	    self.dosave2 = 1
	else:
	    self.dosave2 = 0
	self.dosave.set(self.dosave2)


	val = self.dosave.get()
	if val == 0:
	    self.file_entry.configure(state=DISABLED, fg='grey56')
	else:
	    self.file_entry.configure(state=NORMAL, fg='black')

	self.set_okb()
	    
	     
##############################################################################

    def dsbcb(self):

	if self.dodata2 == 0:
	    self.dodata2 = 1
	else:
	    self.dodata2 = 0
	self.dodata.set(self.dodata2)

	val = self.dodata.get()
	if val == 0:
	    self.datafile_entry.configure(state=DISABLED, fg='grey56')
	else:
	    self.datafile_entry.configure(state=NORMAL, fg='black')

	self.set_okb()
	    
	     
##############################################################################

    def set_okb(self):

	if self.dohard.get() or self.dosave.get() or self.dodata.get():
	    self.okb.configure(fg='black', state=NORMAL)
	else:
	    self.okb.configure(fg='grey56', state=DISABLED)
	    
	     
##############################################################################


    def save_data(self):

        
        def canstr(s):

            #
            # escape newlines in labels before saving to file
            #

            return string.replace(s, '\n', '\\n')
            


	def strip_spaces(nm):
	    tmp = ''
	    for c in nm:
		if c == ' ':
		    tmp +=  '-'
		else:
		    tmp += c

	    return tmp

	base = self.datafile_entry.get()
        dir = os.path.dirname(base)
	#pre = string.split(base, '*')[0]
        print 'base =', base
        #print 'pre =', pre
        print 'dir', dir
        try:
            post = string.split(base, '*')[1] + '.'
        except IndexError:
            post = ''

        print 'post =', post

	for s in self.plot.sets:
            # how many data fields to save
            nf = 0
            for f in s.data[0]:
                if type(f) in \
                       [types.IntType, types.LongType, types.FloatType]:
                    nf += 1
                else:
                    break
            print 's.path =', s.path
            fnm = os.path.join(dir, post + os.path.basename(s.path))
	    self.data_nm = strip_spaces(fnm)
            if self.data_nm[-1] == '/':
               self.data_nm = self.data_nm[:-1]
	    doit = self.check_file(self, 'data_nm', self.datafile_entry)
	    if doit:
                try:
                    f = open(self.data_nm, 'w')
                except IOError, s:
                    print 'Can\'t save:'
                    print s
                    return
                f.write('#plot-title=%s\n' % (canstr(self.plot.title)))
                f.write('#plot-xlab=%s\n' % (canstr(self.plot.xlab)))
                f.write('#plot-ylab=%s\n' % (canstr(self.plot.ylab)))
                
                for d in self.plot.decs:
                    f.write('#plot-dec=%d:%s:%s:%s\n' \
                            % (d.type, repr(d.coords), d.col, d.label))
		for d in s.data:
		    #f.write('%f\t%f\n' % (d[0], d[1]))
		    f.write('%f' % (d[0]))
                    for v in d[1:nf]:
                        f.write('\t%f' % (v))
                    f.write('\n')
                print 'writing data file %s' % (self.data_nm)
		f.close()
	     
##############################################################################

    def cancel(self, event=None):
	self.master.focus_set()
	self.destroy()
	     
##############################################################################

    def ok(self, event=None):

	dosave = 0
	dohard = 0
	dodata = 0

	self.fnm = None

	p = self.params
	p.title = self.title_entry.get()
	p.xlabel = self.xlabel_entry.get()
	p.ylabel = self.ylabel_entry.get()
	i = 0
	for e in self.set_ents:
	   p.sets[i].path = e.get()
	   i = i + 1
	p.lpr = self.lpr_entry.get()
	p.file = self.file_entry.get()

	#print 'dosave = %d' % ( self.dosave.get())
	if self.dosave.get():
	    #dosave = self.check_file()
	    dosave = self.check_file(self.params, 'file', self.file_entry)
	    if dosave:
		self.fnm = self.params.file
		print 'fn=%s' % (self.params.file)
	#print dosave
	#print 'dosave = %d' % (dosave)

	#print 'dohard = %d' % ( self.dohard.get())
	tmpfile = 0
	if self.dohard.get():
	    dohard = 1
	    if not self.fnm:
		self.fnm = tempfile.mktemp()
		tmpfile = 1

	if dosave or dohard:
	    self.set_plot_params()
	    self.tmp_plot()
	    
	if dohard:
	    self.hardcopy()
	    if tmpfile:
		os.remove(self.fnm)

	dodata = self.dodata.get()
	if dodata:
	    self.save_data()

	if dosave or dohard or dodata:

	    self.withdraw()
	    self.update_idletasks()

	    self.cancel()
	     
##############################################################################

    def set_plot_params(self):

	p = self.params
	pl = self.plot

	pl.title = p.title
	pl.xlab = p.xlabel
	pl.ylab= p.ylabel

## 	i = 0
## 	#print 'p.sets = ',
## 	#print self.params.sets
## 	for set in pl.sets:
## 	    #print 'set ',
## 	    #print 'plot %s params %s' % (set[0], p.sets[i])
## 	    set.path = p.sets[i]
## 	    #print ' -> ',
## 	    #print set[0]
## 	    i = i+1
	     
##############################################################################

    def tmp_plot(self):

        # don't give tedious message about tempnam security risk
        filterwarnings('ignore', 'tempnam is a potential security risk to your program')
        
        psfilenm = self.fnm
        tmpfile = os.tempnam('/tmp')
	cm = [0.0, 0.0, 0.0, 'green']
	c = self.canv

        # replot more suitably
        # - especially transpose black/white foreground/background
	scaledata = (c.xmax, c.xmin, c.ymax, c.ymin, 
		     c.xscalef, c.yscalef, c.xrange, c.yrange, 
		     c.xfloats, c.yfloats)

	p = np_plot.Plot_canv(self.plot, c.sets, mode=c.mode, 
		      style=c.style, fgcol='black', bgcol='white', 
		      scaledata=scaledata, plotlinewidth=1, axislinewidth=2)
        
	print 'Generating postscript file %s' % (psfilenm)

        # turn it into postscript
 	p.canv.postscript(file=tmpfile, colormode='color', colormap=cm, 
			  width= self.plot.wi, 
			  height = self.plot.ht-np_plot.B_TB_HT, rotate=1,
 			  pageheight='175m')

        # fine up nasty coarse Tk postscript engine lines
        print 'converting linewidths'
        cmd = 'sed -e \'s/1 setlinewidth/0.4 setlinewidth/g\' -e \'s/2 setlinewidth/1 setlinewidth/g\' %s > %s' % (tmpfile, psfilenm)
	status, output = commands.getstatusoutput(cmd)
	print '... exit status %d %s' % (status, output)

        # tidy up
        os.unlink(tmpfile)
	del(p.canv)
	del(p)
	
        print ' ... done'
	     
##############################################################################

    def hardcopy(self):

	cmd = '%s %s' % (self.params.lpr, self.fnm)
	print 'hardcopy: %s - ' % (cmd),
	status, output = commands.getstatusoutput(cmd)
	print 'exit status %d %s' % (status, output)
	
	     
##############################################################################

    def check_file(self, obj, field, entry_obj):

	fnm = getattr(obj, field)
	try:
	    f = open(fnm, 'r')
	except:
	    # doesn't already exist - carry on
	    return 1
	else:
	    # check
	    results = []
	    d = OverPrint(self.master, title='Overprint', 
			 msg='file already exists - overwrite?', 
			 entries = [('FILE:', fnm)],results=results)
	    if not len(results):
		#print 'no res'
		return 0
	    else:
		fnm = results[0]
		print 'res = %s' % (fnm)
		setattr(obj, field, fnm)
		entry_obj.delete(0, END)
		entry_obj.insert(INSERT, fnm)
		return 1
	     
##############################################################################

    def make_params(self):

	p = self.params = PrintParams()
	p.sets = []
	pl = self.plot

	if len(pl.title):
	    p.title = pl.title
	elif len(pl.sets) == 1:
	    p.title = pl.sets[0].path
	else:
	    p.title = ''
	p.xlabel = pl.xlab
	p.ylabel = pl.ylab
	for set in pl.sets:
	    p.sets.append(set)
	p.lpr = LPR_MONO_DEF
	p.file = self.make_fnm()
	     
##############################################################################

    def make_fnm(self):

	def suffstr_add(suffstr, str):
	    if not len(suffstr):
		#print 'suffstr=%s' % (str)
		return str
	    elif not len(str):
		return suffstr
	    elif suffstr[-1] == '.':
		#print 'suffstr=%s' % (suffstr + str)
		return suffstr + str
	    else:
		#print 'suffstr=%s' % (suffstr + '.' + str)
		return suffstr + '.' + str

	basepath, suffstr = os.path.split(self.plot.path)
	#print basepath
	#print suffstr

	tstr = os.path.split(self.params.title)[1]
	if tstr == suffstr:
	    tstr = ''

	if len(suffstr):
	    suffstr += '.'
	if len(basepath):
	     suffstr = '/' + suffstr

	if len(tstr):
	    suffstr = suffstr_add(suffstr, tstr)

	# Set 0 is the boss - but may not be included in zoom
	i = 0
	for s in self.canv.sets:
	    indx1, indx2 = s.curr_indices[-1]
	    if indx1 != None:
		break
	    i += 1

	if indx1 != 0 or indx2 != self.canv.sets[0].len - 1:
	    if i != 0:
		setstr = 'set%d.' % (i)
	    else:
		setstr = ''
	    suffstr = suffstr_add(suffstr, '%sindx%d-%d' % (setstr, 
							    indx1, indx2))

	if self.plot.canv_curr != 0:
	    suffstr = suffstr_add(suffstr, 'yval%.3f-%.3f' % (self.canv.ymin, self.canv.ymax))

	if self.canv.mode == np_plot.MODE_TS:
	    suffstr = suffstr_add(suffstr, 'ts')
	elif self.canv.mode == np_plot.MODE_PDF:
	    suffstr = suffstr_add(suffstr, 'pdf')
	else:
	    suffstr = suffstr_add(suffstr, 'cdf')

	suffstr = suffstr_add(suffstr, 'ps')
	

## 	path = self.plot.path[0][0] + connstr + indxstr + yvalstr + mstr + suffstr
	#path = os.path.join(basepath, suffstr)
	path = basepath + suffstr
	# remove any spaces
	ss = string.split(path)
	path = ''
	for e in ss:
	    path = path + e

	return self.strip_dot(path)
	
	     
##############################################################################

    def strip_dot(self, path):

	p1, p2 = os.path.split(path)
	if p2[0] == '.':
	    p2 = p2[1:]
	return os.path.join(p1, p2)
	
	     
##############################################################################
############################################################################## 

testroot = None

def callback():
    global testroot

    p = PrintParams()
    p.title = 'Title'
    p.xlabel = 'Xlabel'
    p.ylabel = 'Ylabel'
    p.sets = ['Plot1', 'Plot2']
    p.lpr = 'lpr -Ppalm'
    p.file = '/tmp/foo'
    d = PrintControl(testroot, params=p)

def quit():
    sys.exit(0)

def handle_sigint(n, f):
    print 'SIGINT'
    #handle_sigint_def(n, f)
    sys.exit(0)

def main():

    global testroot


    testroot = Tk()



    handle_sigint_def = signal(SIGINT, handle_sigint)

    # create a toolbar
    toolbar = Frame(testroot)

    b = Button(toolbar, text="PrintControl", width=6, command=callback)
    b.pack(side=LEFT, padx=2, pady=2)
    
    b = Button(toolbar, text="quit", width=6, command=quit)
    b.pack(side=LEFT, padx=2, pady=2)
    
    toolbar.pack(side=TOP, fill=X)
    
    mainloop()
	    
	     
##############################################################################


# Call main when run as script
if __name__ == '__main__':
        main()

	
