#!/usr/bin/python
#
#    byobu-config
#    Copyright (C) 2008 Canonical Ltd.
#
#    Authors: Nick Barcet <nick.barcet@ubuntu.com>
#             Dustin Kirkland <kirkland@ubuntu.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, version 3 of the License.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

# If you change any strings, please generate localization information with:
#       ./debian/rules get-po


import sys, os, os.path, time, string, commands, gettext, glob, snack
from ConfigParser import SafeConfigParser
from snack import *

PKG="byobu"
HOME=os.getenv("HOME")
USER=os.getenv("USER")
BYOBU_CONFIG_DIR=os.getenv("BYOBU_CONFIG_DIR", HOME+"/.byobu")
BYOBU_BACKEND=os.getenv("BYOBU_BACKEND", "screen")
BYOBU_SOCKETDIR=os.getenv("SOCKETDIR", "/var/run/screen")
BYOBU_PREFIX = os.getenv("BYOBU_PREFIX", "/usr")
SHARE=BYOBU_PREFIX+'/share/'+PKG
DOC=BYOBU_PREFIX+'/share/doc/'+PKG
if not os.path.exists(SHARE):
    SHARE = BYOBU_CONFIG_DIR+"/"+SHARE
if not os.path.exists(DOC):
    DOC = BYOBU_CONFIG_DIR+"/"+DOC
DEF_ESC="A"
RELOAD = "If you are using the default set of keybindings, press\n<F5> or <ctrl-a-R> to activate these changes.\n\nOtherwise, exit this session and start a new one."
RELOAD_FLAG="%s/reload-required" % (BYOBU_CONFIG_DIR)
RELOAD_CMD="%s -X at 0 source %s/profile" % (BYOBU_BACKEND, BYOBU_CONFIG_DIR)
ESC = ''
snack.hotkeys[ESC] = ord(ESC)
snack.hotkeys[ord(ESC)] = ESC
if commands.getstatusoutput('which gsed')[0] == 0:
	SED = "gsed"
else:
	SED = "sed"
gettext.bindtextdomain(PKG, SHARE+'/po')
gettext.textdomain(PKG)
_ = gettext.gettext

# Command presets for windows creation
cmd=(   ("System activity", "top", "top"),
        ("System log", "log", "watch -n 10 tail -n 5 /var/log/syslog /var/log/auth.log /var/log/dmesg"),
        ("Disk and ram usage", "mem", 'watch -n 30 "df -h; echo ""; free -mt"'))


def ioctl_GWINSZ(fd):                  #### TABULATION FUNCTIONS
    try:                                ### Discover terminal width
        import fcntl, termios, struct, os
        cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
    except:
        return None
    return cr

def reload_required():
    f = open(RELOAD_FLAG,'w')
    f.close()
    commands.getoutput(RELOAD_CMD)

def terminal_size():                    ### decide on *some* terminal size
    cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)  # try open fds
    if not cr:                                                  # ...then ctty
        try:
            fd = os.open(os.ctermid(), os.O_RDONLY)
            cr = ioctl_GWINSZ(fd)
            os.close(fd)
        except:
            pass
    if not cr:                            # env vars or finally defaults
        try:
            cr = (env['LINES'], env['COLUMNS'])
        except:
            cr = (25, 80)
    return int(cr[1]-5), int(cr[0]-5)         # reverse rows, cols

def menu(snackScreen, size, isInstalled):
    if isInstalled:
        installtext=_("Byobu currently launches at login (toggle off)")
    else:
        installtext=_("Byobu currently does not launch at login (toggle on)")


    li = Listbox(height = 9, width = 60, returnExit = 1)
    li.append(_("Help -- Quick Start Guide"), 1)
    li.append(_("Change Byobu's background color"), 2)
    li.append(_("Change Byobu's foreground color"), 3)
    li.append(_("Toggle status notifications"), 4)
    li.append(_("Change keybinding set"), 5)
    li.append(_("Change escape sequence"), 6)
    li.append(_("Create new windows"), 7)
    li.append(_("Manage default windows"), 8)
    li.append(installtext, 9)
    bb = ButtonBar(snackScreen, (("Exit", "exit", ESC),), compact=1)

    g = GridForm(snackScreen, _(" Byobu Configuration Menu"), 1, 2)
    g.add(li, 0, 0, padding=(4,2,4,2))
    g.add(bb, 0, 1, padding=(1,1,0,0))

    if bb.buttonPressed(g.runOnce()) == "exit":
        return 0
    else:
        return li.current()

