#!/usr/bin/python
from Tkinter import *
import string, pickle
import tkSimpleDialog, tkMessageBox, tkFileDialog
#from gist import *
#from Numeric import *
import Gnuplot

def version():
    return "0.1"

drinks = {  #name of drink          : cm^3 of alcohol per unit
            "pure alcohol (cm^3)"   : 1,
            "small beer 10% (0.5l)" : 12,
            "small beer 12% (0.5l)" : 20,
            "dark beer 12% (0.5l)"  : 30,
            "wine (2dl)"            : 20,
            "red wine (2dl)"        : 24,
            "vodka (0.5dl)"         : 20,
            "whisky (1dl)"          : 30
         }


def initpermille(cm3, weight, sex):
    "initial per mille after drinking cm3 cm^3 of pure alcohol"
    if sex == "Male":
        sexcoef=0.75
    elif sex == "Female":
        sexcoef=0.66    
    #return (cm3*0.7-weight/10)/(weight*sexcoef)
    return (cm3*0.7)/(weight*sexcoef)

def deltapermille(sex, dh):
    "per mille released after dh hours"
    if sex == "Male":
        sexcoef=0.75
    elif sex == "Female":
        sexcoef=0.66    
    return -0.1*sexcoef*dh


def hours2dhm(hours):
    "converts hours to days, hours and minutes"
    d = int(hours)/24
    h = hours - 24*d
    m = int((h - int(h))*60)
    return d, int(h), m


class DrinkDialog(tkSimpleDialog.Dialog):

    def __init__(self, parent, title = None, initval=(0, 1, "vodka")):

        self.initval = initval

        Toplevel.__init__(self, parent)
        self.transient(parent)

        if title:
            self.title(title)

        self.parent = parent

        self.result = None

        body = Frame(self)
        self.initial_focus = self.body(body)
        body.pack(padx=5, pady=5)

        self.buttonbox()

        self.grab_set()

        if not self.initial_focus:
            self.initial_focus = self

        self.protocol("WM_DELETE_WINDOW", self.cancel)

        self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
                                  parent.winfo_rooty()+50))

        self.initial_focus.focus_set()

        self.wait_window(self)

    def body(self, master):
        Label(master, text="Time:").grid(row=0, sticky=W)
        Label(master, text="d").grid(row=0, column=2, sticky=W)
        Label(master, text="h").grid(row=0, column=4, sticky=W)
        Label(master, text="m").grid(row=0, column=6, sticky=W)
        Label(master, text="Nr of drinks:").grid(row=1, sticky=W)

        self.eday = Entry(master, width=2)
        self.ehours = Entry(master, width=3)
        self.eminutes = Entry(master, width=3)
        self.enr = Entry(master, width=3)

        d, h, m = hours2dhm(self.initval[0])

        self.eday.insert(0,d)
        self.ehours.insert(0,h)
        self.eminutes.insert(0,m)
        self.enr.insert(0,`self.initval[1]`)

        self.eday.grid(row=0, column=1, sticky=W)
        self.ehours.grid(row=0, column=3, sticky=W)
        self.eminutes.grid(row=0, column=5, sticky=W)
        self.enr.grid(row=1, column=1, columnspan=2, sticky=W)

        self.drinktype = StringVar()
        self.drinktype.set(self.initval[2])
        for i in range(len(drinks.keys())):
            self.rb = Radiobutton(master, 
                        text=drinks.keys()[i], 
                        width=20,
                        anchor=W,
                        variable=self.drinktype,
                        value=drinks.keys()[i])
            self.rb.grid(row=i+2, columnspan=6)

        return self.ehours

    def validate(self):
        try:
            d, h, m = int(self.eday.get()), int(self.ehours.get()), int(self.eminutes.get())
            time = d*24.+h+m/60.
            nrdrinks = float(self.enr.get())
            drink = self.drinktype.get()
            self.result = time, nrdrinks, drink
            ok = 1
            if d<0 or d>1: ok = 0
            if h<0 or h>24: ok = 0
            if m<0 or m>59: ok = 0
            if nrdrinks<0: ok = 0
            if not ok:
                tkMessageBox.showwarning("Bad input", "Illegal number, try again")
                return 0
            else:
                return 1
        except ValueError:
            tkMessageBox.showwarning("Bad input", "Illegal number, try again")
            return 0
        

