#!/usr/bin/env python
"""
stockchkUI.py

Nexpart Stock Check lookup User Interface.  This code runs on the Windows side in the
system tray.

<....fill this out more...>

"""
try:
    import gtk
except RuntimeError:
    print "Runtime Error: Unable to initialize graphical environment."
    raise SystemExit
import sys
import os
import socket
from urllib import urlencode
from urlparse import urlunparse
from SimpleXMLRPCServer import SimpleXMLRPCServer
import gobject
from time import sleep
import wmi
import webbrowser


SCHEME = 'https'
NETLOC = 'www.nexpart.com'
PARAMS = ""
FRAGMENT = ""
REALM = ""
CLIENT_PORT = 8103
NEXPART_ICON = os.path.join(os.environ['PROGRAMFILES'], "Amador", "icons", "wrench.png")
REVISION = "0.01"





# routine to display a dialog box
def dialogbox( message, parent, mtype=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_OK,
                cancelButton=False):
    """
    Display a dialog box of type 'mtype' on the screen with the given 'message'
    """
    dialog = gtk.MessageDialog(parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                                   mtype, buttons, message)
    if cancelButton:
        if buttons == gtk.BUTTONS_OK:
            order = [gtk.RESPONSE_CANCEL, gtk.RESPONSE_OK]
        elif buttons == gtk.BUTTONS_CLOSE:
            order = [gtk.RESPONSE_CANCEL, gtk.RESPONSE_CLOSE]
        elif buttons == gtk.BUTTONS_YES_NO:
            order = [gtk.RESPONSE_CANCEL, gtk.RESPONSE_YES, gkt.RESPONSE_NO]
        else:
            order = None
        dialog.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        if order:
            settings = gtk.settings_get_default()
            settings.set_property('gtk-alternative-button-order', True)
            dialog.set_alternative_button_order(order)

    ret = dialog.run()
    dialog.destroy()
    return ret



class RPCFunctions:

    def __init__(self):
        pass

    def _listMethods(self):
        return ['stock_check']

    def stock_check(self, partno, username, password):
        url = self.stock_check_url(partno, username, password)
        webbrowser.open(url)


    def stock_check_url(self, part, username, password):
        path = "/do_login.php"
        query = urlencode({"resolution": "1280x1024", "B1": "Login", 
                           "nexpartuname": username, "pwd": password,
                           "part": part, "linecode": "", "product": "1",
                           "logout_url": "http://www.nextpart.com/login.php/"})
        url = urlunparse( (SCHEME, NETLOC, path, PARAMS, query, FRAGMENT) )
        #print "url is", url
        return url



class TrayIcon:

    def __init__(self):
        self.staticon = gtk.StatusIcon()
        self.staticon.set_from_file(NEXPART_ICON)
        #gtk.status_icon_new_from_file(TRAY_ICON)
        #self.staticon.set_from_stock(gtk.STOCK_YES)
        self.staticon.set_visible(True)
        # self.staticon.connect("popup_menu", self.popup)
        self.popup_menu = gtk.Menu()
        #menu_item = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
        menu_item = gtk.ImageMenuItem("About Nexpart StockCheck")
        menu_item.set_image(gtk.image_new_from_stock(gtk.STOCK_ABOUT, gtk.ICON_SIZE_MENU))
        menu_item.connect("activate", self.on_about)
        self.popup_menu.append(menu_item)
        #menu_item = gtk.ImageMenuItem(gtk.STOCK_QUIT)
        menu_item = gtk.ImageMenuItem("Halt Nexpart StockCheck")
        menu_item.set_image(gtk.image_new_from_stock(gtk.STOCK_QUIT, gtk.ICON_SIZE_MENU))
        menu_item.connect("activate", self.on_quit)
        self.popup_menu.append(menu_item)
        self.staticon.connect("popup-menu", self.on_popup, self.popup_menu)
        self.staticon.set_tooltip("AutoPoint's Nexpart StockCheck Interface")

    def on_popup(self, widget, button, time, menu):
        menu.show_all()
        menu.popup(None, None, None, 3, time)

    def on_about(self, widget):
        dialog = gtk.AboutDialog()
        dialog.set_name("Nexpart StockCheck - rev: ")
        dialog.set_version(REVISION)
        dialog.set_comments("AutoPoint's Nexpart StockCheck Interface for part lookup.")
        dialog.run()
        dialog.destroy()

    def on_quit(self, widget):
        dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, 
                    gtk.BUTTONS_YES_NO,
                    "Are you sure you want to halt Nexpart StockCheck Interface?")
        # for some reason windows defaults to yes...i want NO
        dialog.set_default_response(gtk.RESPONSE_NO)
        resp = dialog.run()
        dialog.destroy()
        if resp == gtk.RESPONSE_YES:
            for i in range(gtk.main_level()):
                gtk.main_quit()