def messagebox(snackScreen, width, height, title, text, scroll=0, \
    buttons=((_("Okay"), "okay"),(_("Cancel"), "cancel", ESC)) ):

    t = Textbox(width, height, text, scroll=scroll )
    bb = ButtonBar(snackScreen, buttons, compact = 1)
    g = GridForm(snackScreen, title, 1, 2)
    g.add(t, 0, 0, padding=(0,0,0,0))
    g.add(bb, 0, 1, padding=(1,1,0,0))

    return bb.buttonPressed(g.runOnce())

def help(snackScreen, size, config):
    f=file(DOC+'/help.txt')
    text=f.read()
    f.close()

    text=text.replace("<esckey>", getesckey(), 1)
    text=text.replace("_VER_", commands.getoutput("byobu -v | head -n1 | " + SED + " 's/.* //'"), 1)

    t = Textbox(70, 14, text, scroll=0)
    bb = ButtonBar(snackScreen, ((_("Menu"), "menu", ESC),), compact = 1)
    g = GridForm(snackScreen, _("Byobu Help"), 1, 3)
    g.add(t, 0, 0, padding=(0,0,0,0))
    g.add(bb, 0, 2, padding=(1,1,0,0))

    button = bb.buttonPressed(g.runOnce())

    return 100

def select_color(snackScreen, size, layer):
    li = Listbox(height = 8, width = 60, scroll = 1, returnExit = 1)

    for choice in commands.getoutput('byobu-select-profile -l').splitlines():
        li.append(choice, choice)

    bb = ButtonBar(snackScreen, ((_("Apply"), "apply"), (_("Cancel"), "cancel", ESC)), compact = 1)

    if layer == "foreground":
        g = GridForm(snackScreen, _("Choose a foreground color:"), 1, 2)
    else:
        g = GridForm(snackScreen, _("Choose a background color:"), 1, 2)

    g.add(li, 0, 0, padding=(4,2,4,2))
    g.add(bb, 0, 1, padding=(1,1,0,0))

    if bb.buttonPressed(g.runOnce()) != "cancel":
        if layer == "foreground":
            commands.getoutput('byobu-select-profile --foreground %s' % li.current())
        else:
            commands.getoutput('byobu-select-profile --background %s' % li.current())
	reload_required()
    return 100

def keybindings(snackScreen, size):
    li = Listbox(height = 6, width = 60, returnExit = 1)
    for choice in commands.getoutput('ls '+SHARE+'/keybindings').splitlines():
        if choice != "common" and choice != "none":
            li.append(choice, choice)
    li.append("none", "none")
    bb = ButtonBar(snackScreen, ((_("Apply"), "apply"), (_("Cancel"), "cancel", ESC)), compact = 1)
    g = GridForm(snackScreen, _("Which set of keybindings would you like to use?"), 1, 2)
    g.add(li, 0, 0, padding=(4,2,4,2))
    g.add(bb, 0, 1, padding=(1,1,0,0))
    if bb.buttonPressed(g.runOnce()) != "cancel":
        switch_keybindings(li.current())
	reload_required()
    return 100

def switch_keybindings(set):
    commands.getoutput(SED + " -i -e 's:^source .*$:source "+SHARE+"/keybindings/"+set+":' "+BYOBU_CONFIG_DIR+"/keybindings")

