#!/usr/bin/env python

# example helloworld.py

import pygtk
pygtk.require('2.0')
import gtk
import gobject
import re
import os.path, os
import urllib2
import sys
import gzip
import StringIO
import thread


class Updater:
    def __init__(self):
        # First we need to read all the online libraries
        try:
            fd = open(os.getenv("HOME") + "/.gEDA/online_libs")
            lines = fd.readlines()
            fd.close()
        except:
            lines = ['lib-gz http://www.zerties.org/~stettberger/gschem/symbols.gz']
            fd = open(os.getenv("HOME") + "/.gEDA/online_libs", "w+")
	    for line in lines:
	        fd.write(line + "\n")
            fd.close()
            print "ERROR: Can't read ~/.gEDA/online_libs!\nUsing %s"%lines[0]
        self.archives = []
        for line in lines:
            if line.startswith("lib"):
                self.archives.append(line.strip().split(" ", 1))
    def catch(self, archive_nr):
        if len(self.archives) <= archive_nr:
            return False
        try:
            url = urllib2.urlopen(self.archives[archive_nr][1])
            data = url.read()
            url.close()
        except:
            return "ERROR: Can't download: " + self.archives[archive_nr][1]
        if self.archives[archive_nr][0] == "lib-gz":
            try:
                return gzip.GzipFile(fileobj = StringIO.StringIO(data)).read().split("\n")
            except:
                return "ERROR: Can't decompress data"
        return data.split()
    def symbol_count(self, symbols):
        count = 0
        # Symbol count before removing double symbols
        for i in symbols:
            if i.startswith("Filename:"): count += 1
        return count

    def delete_doublettes(self, symbols):
        new_symbols = []
        sym = None
        for line in symbols + ['Filename: dummy/data']:
            if line.startswith("Filename: "):
                if sym != None:
                    append = True
                    for i in range(0, len(new_symbols)):
                        if new_symbols[i]['filename'] == sym['filename']:
                            if new_symbols[i].has_key('version') and sym.has_key('version'):
                                if new_symbols[i]['version'] < sym['version']:
                                    del new_symbols[i]
                                else:
                                    append = False
                            else: 
                                append = False

                    if append:
                        new_symbols.append(sym)

                sym = {}
                sym['filename'] = os.path.basename(line.split(" ", 1)[1])
                sym['data'] = []
            if line.startswith("Symbol-Version: "):
                sym['version'] = eval(line.split(" ", 1)[1].replace(".", "* 100 +"))

            if (sym): sym['data'].append(line)


                    
        symbols = []
        for new in new_symbols:
            for line in new['data']:
                symbols.append(line)
        return symbols
    def write_symbol_cache(self, symbols):
        try:
            fd = open(os.getenv("HOME") + "/.gEDA/symbols_cache", "w+")
            for i in symbols: fd.write(i + "\n")
            fd.close()
        except:
            return "Can't write symbol cache to: " + os.getenv("HOME") + "/.gEDA/symbols_cache"
    def update(self):
        i = 0
        list = []
        while True:
            symbols = self.catch(i)
            i += 1
            if symbols == False: break
            if symbols.__class__.__name__ == "String":
                print symbols
                continue
            print "Got %d symbols"%self.symbol_count(symbols)
            list += symbols
        before = self.symbol_count(list)
        list = self.delete_doublettes(list)
        after = self.symbol_count(list)

        err = self.write_symbol_cache(list)
        if err:
            print err
            return
        print "%d Symbol; %d doubles deleted"%(after, before-after)
            





def parse_file(file):
    fd = open(file, "r")
    i = 0
    ret = [{}]
    for line in fd.readlines():
        if line.startswith("Filename: "):
            i = i + 1    
            ret.append({})
            ret[i]['file'] = os.path.basename(line.split(" ", 1)[1].strip("\n"))
            ret[i]['filename'] = line.split(" ", 1)[1].strip("\n")
        elif line.startswith("Image: "):
            ret[i]['image'] = line.split(" ", 1)[1].strip("\n")
        elif line.startswith("Description: "):
            ret[i]['desc'] = line.split(" ", 1)[1].strip("\n")
        elif line.startswith("Documentation: "):
            ret[i]['doc'] = line.split(" ", 1)[1].strip("\n")
        elif line.startswith("Symbol-Version: "):
            ret[i]['version'] = line.split(" ", 1)[1].strip("\n")
        elif line.startswith("Author: "):
            ret[i]['author'] = line.split(" ", 1)[1].strip("\n")
        elif line.startswith("Use-License: "):
            ret[i]['use-license'] = line.split(" ", 1)[1].strip("\n")
        elif line.startswith("Dist-License: "):
            ret[i]['dist-license'] = line.split(" ", 1)[1].strip("\n")
    return ret


 


try:
    os.mkdir(os.getenv("HOME") + "/.gEDA")
except: pass # Directory does already exist

if not os.path.exists(os.getenv("HOME") + "/.gEDA/symbols_cache"):
    u = Updater()
    u.update()



