#!/usr/bin/env python

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

__author__ = "Nathan Osman"
__copyright__ = "Copyright (c) 2010, Nathan Osman"
__license__ = "MIT"
__version__ = "1.3"

# Import the necessary modules

import os
import sys

import tempfile

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

import gnomeapplet
import gtk            # GNOME stuff
import gobject

# For fetching the logos:

import urllib2

# See if we can use pynotify to display alerts:

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

# We need the webbrowser module
# to open the home page
import webbrowser

# 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

# ...and lastly, the HTTP request module

import api_request

#=============================
#        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, sitename)
		
		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
	
	# Retrieves a gravatar
	
	def GetGravatar(self,ehash):
		
		request_data  = urllib2.urlopen("http://gravatar.com/avatar/" + ehash + "?s=128&d=identicon").read()
		
		print "Fetching gravatar..."
		
		img_fn = os.path.join(tempfile.gettempdir(),ehash)
				
		f = open(img_fn, "wb")
		f.write(request_data)
		f.close()
		
		return img_fn
	
	# 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!
				
				img_fn = "/usr/share/pixmaps/" + site_data['sitename'] + ".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,site_data['sitename'] + ".png")
				
				n = pynotify.Notification ("You have gained " + str(reputation - old_rep) + " reputation.","",img_fn)
				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:
					
					# Retrieve the Gravatar for the user
					gravatar = self.GetGravatar(comment_data["comments"][0]["owner"]["email_hash"])
					
					n = pynotify.Notification (comment_data["comments"][0]["owner"]["display_name"] + " has posted a comment to you!","",gravatar)
					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: " + str(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):
		
		return api_request.GetJSONData(url)
		
	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()
applet_instance = None

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

def refreshItems(*arguments, **keywords):
	applet_instance.RefreshData()

def showMenu(widget, event, applet, sitename):
	if event.button == 3:
		widget.emit_stop_by_name("button_press_event")
		create_menu(applet)
	else:
		webbrowser.open("http://" + sitename + ".com/")

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="Refresh" label="_Refresh" pixtype="stock" pixname="gtk-about"/>
			  <menuitem name="Item 5" verb="Prefs" label="_Preferences" pixtype="stock" pixname="gtk-properties"/>
			</popup>"""
	verbs = [("About", showAboutDialog),("Refresh", refreshItems),("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(__version__)
	about.set_comments("GNOME panel applet for displaying StackOverflow reputation")
	about.set_copyright(__copyright__)
	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
	
	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)