def newwindow(snackScreen, size):
    title=Entry(8, text="shell", returnExit=1)
    titlel=Label(_("Title: "))
    command=Entry(20, text="/bin/sh", returnExit=1)
    commandl=Label(_("Command: "))
    path=Entry(20, text="~", returnExit=1)
    pathl=Label(_("Path: "))

    rl=Label(_("Presets: "))
    if len(cmd) > 10:
        scroll=1
        size=10
    else:
        scroll=0
        size = len(cmd)

    r=CheckboxTree(size, scroll=scroll)
    count=0
    for cur in cmd:
        r.append(cur[0], count)
        count=count+1

    cb=Checkbox(_("Add to default windows"))

    bb = ButtonBar(snackScreen, ((_("Apply"), "apply"), (_("Cancel"), "cancel", ESC)), compact = 1)

    g = GridForm(snackScreen, _("Create new window(s):"), 2, 6 )
    g.add(titlel, 0, 0, anchorLeft=1,padding=(4,1,0,1))
    g.add(title, 1, 0, anchorLeft=1)
    g.add(commandl, 0, 1, anchorLeft=1, anchorTop=1,padding=(4,0,0,1))
    g.add(command, 1, 1, anchorLeft=1)
    g.add(pathl, 0, 2, anchorLeft=1, anchorTop=1,padding=(4,0,0,1))
    g.add(path, 1, 2, anchorLeft=1)
    g.add(rl, 0, 3, anchorLeft=1,padding=(4,0,0,1))
    g.add(r, 1, 3)
    g.add(cb, 1, 4, padding=(4,1,0,1))
    g.add(bb, 1, 5, padding=(4,1,0,0))

    if bb.buttonPressed(g.runOnce()) != "cancel":
        sel=r.getSelection()
        if sel:
            for s in sel:
                win='%s -t %s %s' % (BYOBU_BACKEND, cmd[s][1], cmd[s][2])
                commands.getoutput(win)
                if cb.value():
                    appendwindow(win)
        else:
	    cpath='%s -X setenv newpath %s' % (BYOBU_BACKEND, path.value())
            win='%s -X eval \'chdir $newpath\' \"%s -t %s %s\"' % (BYOBU_BACKEND, BYOBU_BACKEND, title.value(), command.value())
	    commands.getoutput(cpath)
            commands.getoutput(win)
            if cb.value():
	    	cpath='chdir %s' % (path.value())
            	win='%s -t %s %s' % (BYOBU_BACKEND, title.value(), command.value())
		appendwindow(cpath)
                appendwindow(win)

    return 100

def appendwindow(win):
    f=open(BYOBU_CONFIG_DIR+'/windows', 'a')
    try:
        f.write(win+"\n")

    except IOError:
        f.close()
        return None

    f.close()

def readwindows():
    if not os.path.isfile(BYOBU_CONFIG_DIR+'/windows'):
        windowsfile=SHARE+'/windows/common'
    elif os.path.getsize(BYOBU_CONFIG_DIR+'/windows') == 0:
        windowsfile=SHARE+'/windows/common'
    else:
        windowsfile=BYOBU_CONFIG_DIR+'/windows'
    f=open(windowsfile)
    try:
        li=[]
        for line in f.readlines():
            if line.startswith("# "):
               # this is a comment
               window=[-1, line]
            elif line.startswith("#"):
               # this is an inactive window
               window=[0, line.lstrip("#")]
            else:
               window=[1, line]
            li.append(window)

        return li

    except IOError:
        f.close()
        return None

    f.close()

def readstatus():
    status={}
    glo = {}
    loc = {}
    for f in [SHARE+'/status/status', BYOBU_CONFIG_DIR+'/status']:
        if os.path.exists(f):
            try:
                execfile(f, glo, loc)
            except:
                error("Invalid configuration [%s]" % f)
            if BYOBU_BACKEND == "tmux":
                items = "%s %s" % (loc["tmux_left"], loc["tmux_right"])
            else:
                items = "%s %s %s %s" % (loc["screen_upper_left"], loc["screen_upper_right"], loc["screen_lower_left"], loc["screen_lower_right"])
            for i in items.split():
                if i.startswith("#"):
                    i = i.replace("#", "")
                    status[i] = "0"
                else:
                    status[i] = "1"
    li=[]
    keys = status.keys()
    keys.sort()
    for i in keys:
        window=[int(status[i]), i]
        li.append(window)
    return li