class App(Frame):
    def __init__(self, master=None):
        self.list=[] # list of tuples (time, grams)
        self.frame=Frame(master)
        self.frame.pack()

        scrollbar = Scrollbar(self.frame, orient=VERTICAL)
        self.listbox = Listbox(self.frame, width=35, yscrollcommand=scrollbar.set)
        self.listbox.bind("<Double-Button-1>", self.edit)
        scrollbar.config(command=self.listbox.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.listbox.pack(side=LEFT, fill=BOTH, expand=1)

        self.loadb = Button(self.frame, 
                        text="Load", 
                        width=7,
                        command=self.load)
        self.loadb.pack(side=TOP)

        self.saveb = Button(self.frame, 
                        text="Save", 
                        width=7,
                        command=self.save)
        self.saveb.pack(side=TOP)

        self.bsex = StringVar()
        self.bsex.set("Male")
        self.sexmb = Radiobutton(self.frame, 
                        text="Male",
                        width=7,
                        anchor=W,
                        variable=self.bsex,
                        value="Male")
        self.sexmb.pack(side=TOP)

        self.sexfb = Radiobutton(self.frame, 
                        text="Female", 
                        width=7,
                        anchor=W,
                        variable=self.bsex,
                        value="Female")
        self.sexfb.pack(side=TOP)

        self.kgtext = StringVar()
        self.kgtext.set("80 kg")
        self.weight = 80

        self.kgb = Button(self.frame, 
                        textvariable=self.kgtext, 
                        width=7,
                        relief=RIDGE,
                        cursor='hand2',
                        command=self.enterkg)
        self.kgb.pack(side=TOP)


        self.addb = Button(self.frame, 
                        text="Add", 
                        width=7,
                        command=self.add)
        self.addb.pack(side=TOP)

        self.copyb = Button(self.frame, 
                        text="Copy", 
                        width=7,
                        command=self.copy)
        self.copyb.pack(side=TOP)

        self.delb = Button(self.frame, 
                        text="Delete", 
                        width=7,
                        command=self.delete)
        self.delb.pack(side=TOP)

        self.editb = Button(self.frame, 
                        text="Edit", 
                        width=7,
                        command=self.edit)
        self.editb.pack(side=TOP)

        self.quitb = Button(self.frame, 
                        text="Quit", 
                        width=7,
                        cursor="pirate",
                        command=self.frame.quit)
        self.quitb.pack(side=BOTTOM)

        self.aboutb = Button(self.frame, 
                        text="About", 
                        width=7,
                        relief=GROOVE,
                        command=self.about)
        self.aboutb.pack(side=BOTTOM)

        self.drawb = Button(self.frame, 
                        text="Draw", 
                        width=7,
                        command=self.draw)
        self.drawb.pack(side=BOTTOM)

        self.exportb = Button(self.frame, 
                        text="Export", 
                        width=7,
                        command=self.export)
        self.exportb.pack(side=BOTTOM)

    def askdrink(self, initval=(0, 1, "vodka")):
        "ask for the time, number and type of drink"
        d = DrinkDialog(self.frame,
                    title="What did you drink?",
                    initval=initval)
        return d.result

    def permille(self, time):
        sex = self.bsex.get()
        return permille2(time=time, 
                        sex=sex,
                        weight=self.weight,
                        drinklist=self.list)

    def stringify(self, tup):
        d, h, m = hours2dhm(tup[0])
        if d <> 0:
            result = `d`+"d"
        else:
            result = ""
        result = result + `h`+":"+`m`
        return result+"  " + `tup[1]`+"x " + tup[2]

    def enterkg(self):
        kg=tkSimpleDialog.askfloat(
                title="Weight", 
                prompt="What is your weight?:",
                initialvalue=self.weight)
        if kg <> None:
            self.weight = kg
            self.kgtext.set(`kg`+" kg")
        

    def getpos(self):
        csel = self.listbox.curselection()
        if csel <> ():
            return int(csel[0])
        else:
            return None

    def delete(self):
        pos = self.getpos()
        if pos <> None:
            self.list = self.list[:pos]+self.list[pos+1:]
            self.listbox.delete(ACTIVE)

    def draw(self):
        self.drawb.flash()
        sex = self.bsex.get()
        #x = 48*arange(6*48, typecode=Float)/(6*48.)  # each 10 minutes
        x = (48*6)*[0.]  # workaround so that we do not depend on Numeric
                         # kind of pointless so far, because Gnuplot.py
                         # depends on Numeric, but we may change graphics
                         # package in the future...
        for i in range(6*48):
            x[i] = i*10./60
        y = (48*6)*[0.]
        for i in self.list:
            j = int(round(i[0]*6))
            y[j] = y[j] + initpermille(i[1]*drinks[i[2]], self.weight, sex)
        for i in range(len(y)-1):
            new = y[i] + deltapermille(sex, 1/6.)
            if new > 0:
                y[i+1] = y[i+1] + new
        d = Gnuplot.Data(x, y,
                    title='per mille',
                    with='lines')
        g.plot(d)

    def export(self):
        fname = tkFileDialog.asksaveasfilename(initialfile="chlast.ps",
                        title="Export into file",
                        filetypes=[("postscript", "*.ps"),
                                   ("encapsulated postscript", "*.epsi"),
                                   ("encapsulated postscript", "*.eps")]
                        )
        if fname:
            eps=0
            if fname[-5:] == ".epsi" or fname[-4:] == ".eps":
                eps=1
            g.hardcopy(fname, enhanced=1, color=1, eps=eps)

    def load(self):
        fname = tkFileDialog.askopenfilename(title="Load list",
                            filetypes=[("all","*")])
        if fname:
            f = open(fname, "r")
            self.list = pickle.load(f)
            f.close()
            for i in self.list:
                self.listbox.insert(END, self.stringify(i))

    def save(self):
        fname = tkFileDialog.asksaveasfilename(initialfile="chlast",
                                            title="Save list",
                                            filetypes=[("all","*")])
        if fname:
            f = open(fname, "w")
            pickle.dump(self.list, f)
            f.close()

    def add(self):
        dr = self.askdrink()
        if dr <> None:
            time, nr, drink = dr
            self.list.append((time, nr, drink))
            self.listbox.insert(END, self.stringify((time, nr, drink)))

    def copy(self):
        pos = self.getpos()
        if pos <> None:
            self.list.append(self.list[pos])
            self.listbox.insert(END, self.stringify(self.list[pos]))

    def edit(self, event=None):
        pos = self.getpos()
        if pos <> None:
            dr = self.askdrink(initval = self.list[pos])
            if dr <> None:
                self.list[pos] = dr
                self.listbox.delete(pos)
                self.listbox.insert(pos, self.stringify(self.list[pos]))

    def about(self):
        tkMessageBox.showinfo("About chlastmeter", "chlastmeter "
                    + version()
                    + "\n\nby Radovan Garabik")

g = Gnuplot.Gnuplot()
g.title('chlastmeter')
g.xlabel('time')
g.ylabel('per mille')

root = Tk()
app = App(root)
root.mainloop()

