#!/usr/bin/env python

# This file is part of Window-Switch.
# Copyright (c) 2009-2013 Antoine Martin <antoine@nagafix.co.uk>
# Window-Switch is released under the terms of the GNU GPL v3

import gtk
import gobject
import time
import threading

from winswitch.util.simple_logger import Logger

class ProgressBarWindow:
	def __init__(self, title, cancel_callback=None, window_icon=None):
		Logger(self)
		self.slog(None, title, str(cancel_callback), window_icon)
		self.title = title
		self.last_update = None
		self.first_progress = None
		self.last_percent = None
		self.last_eta = None
		self.window_icon = window_icon
		self.cancel_callback = cancel_callback
		self.window = None
		self.window_lock = threading.Lock()
		self.pbar = None

	def create_window(self):
		self.debug()
		window = gtk.Window(gtk.WINDOW_TOPLEVEL)
		if self.window_icon:
			window.set_icon(self.window_icon)
		window.set_resizable(True)
		window.connect("destroy", self.close)
		window.set_title(self.title)
		window.set_border_width(0)

		vbox = gtk.VBox(False, 5)
		vbox.set_border_width(10)
		window.add(vbox)
		# Create a centering alignment object
		align = gtk.Alignment(0.5, 0.5, 0, 0)
		vbox.pack_start(align, False, False, 20)
		# Create the ProgressBar
		self.pbar = gtk.ProgressBar()
		self.pbar.set_fraction(0.0)
		align.add(self.pbar)

		separator = gtk.HSeparator()
		vbox.pack_start(separator, False, False, 0)

		# rows, columns, homogeneous
		table = gtk.Table(2, 2, False)
		vbox.pack_start(table, False, True, 0)

		# Add a button to cancel whatever we're showing progress for
		button = gtk.Button("Cancel")
		button.connect("clicked", self.cancel)
		button.set_flags(gtk.CAN_DEFAULT)
		vbox.pack_start(button, False, False, 10)
		button.grab_default()
		return	window

	def close(self, *args):
		self.sdebug(None, *args)
		if self.window:
			gobject.idle_add(self.destroy)

	def destroy(self):
		lock = self.window_lock
		try:
			lock.acquire()
			if self.window:
				self.window.destroy()
				self.window = None
				self.window_lock = None
		finally:
			lock.release()
		return	False

	def set_progress(self, percent, show_window=True):
		self.sdebug(None, percent, show_window)
		if self.window_lock and self.window_lock.acquire(False):
			try:
				self.do_set_progress(percent, show_window)
			finally:
				self.window_lock.release()

	def do_set_progress(self, percent, show_window):
		now = time.time()
		if self.last_percent and percent < self.last_percent:
			#going backwards - reset counters
			self.last_update = None
			self.first_progress = None
			self.last_eta = None
		self.last_percent = percent
		#create the window if needed
		if not self.window:
			if not show_window:
				return
			else:
				self.window = self.create_window()
		else:
			show_window = False	#already shown
		#set progress and ETA
		if not self.last_update or (now - self.last_update) > 0.1:
			self.last_update = now
			gobject.idle_add(self.pbar.set_fraction, percent)
			self.sdebug(None, percent, show_window)
			if not self.first_progress:
				if percent>0:
					self.first_progress = (percent, now)
			else:
				eta = ""
				(first_percent, first_time) = self.first_progress
				self.sdebug("first_percent=%s, first_time=%s" % (first_percent, first_time), percent, show_window)
				if now - first_time > 1:		#only start guessing after a while
					diff_pct = percent - first_percent
					diff_time = now - first_time
					remain_pct = 1.0-percent
					if diff_pct != 0:
						time_left = 0.5+(remain_pct / diff_pct) * diff_time
						self.sdebug("time_left=%s" % time_left, percent, show_window)
						if time_left<1:
							eta = "ETA: almost done!"
						elif time_left<120:
							eta = "ETA: %d seconds" % time_left
						else:
							eta = "ETA: %d minutes" % (time_left/60)
				if not self.last_eta or self.last_eta != eta:
					self.last_eta = eta
					gobject.idle_add(self.pbar.set_text, eta)
		#first time shown?
		if show_window:
			self.window.show_all()

	def cancel(self, widget, data=None):
		self.slog(None, widget, data)
		if self.cancel_callback:
			self.cancel_callback()