def genstatusstring(s, status):
    new = ""
    glo = {}
    loc = {}
    execfile(SHARE+'/status/status', glo, loc)
    for i in loc[s].split():
        if i.startswith("#"):
                i = i.replace("#", "")
        if status[i] == 1:
            new += " " + i
        else:
            new += " #" + i
    return new

def writestatus(items):
    status={}
    path = BYOBU_CONFIG_DIR+'/status'
    for i in items:
        status[i[1]] = i[0]
    # SED is hacky here, but effective
    if BYOBU_BACKEND == "tmux":
	for key in ["tmux_left", "tmux_right"]:
            val = genstatusstring(key, status)
            commands.getoutput("%s -i -e '/^%s=/d' %s" % (SED, key, path))
            commands.getoutput("echo '%s=\"%s\"' >> %s" % (key, val, path))
    else:
	for key in ["screen_upper_left", "screen_upper_right", "screen_lower_left", "screen_lower_right"]:
            val = genstatusstring(key, status)
            commands.getoutput("%s -i -e '/^%s=/d' %s" % (SED, key, path))
            commands.getoutput("echo '%s=\"%s\"' >> %s" % (key, val, path))

def togglestatus(snackScreen, size):
    itemlist=readstatus()
    rl=Label("")
    r=CheckboxTree(12, scroll=1)
    count=0
    for item in itemlist:
       if item[0] != -1:
            r.append(item[1],count,selected=item[0])
       count=count+1
    bb = ButtonBar(snackScreen, ((_("Apply"), "apply"), (_("Cancel"), "cancel", ESC)), compact = 1)
    g = GridForm(snackScreen, _("Toggle status notifications:"), 2, 4 )
    g.add(rl, 0, 0, anchorLeft=1, anchorTop=1, padding=(4,0,0,1))
    g.add(r, 1, 0)
    g.add(bb, 1, 1, padding=(4,1,0,0))
    if bb.buttonPressed(g.runOnce()) != "cancel":
        count=0
        for item in itemlist:
            if item[0] != -1:
                item[0] = r.getEntryValue(count)[1]
            count=count+1
        writestatus(itemlist)
	reload_required()
    return 100

def writewindows(winlist):
    f=open(BYOBU_CONFIG_DIR+'/windows', 'w')
    try:
        for win in winlist:
            if win[0] == -1:
                f.write(win[1])
            elif win[0] == 0:
                f.write("#"+win[1])
            else:
                f.write(win[1])
    except IOError:
        f.close()
        return None
    f.close()

def defaultwindows(snackScreen, size):
    winlist=readwindows()

    rl=Label(_("Windows:"))
    r=CheckboxTree(10, scroll=1)
    count=0
    for win in winlist:
       if win[0] != -1:
            r.append(win[1],count,selected=win[0])
       count=count+1

    bb = ButtonBar(snackScreen, ((_("Apply"), "apply"), (_("Cancel"), "cancel", ESC)), compact = 1)

    g = GridForm(snackScreen, _("Select window(s) to create by default:"), 2, 4 )
    g.add(rl, 0, 0, anchorLeft=1, anchorTop=1, padding=(4,0,0,1))
    g.add(r, 1, 0)
    g.add(bb, 1, 1, padding=(4,1,0,0))

    if bb.buttonPressed(g.runOnce()) != "cancel":
        count=0
        for win in winlist:
            if win[0] != -1:
                win[0] = r.getEntryValue(count)[1]
            count=count+1

        writewindows(winlist)

    return 100

def install(snackScreen, size, isInstalled):
    if isInstalled:
        out = commands.getoutput("byobu-launcher-uninstall")
        if out == "":
            out = _("Byobu will not be used next time you login.")
        button = messagebox(snackScreen, 60, 2, _("Message"), out, buttons=((_("Menu"), )))
        return 101
    else:
        out = commands.getoutput("byobu-launcher-install")
        if out == "":
            out = _("Byobu will be launched automatically next time you login.")
        button = messagebox(snackScreen, 60, 2, "Message", out, buttons=((_("Menu"), )))
        return 100

def appendtofile(p, s):
    f = open(p, 'a')
    try:
        f.write(s)
    except IOError:
        f.close()
        return
    f.close()
    return


