#!/usr/bin/env python2

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkPixbuf
from gi.repository import GObject
import os
import mate_invest
from gettext import gettext as _
from mate_invest import *
import sys
from os.path import join
import urllib
from threading import Thread
import time

AUTOREFRESH_TIMEOUT = 20*60*1000 # 15 minutes

# based on http://www.johnstowers.co.nz/blog/index.php/2007/03/12/threading-and-pygtk/
class _IdleObject(GObject.GObject):
	"""
	Override GObject.GObject to always emit signals in the main thread
	by emmitting on an idle handler
	"""
	def __init__(self):
		GObject.GObject.__init__(self)

	def emit(self, *args):
		GObject.idle_add(GObject.GObject.emit,self,*args)

class ImageRetriever(Thread, _IdleObject):
	"""
	Thread which uses gobject signals to return information
	to the GUI.
	"""
	__gsignals__ =  { 
			"completed": (
				GObject.SignalFlags.RUN_LAST, None, []),
			# FIXME: should we be making use of this?
			#"progress": (
			#	GObject.SignalFlags.RUN_LAST, None, [
			#	GObject.TYPE_FLOAT])        #percent complete
			}

	def __init__(self, image_url):
		Thread.__init__(self)
		_IdleObject.__init__(self)	
		self.image_url = image_url
		self.retrieved = False
		
	def run(self):
		self.image = Gtk.Image()
		try: sock = urllib.urlopen(self.image_url, proxies = mate_invest.PROXY)
		except Exception, msg:
			mate_invest.debug("Error while opening %s: %s" % (self.image_url, msg))
		else:
			loader = GdkPixbuf.PixbufLoader()
			loader.connect("closed", lambda loader: self.image.set_from_pixbuf(loader.get_pixbuf()))
			loader.write(sock.read())
			sock.close()
			loader.close()
			self.retrieved = True
		self.emit("completed")

# p:
#  eX = Exponential Moving Average
#  mX = Moving Average
#  b = Bollinger Bands Overlay
#  v = Volume Overlay
#  p = Parabolic SAR overlay
#  s = Splits Overlay
# q:
#  l = Line
#  c = Candles
#  b = Bars
# l:
#  on = Logarithmic
#  off = Linear
# z:
#  l = Large
#  m = Medium
# t:
#  Xd = X Days
#  Xm = X Months
#  Xy = X Years
# a:
#  fX = MFI X days
#  ss = Slow Stochastic
#  fs = Fast Stochastic
#  wX = W%R X Days
#  mX-Y-Z = MACD X Days, Y Days, Signal
#  pX = ROC X Days
#  rX = RSI X Days
#  v = Volume
#  vm = Volume +MA
# c:
#  X = compare with X
#

