#!/usr/bin/python2
"""View the output from osg-system-profiler in a structured way"""

from Tkinter import *
import ScrolledText
import os
import re
import subprocess
import sys
import urllib2
import tkFont



INSTALLED_VERSIONS_SCRIPT = 'osg-installed-versions'
RELEASE_INFO_PATH = '/p/vdt/public/html/release-info'


class Section(object):
    "A section from the profiler output"
    def __init__(self, label, text=""):
        self.label = label
        self.text = text


def is_empty(text):
    "True if a block of text is empty except for whitespace"
    return not re.search('\S', text)


def load_pdata_from_handle(fh):
    pdata = []
    section = Section("TOP")
    in_files_section = False
    in_file = False
    for line in fh:
        section_match = re.match(r'[*]{5}\s+(.+?)\s*$', line)
        file_match = re.match(r'File:\s*(.+?)\s*$', line)

        end_of_section = section_match or (in_files_section and file_match)
        if end_of_section:
            if is_empty(section.text):
                if in_file:
                    section.label += ' (empty)'
                else:
                    section.label += ' (no text)'
            pdata.append(section)

            if section_match:
                section_name = section_match.group(1)
                section = Section(section_name)
                in_files_section = section_name.startswith('Files in')
                in_file = False
            elif in_files_section and file_match:
                section = Section(file_match.group(1))
                in_file = True
        else:
            section.text += line

    pdata.append(section)
    return pdata


def load_pdata_from_file(filename):
    "Load profiler data from filename and return as a list of Section objects"
    pdata = []
    try:
        fh = open(filename, 'r')
        try:
            return load_pdata_from_handle(fh)
        finally:
            fh.close()
    except IOError, e:
        print >> sys.stderr, "Error reading file %s: %s" % (filename, e.strerror)
        sys.exit(1)


def load_pdata_from_url(url):
    try:
        urlhandle = urllib2.urlopen(url)
        try:
            return load_pdata_from_handle(urlhandle)
        finally:
            urlhandle.close()
    except urllib2.URLError, e:
        print >> sys.stderr, "Error reading URL %s: %s" % (url, e.strerror)
        sys.exit(1)


def get_installed_versions(profile, script=None):
    env = dict(os.environ)
    if script is None:
        env['PATH'] = env['PATH'] + ':' + os.path.dirname(sys.argv[0])
        script = INSTALLED_VERSIONS_SCRIPT
    output = ''
    cmd = [script, '-p', profile]
    if os.path.exists(RELEASE_INFO_PATH):
        cmd.append('--afs')
    try:
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=env)
        output = proc.communicate()[0]
    except OSError, err:
        return Section('Error getting installed versions', str(err))
    if output:
        return Section('Installed versions', output)
    else:
        return Section('Installed versions (empty)', output)


class Application(Frame):
    "The GUI"

    def __init__(self, master, pdata):
        self.master = master
        Frame.__init__(self, master)
        self.pack(fill=BOTH, expand=True)

        self.font = tkFont.Font(family='Sans', size=12)
        self.textfont = tkFont.Font(family='Monospace', size=12)

        self.top = Frame(self)
        self.top.pack(side=TOP, fill=X)

        self.quit_btn = Button(self.top, text="QUIT", fg="red", command=self.quit, font=self.font)
        self.quit_btn.pack(side=LEFT)

        self.copy_section_btn = Button(self.top, text="Copy Section to X Clipboard", command=self.copy_section, font=self.font)
        self.copy_section_btn.pack(side=LEFT)

        self.bot = Frame(self)
        self.bot.pack(side=BOTTOM, fill=BOTH, expand=True)

        self.section_lbx = Listbox(self.bot, selectmode=BROWSE, font=self.font)
        self.section_lbx.pack(side=LEFT, fill=BOTH, expand=True)
        self.current = None

        self.label = Label(self.top, text="", font=self.font)
        self.label.pack(side=LEFT, fill=X)

        self.text = ScrolledText.ScrolledText(self.bot, font=self.textfont)
        self.text.pack(side=RIGHT, fill=BOTH, expand=True)

        self.pdata = pdata
        self.populate()

        self.poll()

    def populate(self):
        "Fill the section list box with the labels"
        self.section_lbx.delete(0, END)
        for section in self.pdata:
            self.section_lbx.insert(END, section.label)

    def poll(self):
        "Check if selection has changed"
        now = self.section_lbx.curselection()
        if now != self.current:
            self.list_has_changed(now)
            self.current = now
        self.after(250, self.poll)

    def list_has_changed(self, now):
        "Update the label and the text box"
        if now:
            idx = int(now[0])
            self.label.configure(text=self.pdata[idx].label)
            self.text.config(state=NORMAL)
            self.text.delete(1.0, END)
            self.text.insert(END, self.pdata[idx].text)
            self.text.config(state=DISABLED)

    def copy_section(self):
        "Copy all text in section to the X clipboard"
        self.master.clipboard_clear()
        self.master.clipboard_append(self.text.get(1.0, END))


def main():
    if len(sys.argv) < 2:
        print "Usage: %s <FILENAME OR URL>" % sys.argv[0]
        print "View the output of osg-system-profiler"
        sys.exit(2)
    profile = sys.argv[1]
    if '://' not in profile:
        pdata = load_pdata_from_file(profile)
    else:
        pdata = load_pdata_from_url(profile)
    installed_versions = get_installed_versions(profile)
    if installed_versions:
        pdata.append(installed_versions)
    root = Tk()
    root.wm_title("osg-system-profiler viewer")
    app = Application(root, pdata)
    root.mainloop()


if __name__ == '__main__':
    main()