def getesckey():
    path=BYOBU_CONFIG_DIR+'/keybindings'
    if not os.path.exists(path):
        return DEF_ESC
    line = commands.getoutput("grep ^escape "+path)
    if line == "":
        return DEF_ESC
    esc = line[line.find('^')+1]
    if esc == "`":
        esc = " "
    return esc

def setesckey(key):
    path = BYOBU_CONFIG_DIR+'/keybindings'
    if key != "":
        if key == " ":
            key = "`"
        u = key[0].upper()
        l = key[0].lower()
        if os.path.exists(path):
            out = commands.getoutput(SED+" -i -e 's/^escape.*$//' "+path)
            appendtofile(path, "escape ^"+u+l+"\n")
            out = commands.getoutput(SED+" -i -e 's/^register.*$//' "+path)
            out = commands.getoutput("grep -sh '^register x' "+SHARE+"/keybindings/*keys")
            appendtofile(path, out+"\n")
            out = commands.getoutput(SED+" -i -e 's/\"^^/\"\^"+l+"/g' "+path)
            out = commands.getoutput(SED+" -i -e '/^$/d' "+path)

def chgesc(snackScreen, size):
    esc=Entry(2, text=getesckey(), returnExit=1)
    escl=Label(_("Escape key: ctrl-"))
    bb = ButtonBar(snackScreen, ((_("Apply"), "apply"), (_("Cancel"), "cancel", ESC)), compact = 1)

    g = GridForm(snackScreen, _("Change escape sequence:"), 2, 4 )
    g.add(escl, 0, 0, anchorLeft=1, padding=(1,0,0,1))
    g.add(esc, 1, 0, anchorLeft=1)
    g.add(bb, 1, 1)
    g.setTimer(100)
    loop=1
    while loop:
        which=g.run()
        if which == "TIMER":
            val=esc.value()
            if len(val) > 1:
                esc.set(val[1])
            # Ensure that escape sequence is not \ or /
            if val == '/' or val == '\\':
                esc.set(DEF_ESC)
            # Ensure that the escape sequence is not set to a number
            try:
                dummy = int(esc.value())
                esc.set(DEF_ESC)
            except:
               # do nothing
               dummy = "foo"
        else:
            loop=0
    snackScreen.popWindow()
    button = bb.buttonPressed(which)
    if button != "cancel":
        setesckey(esc.value())
	reload_required()
        if button == "exit":
            return 0
    return 100

def autolaunch():
    if os.path.exists(BYOBU_CONFIG_DIR+"/disable-autolaunch"):
	return 0
    if commands.getstatusoutput('grep -qs byobu-launch %s/.profile' % HOME)[0] == 0:
	return 1
    if os.path.exists("/etc/profile.d/Z98-%s.sh" % PKG):
	return 1
    return 0


def main():
    """This is the main loop of our utility
    """

    size = terminal_size()
    snackScreen = SnackScreen()
    snackScreen.drawRootText(1,0,_(' Byobu Configuration Menu'))
    snackScreen.pushHelpLine(_('<Tab>/<Alt-Tab> between elements | <Enter> selects | <Esc> exits'))

    config = SafeConfigParser()

    isInstalled = autolaunch()

    tag = 100

    while tag > 0 :
        tag = menu(snackScreen, size, isInstalled)
        if tag == 1:
            tag = help(snackScreen, size, config)
        elif tag == 2:
            tag = select_color(snackScreen, size, "background")
        elif tag == 3:
            tag = select_color(snackScreen, size, "foreground")
        elif tag == 4:
            tag = togglestatus(snackScreen, size)
        elif tag == 5:
            tag = keybindings(snackScreen, size)
	elif tag == 6:
            tag = chgesc(snackScreen, size)
        elif tag == 7:
            tag = newwindow(snackScreen, size)
        elif tag == 8:
            tag = defaultwindows(snackScreen, size)
        elif tag == 9:
            tag = install(snackScreen, size, isInstalled)
            isInstalled = autolaunch()

    snackScreen.finish()
    sys.exit(0)


if __name__ == "__main__": main()