class GafOnLib:
    def delete_event(self, widget, event, data=None):
        return False

    def destroy(self, widget, data=None):
        gtk.main_quit()

    def search_change_callback(self, widget, data):
        regex = re.compile(".*" + data.get_text() + ".*", re.I)
        found = []
        for i in self.db:
            if (i.has_key('file') and regex.match(i['file'])) or (i.has_key('desc') and regex.match(i['desc'])):
                found.append(i)
        self.filtered = found
        self.treeview.set_model(self.create_model())

    def row_activate(self, view, detail_treeview = None):
        selection = view.get_selection()
        model, iter = selection.get_selected()
        symbol = self.filtered[model.get_path(iter)[0]]
        self.treeview.scroll_to_cell(model.get_path(iter))
        # This is a hack :(
        gobject.idle_add(self.treeview.scroll_to_cell, model.get_path(iter))


        # create list store
        model = gtk.ListStore(
            gobject.TYPE_STRING,
            gobject.TYPE_STRING,
            gobject.TYPE_BOOLEAN
       )
        for key, desc in [["filename",    "Filename:"], \
                          ["desc",        "Description:"], \
                          ["doc",         "Documentation:"], \
                          ["version",     "Version:"], \
                          ["author",      "Author:"], \
                          ["use-license", "Use-License:"], \
                          ["dist-license","Dist-License:"]]:
                iter = model.append()
                data = ""
                if symbol.has_key(key): data = symbol[key]
                model.set (iter, 0, desc, 1,data, 2, True)

        detail_treeview.set_model(model)
        self.detail_box.show()
	self.last_thread = thread.start_new_thread(self.update_detail_image, tuple([symbol]))

    def update_detail_image(self, symbol):
	if (self.detail_image.get_pixbuf()):
	    width = self.detail_image.get_pixbuf().get_width()
	    height = self.detail_image.get_pixbuf().get_height()
	    self.detail_image.clear()
	    self.detail_image.set_size_request(width, height)
        try:
            data = urllib2.urlopen(symbol['image']).read()
        except:
            print "Can't load image for: " + symbol['filename']
            return
        else:
            pixbuf_loader = gtk.gdk.PixbufLoader()
            pixbuf_loader.write(data, len(data))
            pixbuf_loader.close()
            pixbuf = pixbuf_loader.get_pixbuf()
            pixbuf = pixbuf.scale_simple(pixbuf.get_width()/2, \
                            pixbuf.get_height()/2, gtk.gdk.INTERP_BILINEAR)

        self.detail_image.set_from_pixbuf(pixbuf)
	width = self.detail_image.get_pixbuf().get_width()
	height = self.detail_image.get_pixbuf().get_height()
	self.detail_image.set_size_request(width, height)
        self.detail_image.show()


    def create_model(self):
         # create list store
        model = gtk.ListStore(
            gobject.TYPE_STRING,
            gobject.TYPE_STRING
       )

        # add items
        for filter in self.filtered:
            iter = model.append()
            desc = ""
            if filter.has_key("desc"): desc = filter['desc']

            model.set (iter, 0, filter['file'], 1, desc)

        return model
    def download_symbol(self,widget,view):
        selection = view.get_selection()
        model, iter = selection.get_selected()
        symbol = self.filtered[model.get_path(iter)[0]]
        chooser = gtk.FileChooserDialog(title="Save symbol to...",action=gtk.FILE_CHOOSER_ACTION_SAVE,
        buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK))
        chooser.set_current_name(symbol['file'])
        chooser.set_current_folder(os.getenv("HOME") + "/.gEDA")
        response  = chooser.run()
        if response != gtk.RESPONSE_OK:
            chooser.destroy()
            return

        filename = chooser.get_filename()
        chooser.destroy()

        try:
            data = urllib2.urlopen(symbol['filename']).read()
            fd = open(filename, "w+")
            fd.write(data)
            fd.close()
        except:
            dialog = gtk.MessageDialog(self.window,
              gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
              gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
          "Couldn't download symbol:\n" + symbol['filename'])
    def update_symbol_cache(self, widget, data):
        self.update_label.show()
        update = Updater()
        i = 0
        list = []
        while True:
            text = self.update_label.get_text()
            symbols = update.catch(i)
            i += 1
            if symbols == False: break
            if symbols.__class__.__name__ == "String":
                self.update_label.set_text(text + "\n" + symbols)
                continue
            self.update_label.set_text(text + "\nGot %d symbols"%update.symbol_count(symbols))
            list += symbols
            gtk.main_iteration(block=False)
        before = update.symbol_count(list)
        list = update.delete_doublettes(list)
        after = update.symbol_count(list)

        err = update.write_symbol_cache(list)
        if err:
            text = self.update_label.get_text()
            self.update_label.set_text(text + "\n" + err)
            return
        text = self.update_label.get_text()
        self.update_label.set_text(text + "\n%d Symbol; %d doubles deleted"%(after, before-after))
        gtk.main_iteration(block=False)
            
            
        gobject.timeout_add(5000, lambda : self.update_label.hide() and False)
        text = self.update_label.get_text()
        self.update_label.set_text(text + "\nNote: This window will close automatically")

        self.db = parse_file(os.getenv("HOME") + "/.gEDA/symbols_cache")
        self.filtered = []

    def __init__(self):
        self.db = parse_file(os.getenv("HOME") + "/.gEDA/symbols_cache")
        self.filtered = []

        # create a new window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
    
        # When the window is given the "delete_event" signal (this is given
        # by the window manager, usually by the "close" option, or on the
        # titlebar), we ask it to call the delete_event () function
        # as defined above. The data passed to the callback
        # function is NULL and is ignored in the callback function.
        self.window.connect("delete_event", self.delete_event)
    
        # Here we connect the "destroy" event to a signal handler.  
        # This event occurs when we call gtk_widget_destroy() on the window,
        # or if we return FALSE in the "delete_event" callback.
        self.window.connect("destroy", self.destroy)
    
        # Sets the border width of the window.
        self.window.set_border_width(10)
    
        # Creates a new button with the label "Hello World".

        self.search = gtk.Entry(max = 0)
        self.search.connect("activate", self.search_change_callback, self.search)

        self.button = gtk.Button("Search")

        self.button.connect("clicked", self.search_change_callback, self.search)

        # Filter list
        sw = gtk.ScrolledWindow()
        sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

        # Treeview with colums
        self.treeview = gtk.TreeView(self.create_model())
        self.treeview.set_rules_hint(True)
        self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
        ##
        ## Add Colums
        ##
        model = self.treeview.get_model()

        #  column
        renderer = gtk.CellRendererText()
        renderer.set_data("column", 0)

        column = gtk.TreeViewColumn("File", renderer, text=0)
        self.treeview.append_column(column)

        # description column
        renderer = gtk.CellRendererText()
        renderer.set_data("column", 1)
        column = gtk.TreeViewColumn("Description", renderer, text=1)
        self.treeview.append_column(column)

        sw.add(self.treeview)

        # Detail Treeview with colums
        self.detail_treeview = gtk.TreeView(self.create_model())
        self.detail_treeview.set_rules_hint(True)
        self.detail_treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
        ##
        ## Add Colums
        ##
        #  column
        renderer = gtk.CellRendererText()
        renderer.set_data("column", 0)

        column = gtk.TreeViewColumn("Key", renderer, text=0)
        self.detail_treeview.append_column(column)

        # description column
        renderer = gtk.CellRendererText()
        renderer.set_data("column", 1)
        column = gtk.TreeViewColumn("Value", renderer, text=1, editable=2)
        self.detail_treeview.append_column(column)
        # Buttons 
        action_hbox = gtk.HBox(homogeneous=False, spacing=0)
        button = gtk.Button("Update Symbol Cache")
        button.connect("clicked", self.update_symbol_cache, None)
        action_hbox.pack_start(button)
        button = gtk.Button("Download")
        button.connect("clicked", self.download_symbol, self.treeview)
        action_hbox.pack_start(button)


        # Detail buffer
        self.treeview.connect("cursor-changed", self.row_activate, self.detail_treeview)
        self.treeview.connect("row-activated", lambda *w:self.download_symbol(None, self.treeview))

        self.detail_image = gtk.Image()
        self.detail_image.set_from_pixbuf(None)

        self.update_label = gtk.Label()


        # Start packing
        self.search_box = gtk.HBox(homogeneous=False, spacing=0)
        self.search_box.pack_start(self.search, expand=True, fill=True, padding=0)
        self.search_box.pack_start(self.button, expand=False, fill=False, padding=0)


        self.detail_box = gtk.HBox(homogeneous = False, spacing = 5)
        self.detail_box.pack_start(self.detail_treeview, expand = True)
        self.detail_box.pack_start(self.detail_image, expand = True)

        vpaned = gtk.VPaned()
        vpaned.set_border_width(5)
        vpaned.pack1(sw,resize=True,shrink = False)
        vpaned.pack2(self.detail_box,resize=False, shrink = False)


        self.vbox = gtk.VBox(homogeneous = False, spacing = 0)
        self.vbox.pack_start(self.search_box, expand = False)
        self.vbox.pack_start(vpaned)
        self.vbox.pack_start(action_hbox, expand = False)
        self.vbox.pack_start(self.update_label, expand = True)



        # This packs the button into the window (a GTK container).
        self.window.add(self.vbox)
    
        # The final step is to display this newly created widget.
        # and the window
        self.window.show_all()
        self.search_change_callback(None, self.search)
        self.detail_box.hide()
        self.update_label.hide()

    def main(self):
	gobject.threads_init()
        # All PyGTK applications must have a gtk.main(). Control ends here
        # and waits for an event to occur (like a key press or mouse event).
        gtk.main()

# If the program is run directly or passed as an argument to the python
# interpreter then create a HelloWorld instance and show it
if __name__ == "__main__":
    gafon = GafOnLib()
    gafon.main()
