import Image, ImageDraw, ImageFont
import Tkinter, tkFileDialog
from Tkinter import LEFT, TOP, BOTTOM, RIGHT, NORMAL, END
from pyExcelerator import parse_xls
import csv

font = 'GIL_____.TTF'

class App:    
    def do_stuff(self):
        try:
            self.size = (int(self.entry1.get()), int(self.entry2.get()))
            self.left_header = self.entry3.get()
            self.right_header = self.entry4.get()
            self.fontsize = int(self.entry5.get())
            self.frame1.quit()
            self.frame2.quit()
            self.frame3.quit()
            self.frame4.quit()
            self.frame5.quit()
            self.frame6.quit()
        except:
            send_error("invalid entries; size and fontsize need to be integers")

    def __init__(self, master):
        self.frame1 = Tkinter.Frame(master)
        self.frame2 = Tkinter.Frame(master)
        self.frame3 = Tkinter.Frame(master)
        self.frame4 = Tkinter.Frame(master)
        self.frame5 = Tkinter.Frame(master)
        self.frame6 = Tkinter.Frame(master)
        self.frame1.pack(padx=2, pady=2)
        self.frame2.pack(padx=2, pady=2)
        self.frame3.pack(padx=2, pady=2)
        self.frame4.pack(padx=2, pady=2)
        self.frame5.pack(padx=2, pady=2)
        self.frame6.pack(padx=2, pady=2)
            
        std_width = 10
        self.label1 = Tkinter.Label(self.frame1, text="Width:", width=std_width)
        self.label1.pack(side=LEFT)
        
        self.entry1 = Tkinter.Entry(self.frame1)
        self.entry1.insert(0, "600")
        self.entry1.pack(side=LEFT)
        
        self.label2 = Tkinter.Label(self.frame2, text="Height:", width=std_width)
        self.label2.pack(side=LEFT)
        
        self.entry2 = Tkinter.Entry(self.frame2)
        self.entry2.insert(0, "800")
        self.entry2.pack(side=LEFT)
        
        self.label3 = Tkinter.Label(self.frame3, text="Left Header:", width=std_width)
        self.label3.pack(side=LEFT)
        
        self.entry3 = Tkinter.Entry(self.frame3)
        self.entry3.insert(0, "Header1")
        self.entry3.pack(side=LEFT)
        
        self.label4 = Tkinter.Label(self.frame4, text="Right Header:", width=std_width)
        self.label4.pack(side=LEFT)
        
        self.entry4 = Tkinter.Entry(self.frame4)
        self.entry4.insert(0, "Header2")
        self.entry4.pack(side=LEFT)
        
        self.label5 = Tkinter.Label(self.frame5, text="Fontsize:", width=std_width)
        self.label5.pack(side=LEFT)
        
        self.entry5 = Tkinter.Entry(self.frame5)
        self.entry5.insert(0, "14")
        self.entry5.pack(side=LEFT)

        self.moveon = Tkinter.Button(self.frame6, text="Generate Image", command=self.do_stuff)
        self.moveon.pack(side=BOTTOM)
        
class ErrorApp:
    def __init__(self, master, error):
        bigerrortext = """
        When importing a CSV file, the file must contain three comma delimited columns, the first of which
        contains the label, and the later two contain numbers.
        When importing an XLS file, the data must be contained in the first sheet, starting with A1.
        There may not be any other data within this sheet.  Just as with a CSV, the first column contains
        a label, and the later two should contain numbers.
        """
        self.frame1 = Tkinter.Frame(master)
        self.frame2 = Tkinter.Frame(master)
        self.frame1.pack(padx=2, pady=2)
        self.frame2.pack(padx=2, pady=2)
        self.label1 = Tkinter.Label(self.frame1, text=error)
        self.label1.pack(side=LEFT)
        self.label2 = Tkinter.Label(self.frame2, text=bigerrortext)
        self.label2.pack(side=LEFT)
        
def get_input_file():
    myFormats = [
        ('Excel','*.xls'),
        ('Comma Delimited File','*.csv'),
        ]
    root = Tkinter.Tk()
    file = tkFileDialog.askopenfile(parent=root,mode='rb',filetypes=myFormats, title='Choose a file')
    root.destroy()
    if file != None:      
        root = Tkinter.Tk()  
        app = App(root)
        root.title = 'Input Defaults'
        root.mainloop()
        root.destroy()
        return file, app.size, app.left_header, app.right_header, app.fontsize
    else:
        root.destroy()
        return ""
    
def file_save_name(initfile="tufte.jpg"):        
    myFormats = [
        ('JPEG','*.jpg'),
        ]

    root = Tkinter.Tk()
    fileName = tkFileDialog.asksaveasfilename(parent=root,filetypes=myFormats,title="Save the image as...", initialfile=initfile)
    if len(fileName ) > 0:
        print "Now saving under %s" % fileName
    root.destroy()
    return fileName
    
