#!/usr/bin/env python

#===================================
#           StackApplet
#  Copyright 2010 - Nathan Osman
#
#   StackApplet is released under
#        the MIT license
#===================================

# Import the necessary modules

import os
import sys

import pygtk
pygtk.require('2.0')  # make sure we have pyGTK 2+

import gnomeapplet
import gtk            # GNOME stuff
import gobject

import urllib2
import zlib    # for handling GZIP compression

# Now lets see if we can import the JSON
# module. We're going to make two tries at this
# to make sure we get one:

try:
    import json
except ImportError:
    import simplejson as json

# See if we can use pynotify to display alerts:

try:
    import pynotify
    can_display_alerts = 1;
    
except ImportError:
    can_display_alerts = 0;

# We have a class we want to use for the settings
# dialog that will be used for, well, displaying settings

import setting_dialog

# We also have a class we want to use for
# storing the settings:

import setting_store

#=============================
#        StackApplet
#        -----------
#    This is the main class
#  where all the action takes
#           place
#=============================

main_app = None

class StackApplet(gobject.GObject):

	# Constructor

	def __init__(self, applet, iid, prefs):
		
		self.__gobject_init__()
		
		prefs.connect('new_settings', self.HandleNewSettings)
		
		# Load the controls
		
		self.show_user_names = None
		
		self.timer = None
		
		self.applet = applet
		
		self.LoadControls()
		
		global main_app
		main_app = self
	
	def LoadControls(self):
		
		# Begin building the actual applet
		
		print "Constructing applet"
		
		# We need to create a set of 'boxes' that
		# each contain: logo, username, and rep
		
		# However, we need to find out from the
		# settings just how much of this to display.
		
		settings = setting_store.SettingStore()
		settings.Load()
		
		self.show_user_names = settings.Get("ShowNames","true")
		
		desired_sites = settings.Get("DesiredSites","stackoverflow/1")
		
		print "Desired sites: " + desired_sites
		
		desired_sites = desired_sites.split()

		self.refresh_rate = settings.Get("RefreshRate","300000")
		
		self.hbox = gtk.HBox(False, 10)
		
		self.site_list = []
		
		for site in desired_sites:
			site_data = site.split('/')
			
			self.site_list.append(self.AddSite(self.applet,self.hbox,site_data[0],site_data[1]))
		
		# Now show it all!
		
		self.applet.add(self.hbox)
		self.applet.show_all()
		
		# Now get the initial data!
		
		# kill the current timer
		if(not self.timer == None):
			gobject.source_remove(self.timer)
		
		# ...after 1 second, of course
		self.timer = gobject.timeout_add(1000,self.RefreshData)
		
	# Here we add a site to the panel
		
	def AddSite(self,applet,parent_container,sitename,user_id):
		
		# Actually create the GUI elements
		
		print "Adding " + sitename + " / " + str(user_id)
		
		# The label:
		button = gtk.Button()
		button.set_relief(gtk.RELIEF_NONE)
		button.set_label("Please wait...")
		
		button.connect("button_press_event", showMenu, applet)
		
		img = gtk.Image()
		
		hbox = gtk.HBox(False, 3)
		
		hbox.add(img)
		hbox.add(button)
		
		parent_container.add(hbox)
		
		# We need to return an associative array
		# containing all of the stuff
		
		return {'sitename': sitename, 'user_id': user_id,'label': button, 'icon': img, 'rep': 0, 'last_comment': 0}
	
	# Here is where the action takes place
	
	def RefreshData(self):
	
		# We need to enumerate over the sites:
		
		for site in self.site_list:
			
			# Get the data refreshed:
			
			self.GetSiteData(site)
		
		# kill the current timer
		if(not self.timer == None):
			gobject.source_remove(self.timer)
		
		self.timer = gobject.timeout_add(int(self.refresh_rate),self.RefreshData)
		
		return False
	
	# Actually gets the data for the site
	
	def GetSiteData(self,site_data):
		
		# First we need the user's name
		# and his reputation
		
		try:
			user_data = self.GetJSONResponse("http://api." + site_data['sitename'] + ".com/1.0/users/" + site_data['user_id'] + "?key=_qlxmEAOH06hLA1_FsZIGQ")
			
			reputation = user_data['users'][0]['reputation']
			
			if self.show_user_names == "true":
				site_data['label'].set_label(user_data['users'][0]['display_name'] + ": " + str(reputation))
			else:
				site_data['label'].set_label(str(reputation))
			
			# See if the user's rep has changed
			
			old_rep = site_data['rep']
			
			global can_display_alerts
			if old_rep != 0 and reputation != old_rep and can_display_alerts:
				
				# the rep has changed!
				
				n = pynotify.Notification ("You have gained " + str(reputation - old_rep) + " reputation.","","/usr/share/pixmaps/" + site_data['sitename'] + ".png")
				n.show()
				
			# this is now their new reputation!
			site_data['rep'] = reputation
			
			self.SetImage(site_data['icon'],site_data['sitename'])
			
			# Check for new comments:
			
			comment_data = self.GetJSONResponse("http://api." + site_data['sitename'] + ".com/1.0/users/" + site_data['user_id'] + "/mentioned?key=_qlxmEAOH06hLA1_FsZIGQ")
			
			if len(comment_data["comments"]):
			
				this_id = comment_data["comments"][0]["comment_id"]
			
				old_comment = site_data['last_comment']
			
				if old_comment != 0 and this_id != old_comment and can_display_alerts:
				
					n = pynotify.Notification (comment_data["comments"][0]["owner"]["display_name"] + " has posted a comment to you!","","/usr/share/pixmaps/" + site_data['sitename'] + ".png")
					n.show()
				
				# this is now the new comment ID!
				site_data['last_comment'] = this_id
		
		# We'll catch anything!
		
		except Exception as e:
			
			print "Error: " + e
			
			site_data['label'].set_label("HTTP error")
		
	# This is the function that returns the JSON
	# data, given a URL to get that data from...
	
	def GetJSONResponse(self,url):
		
		# Create the request
		request  = urllib2.Request(url)
		request.add_header('Accept-Encoding', 'gzip,deflate')    # Required by the API
		
		opener = urllib2.build_opener()
		
		# Get the raw stream and decompress it
		
		gzipped_stream = opener.open(request)
		raw_data = zlib.decompress(gzipped_stream.read(), 16+zlib.MAX_WBITS)
		
		# parse the json
		json_data = json.loads(raw_data)
		
		return json_data
		
	def SetImage(self,img,name):
		
		# Set the image if it exists:
		
		img_fn = "/usr/share/pixmaps/" + name + ".png"
		
		if not os.path.isfile(img_fn):
			
			img_path = os.path.join(os.getenv("HOME"),os.path.join('.config','stackapplet'))
			
			img_fn = os.path.join(img_path,name + ".png")
			
			if not os.path.isfile(img_fn):
			
				if not os.path.isdir(img_path):
					os.mkdir(img_path)
				
				# It isn't so easy now. We have to fetch the
				# icon from stackauth >- sigh -<
				
				site_data = self.GetJSONResponse("http://api." + name + ".com/1.0/stats?key=_qlxmEAOH06hLA1_FsZIGQ")
				
				site_icon = site_data['statistics'][0]['site']['icon_url']
				
				print "Fetching " + site_icon
				
				# Now fetch the file
				request_data  = urllib2.urlopen(site_icon).read()
				
				print "...done! Saving to " + img_fn
				
				f = open(img_fn, "wb")
				f.write(request_data)
				f.close()
		
		pixbuf = gtk.gdk.pixbuf_new_from_file(img_fn)
		
		scaled_buf = pixbuf.scale_simple(16,16,gtk.gdk.INTERP_BILINEAR)     # scale it to 16x16
		img.set_from_pixbuf(scaled_buf)
		
	def Reload(self):
	
		self.applet.remove(self.hbox)
		self.LoadControls()
		
	def HandleNewSettings(self,object):
		
		self.Reload()