class StockCheckXMLRPCServer( SimpleXMLRPCServer ):

    def __init__(self, addr):
        SimpleXMLRPCServer.__init__(self, addr, allow_none=True)
        gobject.io_add_watch(self.socket, gobject.IO_IN, self.do_request)

    def do_request(self, source, condition):
        # had to add this stupid kludge because of issue in windows sockets
        # see: http://bugs.python.org/issue9090
        # This prevents error in SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_post()
        # at the call to 'self.rfile.read(chunk_size)':
        #    "A non-blocking socket operation could not be completed immediately"
        sleep(0.5)
        self.handle_request()
        return True




def splash_screen():
    splash_screen = ProgressWindow(" Nexpart StockCheck Startup ",
                             "\n  Bringing up Nexpart StockCheck in the backgroud...  \n")
    splash_screen.pbar.set_text("Revision %s" % REVISION)
    splash_screen.splash(2000)



class ProcessFinder:

    def __init__(self):
        pass

    def running(self, program, firstarg):
        copies = 0
        for process in wmi.WMI().Win32_Process(name=program):
            arg = process.CommandLine.split()[-1]
            if arg[-1] == '"':
                arg = arg[:-1]
            if arg.endswith(firstarg):
                copies += 1

        return (copies > 1)





class ProgressWindow:

    def __init__(self, title, message):
        """
        Create a popup window with:
            label
            progress bar
            separator
            Hbutton box with cancel button
        """
        pwin = gtk.Window(gtk.WINDOW_TOPLEVEL)
        pwin.set_title(title)
        pwin.set_border_width(10)
        vbox = gtk.VBox(spacing=4)
        self.label = gtk.Label(message)
        #self.label.set_justify(gtk.JUSTIFY_LEFT)
        self.pbar = gtk.ProgressBar()
        vbox.pack_start(self.label, False, False, 4)
        ###vbox.pack_start(self.label, True, True, 4)
        vbox.pack_start(self.pbar, False, False, 4)
        vbox.pack_start(gtk.HSeparator(), False, False, 2)
        hbuttonbox = gtk.HButtonBox()
        self.cancelbutton = gtk.Button(stock=gtk.STOCK_CANCEL)
        hbuttonbox.pack_end(self.cancelbutton, False, False, 0)
        hbuttonbox.set_layout(gtk.BUTTONBOX_END)
        vbox.pack_start(hbuttonbox, False, False, 0)
        pwin.connect("destroy-event", gtk.main_quit)
        pwin.connect("delete-event", gtk.main_quit)
        self.cancelbutton.connect("clicked", self.cancelbutton_clicked)
        pwin.add(vbox)
        self.pwin = pwin
        self.cancelled = False
        self.visible = False

    def run(self):
        self.pwin.show_all()
        gtk.main()

    def cancelbutton_clicked(self, widget):
        self.cancelled = True
        if gtk.main_level() == 0:
            raise SystemExit
        gtk.main_quit()

    def close(self, delay=1000, func=None):
        """
        Optional(?) routine to execute before closing popup window
        """
        if self.cancelled:
            self.cancelled = False
            # force to not execute any closeout function
            func = None
        self.cancelbutton.set_sensitive(False)
        if func:
            # set timeout for gtk.main() loop to exit
            gobject.timeout_add(delay, self.timer_callback)
            # automatically passes itself to function
            func(self)
            gtk.main()
        else:
            # destroy yourself!
            gobject.timeout_add(250, self.timer_callback)
            #gobject.timeout_add(delay, self.timer_callback)
            #self.pwin.destroy()
            gtk.main()

    def timer_callback(self):
        self.pwin.destroy()
        gtk.main_quit()

    def splash(self, delay=1500):
        gobject.timeout_add(delay, self.timer_callback)
        self.pbar.set_fraction(1.00)
        self.pwin.show_all()
        gtk.main()

    def fraction(self, frac, msg=None):
        if not self.visible:
            self.pwin.show_all()
            self.visible = True
        if msg:
            self.label.set_text(msg)
        if frac is not None:
            self.pbar.set_fraction(frac)
        while gtk.events_pending():
            gtk.main_iteration(False)






if __name__ == "__main__":
    try:
        if ProcessFinder().running("pythonw.exe", "stockchkUI.py"):
            raise IOError("Nexpart StockCheck is already running.")
        splash_screen()
        client_ip = socket.gethostbyname(socket.gethostname())
        server = StockCheckXMLRPCServer((client_ip, CLIENT_PORT))
        server.register_introspection_functions()
        server.register_instance(RPCFunctions())
        # skip this if running in the background
        ##server.serve_forever()
        # create tray icon for this program
        TrayIcon()
    except (IOError, socket.error), err:
        dialogbox(str(err), None, gtk.MESSAGE_ERROR)
    else:
        gtk.main()