class FinancialChart:
	def __init__(self, ui):
		self.ui = ui
		
		#Time ranges of the plot (parameter / combo-box t)
		self.time_ranges = ["1d", "5d", "3m", "6m", "1y", "5y", "my"]
		
		#plot types (parameter / combo-box q)
		self.plot_types = ["l", "b", "c"]

		#plot scales (parameter / combo-box l)
		self.plot_scales = ["off", "on"]

		# Window Properties
		win = ui.get_object("window")
		win.set_title(_("Financial Chart"))
		
		try:
			pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(join(mate_invest.ART_DATA_DIR, "invest_neutral.svg"), 96,96)
			self.ui.get_object("plot").set_from_pixbuf(pixbuf)
		except Exception, msg:
			mate_invest.debug("Could not load 'invest-neutral.svg' file: %s" % msg)
			pass
		
		# Defaut comboboxes values
		for widget in ["t", "q", "l"]:
			ui.get_object(widget).set_active(0)
		
		# Connect every option widget to its corresponding change signal	
		symbolentry = ui.get_object("s")
		refresh_chart_callback = lambda w: self.on_refresh_chart()
		
		for widgets, signal in [
			(("pm5","pm10","pm20","pm50","pm100","pm200",
			"pe5","pe10", "pe20","pe50","pe100","pe200",
			"pb","pp","ps","pv",
			"ar","af","ap","aw","am","ass","afs","av","avm"), "toggled"),
			(("t", "q", "l"), "changed"),
			(("s",), "activate"),
		]:
			for widget in widgets:
				ui.get_object(widget).connect(signal, refresh_chart_callback)
		
		ui.get_object("progress").hide()
		
		# Connect auto-refresh widget
		self.autorefresh_id = 0
		ui.get_object("autorefresh").connect("toggled", self.on_autorefresh_toggled)
										
	def on_refresh_chart(self, from_timer=False):
		tickers = self.ui.get_object("s").get_text()
			
		if tickers.strip() == "":
			return True
		
		# FIXME: We don't just do US stocks, so we can't be this
		# simplistic about it, but it is a good idea.
		#if from_timer and not ustime.hour_between(9, 16):
		#	return True
			
		tickers = [ticker.strip().upper() for ticker in tickers.split(' ') if ticker != ""]
		
		# Update Window Title ------------------------------------------------------
		win = self.ui.get_object("window")
		title = _("Financial Chart - %s")
		titletail = ""
		for ticker in tickers:
			titletail += "%s / " % ticker
		title = title % titletail
		
		win.set_title(title[:-3])

		# Detect Comparison or simple chart ----------------------------------------
		opt = ""
		for ticker in tickers[1:]:
			opt += "&c=%s" % ticker
		
		# Create the overlay string ------------------------------------------------
		p = ""
		for name, param in [
			("pm5",   5),
			("pm10",  10),
			("pm20",  20),
			("pm50",  50),
			("pm100", 100),
			("pm200", 200),
			("pe5",   5),
			("pe10",  10),
			("pe20",  20),
			("pe50",  50),
			("pe100", 100),
			("pe200", 200),
			("pb", ""),
			("pp", ""),
			("ps", ""),
			("pv", ""),
		]:
			if self.ui.get_object(name).get_active():
				p += "%s%s," % (name[1], param)
			
		# Create the indicators string ---------------------------------------------
		a = ""
		for name, param in [
			("ar",  14),
			("af",  14),
			("ap",  12),
			("aw",  14),
			("am",  "26-12-9"),
			("ass", ""),
			("afs", ""),
			("av",  ""),
			("avm", ""),
		]:
			if self.ui.get_object(name).get_active():
				a += "%s%s," % (name[1:], param)
		
		# Create the image URL -----------------------------------------------------
		chart_base_url = "http://chart.finance.yahoo.com/z?s=%(s)s&t=%(t)s&q=%(q)s&l=%(l)s&z=%(z)s&p=%(p)s&a=%(a)s%(opt)s"
		url = chart_base_url % {
			"s": tickers[0],
			"t": self.time_ranges[self.ui.get_object("t").get_active()],
			"q": self.plot_types[self.ui.get_object("q").get_active()],
			"l": self.plot_scales[self.ui.get_object("l").get_active()],
			"z": "l",
			"p": p,
			"a": a,
			"opt": opt,
		}

		# Download and display the image -------------------------------------------	
		progress = self.ui.get_object("progress")
		progress.set_text(_("Opening Chart"))
		progress.show()
		
		image_retriever = ImageRetriever(url)
		image_retriever.connect("completed", self.on_retriever_completed)
		image_retriever.start()

		# Update timer if needed
		self.on_autorefresh_toggled(self.ui.get_object("autorefresh"))
		return True
	
	def on_retriever_completed(self, retriever):
		self.ui.get_object("plot").set_from_pixbuf(retriever.image.get_pixbuf())
		progress = self.ui.get_object("progress")
		if retriever.retrieved == True:
			progress.set_text(_("Chart downloaded"))
		else:
			progress.set_text(_("Chart could not be downloaded"))
	
	def on_autorefresh_toggled(self, autorefresh):
		if self.autorefresh_id != 0:
			GObject.source_remove(self.autorefresh_id)
			self.autorefresh_id = 0
			
		if autorefresh.get_active():
			self.autorefresh_id = GObject.timeout_add(AUTOREFRESH_TIMEOUT, self.on_refresh_chart, True)

def show_chart(tickers):
	ui = Gtk.Builder();
	ui.add_from_file(os.path.join(mate_invest.BUILDER_DATA_DIR, "financialchart.ui"))
	chart = FinancialChart(ui)
	ui.get_object("s").set_text(' '.join(tickers))
	chart.on_refresh_chart()
	return ui.get_object("window")