# Here we have the GNOME stuff:

prefs = setting_dialog.SettingDialog()

def factory(applet, iid):
	global prefs
	
	applet_instance = StackApplet(applet, iid, prefs)
	
	return True

def showMenu(widget, event, applet):
	widget.emit_stop_by_name("button_press_event")
	create_menu(applet)

def create_menu(applet):
	propxml="""
			<popup name="button3">
			  <menuitem name="Item 3" verb="About" label="_About" pixtype="stock" pixname="gtk-about"/>
			  <menuitem name="Item 4" verb="Prefs" label="_Preferences" pixtype="stock" pixname="gtk-properties"/>
			</popup>"""
	verbs = [("About", showAboutDialog),("Prefs", showPrefsDialog)]
	applet.setup_menu(propxml, verbs, None)

def close_about_box(w, res):
	if res == gtk.RESPONSE_CANCEL:
		w.hide()

def showAboutDialog(*arguments, **keywords):
	about = gtk.AboutDialog()
	about.set_name("StackApplet")
	about.set_version("1.1")
	about.set_comments("GNOME panel applet for displaying StackOverflow reputation")
	about.set_copyright("Copyright (C) 2010 Nathan Osman")
	about.set_website("http://stackoverflow.quickmediasolutions.com")
	about.set_logo_icon_name("stackoverflow")
	about.set_authors(["Nathan Osman - Main Developer"])
	about.connect("response",close_about_box)
	about.show()

def showPrefsDialog(*arguments, **keywords):
	global prefs
	global main_app
	
	prefs.Display()

if len(sys.argv) == 2:
	if sys.argv[1] == "run-in-window":
		mainWindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
		mainWindow.set_title("Ubuntu System Panel")
		mainWindow.connect("destroy", gtk.main_quit)
		applet = gnomeapplet.Applet()
		factory(applet, None)
		applet.reparent(mainWindow)
		mainWindow.show_all()
		gtk.main()
		sys.exit()

if __name__ == '__main__':
	print "Starting factory"
	gnomeapplet.bonobo_factory("OAFIID:GNOME_StackOverflowApplet_Factory", gnomeapplet.Applet.__gtype__, "StackOverflow Reputation Displayer", "1.0", factory)
