#!/usr/bin/env python
#
#   ConVirt   -  Copyright (c) 2008 Convirture Corp.
#   ======
#
# ConVirt is a Virtualization management tool with a graphical user
# interface that allows for performing the standard set of VM operations
# (start, stop, pause, kill, shutdown, reboot, snapshot, etc...). It
# also attempts to simplify various aspects of VM lifecycle management.
#
#
# This software is subject to the GNU General Public License, Version 2 (GPLv2)
# and for details, please consult it at:
#
#    http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
# 
#
#

import gtk, gobject

# encapsulates TaskProgressWnd
# Note this is using a different scheme. It creates new widgets and
# destroy when done.
from convirt.client.dialogs import gladefile
class TaskProgressWnd:

    (START_PULSE, STOP_PULSE, SET_FRACTION, COMPLETE, CANCELED, CLOSE) = range(6)
    def __init__(self,context, initial_status = "", can_cancel = True):

        """ Constructor"""
        self.gladefile = gladefile
        wtree = gtk.glade.XML(self.gladefile, "TaskProgress")
        self.dialog = wtree.get_widget("TaskProgress")

        
        self.context = wtree.get_widget("progress_context_label")
        self.status = wtree.get_widget("progress_status_label")
        self.pbar = wtree.get_widget("progress_progressbar")
        self.cancel_button = wtree.get_widget("progress_cancel_button")

        self.context.set_text(context)
        self.status.set_text(initial_status)
        self.cancel_pressed = False
        self.done = False

        self.ret = None
        self.ex = None

        self.can_cancel = can_cancel
        if not self.can_cancel:
            self.cancel_button.set_sensitive(False)
            
        # Add a timer callback to update the value of the progress bar
        self.timer = None

        # connect the handlers
        wtree.signal_connect("on_progress_cancel_clicked",
                                 self.on_cancel_button_clicked)

        self.dialog.connect("delete-event",
                            self.on_delete_event)

        self.dialog.connect("key-press-event",
                            self.on_key_press_event)

    def on_key_press_event(self, widget, event, data = None):
        if event.keyval == 65307: # ignore ESC
            return True

    def on_delete_event(self, widget, data = None):
        self.done = True
        self.dialog.destroy()

    def keep_pulse(self):
        self.pbar.pulse()
        return True

    def set_mode(self, mode):
        self.mode = mode
        if mode == "pulse":
            if self.timer is None:
                self.timer = gobject.timeout_add (300, self.keep_pulse)
        else:
            self.stop_pulse()
            
    def pulse(self):
        self.set_mode("pulse")

    def stop_pulse(self):
        if self.timer is not None:
            gobject.source_remove(self.timer)
            self.timer = None

    def set_fraction(self, f):
        self.set_mode("fraction")
        self.pbar.set_fraction(f)

    def show(self, widget = None,
             modal = False, parentwin = None):
            
        self.parentwin = parentwin
        #self.dialog.set_deletable(False)
        
        if parentwin:
            self.dialog.set_transient_for(parentwin)
            self.dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)


        self.dialog.show()
        if modal :
            ret = self.dialog.run()
            if ret == gtk.RESPONSE_DELETE_EVENT:
                self.done = True
                self.destroy()
                

    def destroy(self):
        self.stop_pulse()
        self.dialog.destroy()

    def on_cancel_button_clicked(self, widget):
        if self.done:
            self.destroy()
        else:
            self.status.set_text("Canceling...")
            self.cancel_pressed = True
        return True

    # integration with idle add for invocation from separate thread.
    def _update_bar(self, op, status=None, fraction=0.0):
        try:
            if self.done:
                print "Returning from _update bar as we are already done!"
                return
            gtk.gdk.threads_enter()
            #print op,status, fraction,threading.currentThread()
            if status and self.continue_op():
                self.status.set_text(status)

            if op == self.START_PULSE:
                self.pulse()
            elif op == self.STOP_PULSE:
                self.stop_pulse()
            elif op == self.SET_FRACTION:
                self.set_fraction(fraction)
            elif op == self.COMPLETE:
                self.stop_pulse()
                if status:
                    self.status.set_text(status)
                else:
                    self.status.set_text("Complete")
                self.set_fraction(1.0)
                self.cancel_button.set_sensitive(True)
                self.cancel_button.set_label("Close")
                self.done = True
            elif op == self.CANCELED:
                self.stop_pulse()
                if status:
                    self.status.set_text(status)
                else:
                    self.status.set_text("Canceled")
                self.cancel_button.set_label("Close")
                self.cancel_button.set_sensitive(True)
                self.done = True
            elif op == self.CLOSE:
                self.done = True
                self.destroy()
                
        finally:
            gtk.gdk.threads_leave()

    def update(self, op, status=None, fraction = 0.0):
        gobject.idle_add(self._update_bar, op, status, fraction)

    def set_return_val(self, ret):
        self.ret = ret

    def get_return_val(self):
        return self.ret

    def set_exception(self, ex):
        self.ex = ex

    def get_exception(self):
        return self.ex
    
    def continue_op(self):
        return not self.cancel_pressed
    
import time
import threading
from threading import Thread
class ProgressTest(Thread):
    def __init__(self, pbar):
        Thread.__init__(self)
        self.setDaemon(True)
        self.pbar = pbar

    def run(self):
        self.pbar.update(TaskProgressWnd.START_PULSE,"Unzipping")
        time.sleep(5)
        for i in range(1,10):
            #print threading.currentThread(), i
            time.sleep(1)
            self.pbar.update(TaskProgressWnd.SET_FRACTION, "unzipping",float(i)/10)
            if not self.pbar.continue_op():
                time.sleep(3)
                self.pbar.update(TaskProgressWnd.CANCELED, "Canceled by user")
                break
            
        if self.pbar.continue_op():
            self.pbar.update(TaskProgressWnd.START_PULSE,"Unzipping")
            time.sleep(5)
            self.pbar.update(TaskProgressWnd.COMPLETE)

        
if __name__ == "__main__":
    gobject.threads_init()
    gtk.gdk.threads_init()

    t1 = TaskProgressWnd("Downloading foooooo", "Starting")
    t2 = TaskProgressWnd("Downloading baaaaar", "Downloading")

    t1.show()
    p1 = ProgressTest(t1)
    p2 = ProgressTest(t2)

    p1.start()
    p2.start()
    t2.show()
        
    print threading.currentThread()
    gtk.gdk.threads_enter()
    gtk.main()
    gtk.gdk.threads_leave()

