#!/usr/bin/env python2
# coding:utf-8
# Contributor:
#      Phus Lu        <phus.lu@gmail.com>

__version__ = '1.6'

GOAGENT_LOGO_DATA = """\
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"""

import sys
import os
import re
import thread
import base64
import platform

try:
    import pygtk
    pygtk.require('2.0')
    import gtk
    # gtk.gdk.threads_init()
except Exception:
    sys.exit(os.system(u'gdialog --title "XX-Mini GTK" --msgbox "\u8bf7\u5b89\u88c5 python-gtk2" 15 60'.encode(sys.getfilesystemencoding() or sys.getdefaultencoding(), 'replace')))
try:
    import pynotify
    pynotify.init('XX-Mini Notify')
except ImportError:
    pynotify = None
try:
    import appindicator
except ImportError:
    appindicator = None
try:
    import vte
except ImportError:
    sys.exit(gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, u'请安装 python-vte').run())


def spawn_later(seconds, target, *args, **kwargs):
    def wrap(*args, **kwargs):
        import time
        time.sleep(seconds)
        return target(*args, **kwargs)
    return thread.start_new_thread(wrap, args, kwargs)


def drop_desktop():
    filename = os.path.abspath(__file__)
    dirname = os.path.dirname(filename)
    DESKTOP_FILE = '''\
#!/usr/bin/env xdg-open
[Desktop Entry]
Type=Application
Name=XX-Mini GTK
Comment=XX-Mini GTK Launcher
Categories=Network;Proxy;
Exec=/usr/bin/env python "%s"
Icon=%s/goagent-logo.png
Terminal=false
StartupNotify=true
''' % (filename, dirname)
    for dirname in map(os.path.expanduser, ['~/Desktop', u'~/桌面']):
        if os.path.isdir(dirname):
            filename = os.path.join(dirname, 'XX-Mini-gtk.desktop')
            with open(filename, 'w') as fp:
                fp.write(DESKTOP_FILE)
            os.chmod(filename, 0755)


def should_visible():
    import ConfigParser
    ConfigParser.RawConfigParser.OPTCRE = re.compile(r'(?P<option>[^=\s][^=]*)\s*(?P<vi>[=])\s*(?P<value>.*)$')
    config = ConfigParser.ConfigParser()
    config.read(['proxy.ini', 'proxy.user.ini'])
    visible = config.has_option('listen', 'visible') and config.getint('listen', 'visible')
    return visible

#gtk.main_quit = lambda: None
#appindicator = None


class GoAgentGTK:

    command = ['/usr/bin/env', 'python', 'proxy.py']
    message = u'XX-Mini已经启动，单击托盘图标可以最小化'
    fail_message = u'XX-Mini启动失败，请查看控制台窗口的错误信息。'

    def __init__(self, window, terminal):
        self.window = window
        self.window.set_size_request(652, 447)
        self.window.set_position(gtk.WIN_POS_CENTER)
        self.window.connect('delete-event',self.delete_event)
        self.terminal = terminal

        for cmd in ('python2.7', 'python27', 'python2'):
            if os.system('which %s' % cmd) == 0:
                self.command[1] = cmd
                break

        self.window.add(terminal)
        self.childpid = self.terminal.fork_command(self.command[0], self.command, os.getcwd())
        if self.childpid > 0:
            self.childexited = self.terminal.connect('child-exited', self.on_child_exited)
            self.window.connect('delete-event', lambda w, e: gtk.main_quit())
        else:
            self.childexited = None

        spawn_later(0.5, self.show_startup_notify)

        if should_visible():
            self.window.show_all()

        logo_filename = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'goagent-logo.png')
        if not os.path.isfile(logo_filename):
            with open(logo_filename, 'wb') as fp:
                fp.write(base64.b64decode(GOAGENT_LOGO_DATA))
        self.window.set_icon_from_file(logo_filename)

        if appindicator:
            self.trayicon = appindicator.Indicator('XX-Mini', 'indicator-messages', appindicator.CATEGORY_APPLICATION_STATUS)
            self.trayicon.set_status(appindicator.STATUS_ACTIVE)
            self.trayicon.set_attention_icon('indicator-messages-new')
            self.trayicon.set_icon(logo_filename)
            self.trayicon.set_menu(self.make_menu())
        else:
            self.trayicon = gtk.StatusIcon()
            self.trayicon.set_from_file(logo_filename)
            self.trayicon.connect('popup-menu', lambda i, b, t: self.make_menu().popup(None, None, gtk.status_icon_position_menu, b, t, self.trayicon))
            self.trayicon.connect('activate', self.show_hide_toggle)
            self.trayicon.set_tooltip('XX-Mini')
            self.trayicon.set_visible(True)

    def make_menu(self):
        menu = gtk.Menu()
        itemlist = [(u'\u663e\u793a', self.on_show),
                    (u'\u9690\u85cf', self.on_hide),
                    (u'\u505c\u6b62', self.on_stop),
                    (u'\u91cd\u65b0\u8f7d\u5165', self.on_reload),
                    (u'\u9000\u51fa', self.on_quit)]
        for text, callback in itemlist:
            item = gtk.MenuItem(text)
            item.connect('activate', callback)
            item.show()
            menu.append(item)
        menu.show()
        return menu

    def show_notify(self, message=None, timeout=None):
        if pynotify and message:
            notification = pynotify.Notification('XX-Mini Notify', message)
            notification.set_hint('x', 200)
            notification.set_hint('y', 400)
            if timeout:
                notification.set_timeout(timeout)
            notification.show()

    def show_startup_notify(self):
        if self.check_child_exists():
            self.show_notify(self.message, timeout=3)

    def check_child_exists(self):
        if self.childpid <= 0:
            return False
        cmd = 'ps -p %s' % self.childpid
        lines = os.popen(cmd).read().strip().splitlines()
        if len(lines) < 2:
            return False
        return True

    def on_child_exited(self, term):
        if self.terminal.get_child_exit_status() == 0:
            gtk.main_quit()
        else:
            self.show_notify(self.fail_message)

    def on_show(self, widget, data=None):
        self.window.show_all()
        self.window.present()
        self.terminal.feed('\r')

    def on_hide(self, widget, data=None):
        self.window.hide_all()

    def on_stop(self, widget, data=None):
        if self.childexited:
            self.terminal.disconnect(self.childexited)
        os.system('kill -9 %s' % self.childpid)

    def on_reload(self, widget, data=None):
        if self.childexited:
            self.terminal.disconnect(self.childexited)
        os.system('kill -9 %s' % self.childpid)
        self.on_show(widget, data)
        self.childpid = self.terminal.fork_command(self.command[0], self.command, os.getcwd())
        self.childexited = self.terminal.connect('child-exited', lambda term: gtk.main_quit())

    def show_hide_toggle(self, widget, data= None):
        if self.window.get_property('visible'):
            self.on_hide(widget, data)
        else:
            self.on_show(widget, data)

    def delete_event(self, widget, data=None):
        self.on_hide(widget, data)
        # 默认最小化至托盘
        return True

    def on_quit(self, widget, data=None):
        gtk.main_quit()


def main():
    global __file__
    __file__ = os.path.abspath(__file__)
    if os.path.islink(__file__):
        __file__ = getattr(os, 'readlink', lambda x: x)(__file__)
    os.chdir(os.path.dirname(os.path.abspath(__file__)))

    if not os.path.exists('goagent-logo.png'):
        # first run and drop shortcut to desktop
        drop_desktop()

    window = gtk.Window()
    terminal = vte.Terminal()
    GoAgentGTK(window, terminal)
    gtk.main()

if __name__ == '__main__':
    main()