def parser_csv(data):
    rd = csv.reader(data.splitlines())
    return [[row[0], float(row[1]), float(row[2])] for row in rd]
    
def parser_xls(file):
    for sheet_name, values in parse_xls(file, 'cp1251'): # parse_xls(arg) -- default encoding
        matrix = [[]]
        for row_idx, col_idx in sorted(values.keys()):
            v = values[(row_idx, col_idx)]
            if isinstance(v, unicode):
                v = v.encode('cp866', 'backslashreplace')
            else:
                v = `v`
            v = '%s' % v.strip()
            last_row, last_col = len(matrix), len(matrix[-1])
            while last_row <= row_idx:
                matrix.extend([[]])
                last_row = len(matrix)

            while last_col < col_idx:
                matrix[-1].extend([''])
                last_col = len(matrix[-1])

            matrix[-1].extend([v])
        break
    return [[row[0], float(row[1]), float(row[2])] for row in matrix]
  
def text(label, x, y, width=10, align='LEFT', fill=(0,0,0)):
    if align=='CENTER':
        aligner = (width - draw.textsize(label, font=fonter)[0])/2
    elif align=='RIGHT':
        aligner = width - draw.textsize(label, font=fonter)[0]
    else:
        aligner = 0
    draw.text((x + aligner, y - .5*draw.textsize(label, font=fonter)[1]), label, fill=fill)
    
def send_error(error):
    print error
    root = Tkinter.Tk()
    app = ErrorApp(root, error)
    root.title = 'Problem'
    root.mainloop()
    root.destroy()
    
def data_checker(data):
    error = ""
    for row in data:
        if len(row) < 3:
            send_error("too few entries in row " + str(row))
        if len(row[0]) == 0:
            send_error("no entry in row " + str(row))
        

file, size, left_header, right_header, fontsize = get_input_file()
if file.name.split('.')[-1]=='csv':
    try:
        data = parser_csv(file.read())
    except:
        send_error("error reading csv")
elif file.name.split('.')[-1]=='xls':
    try:
        data = parser_xls(file.name)
    except:
        send_error("error reading xls")
else:
    send_error("wrong file type")

initfile = file.name.split('.')[0]    

oldsize = size
multiplier = 2
size = (multiplier * size[0], multiplier * size[1])
fontsize = fontsize * multiplier

im = Image.new("RGB", size, (255,255,255))
draw = ImageDraw.Draw(im)
fonter = ImageFont.truetype(font, fontsize)
draw.setfont(fonter)

text(left_header, size[0]*.28, size[1]*0.03)
text(right_header, size[0]*.68, size[1]*0.03)


# find the minimum and maximum values in the range
newdata = []
minval = maxval = data[0][1]
for label, startdata, enddata in data:
    minval = min(minval, startdata, enddata)
    maxval = max(maxval, startdata, enddata)

def ypos(val):
    return size[1] * (0.9 - 0.8 * (val - minval) / (maxval - minval))
    
for label, startdata, enddata in data:
    newdata += [[label, startdata, enddata, ypos(startdata), ypos(enddata)]]

for i in range(50):
    for j in range(len(newdata)):
        for k in range(len(newdata)):
            if j<>k:
                if newdata[j][3] >= newdata[k][3] and newdata[j][3] - newdata[k][3] < 0.8*draw.textsize('a', font=fonter)[1]:
                    newdata[j][3] += 1
                    newdata[k][3] -= 1
                if newdata[j][4] >= newdata[k][4] and newdata[j][4] - newdata[k][4] < 0.8*draw.textsize('a', font=fonter)[1]:
                    newdata[j][4] += 1
                    newdata[k][4] -= 1

for label, startdata, enddata, startpos, endpos in newdata:
    text(label, 0, startpos+4, width=0.25*size[0], align='RIGHT')
    text("%0.1f" % startdata, 0.25*size[0], startpos+4, width=0.07*size[0], align='RIGHT')
    text(label, size[0]*.75, endpos+4, align='LEFT')
    text("%0.1f" % enddata, 0.68*size[0], endpos+4, width=0.07*size[0], align='LEFT')
    draw.line((size[0]*.33, ypos(startdata),
         size[0]*.67, ypos(enddata)), fill=(0,0,0), width=int(multiplier/2))

im.thumbnail(oldsize, Image.ANTIALIAS)
out = file_save_name(initfile + '.jpg')
try:
    im.save(out, quality=95, optimize=1)
except:
    im.save(out + '.jpg', quality=95, optimize=1)