#!/usr/bin/env python

import urllib2
import tempfile
import os, sys
import shutil
from subprocess import Popen, PIPE, call
from optparse import OptionParser

# TODO: update the .hgignore from the apt-hook

__version__ = "0.1.2"

HG_V = "0.9.5"
HG_D = "mercurial-%s" % HG_V
HG_PACK = "%s.tar.gz" % HG_D
HG_URL = "http://www.selenic.com/mercurial/release/" + HG_PACK

DEPS = ["cdbs", "debhelper", "diffstat", "gawk", "gcc", "gettext", "mawk",
        "patchutils", "python-all", "python-all-dev", "quilt", "ucf"]

IGNORE = "/etc/.hgignore"

PY_EXE = sys.executable
PY_MAJ, PY_MIN = sys.version_info[:2]

XTRA_FUNCTS = """
# Override the Mercurial username for better logs
function ecom () {
  export HGUSER=$1;
  shift;
  hg commit "$@";
}
"""

XTRA_VARS = """
export PATH=/opt/%s/bin:$PATH
export PYTHONPATH=/opt/%s/lib/python%d.%d/site-packages:$PYTHONPATH
""" % (HG_D, HG_D, PY_MAJ, PY_MIN)

APT_PATH = "/usr/bin/apt-get"
APT_SNAP_PATH = "/etc/apt/hg-snapshot-script"

APT_SNAP = r"""#!/bin/sh
set -e

caller=$(ps axww | mawk '/aptitude|apt-get/ {for (i=5; i<=NF ; i++) printf ("%s ",$i); printf ("\n") }' | head -1)

hg addremove 1>/dev/null
STATUS="$(hg st)"

export HGUSER=apt_auto

if [ -z "$STATUS" ] ; then
   echo "hg-snapshot-script: nothing to be done"
else
   case "$1" in
        pre)
           echo "hg-snapshot-script found changed files:"
           hg st
           hg ci -m "snapshot before: $caller"
          ;;
        post)
           echo "hg-snapshot-script found changed files:"
           hg st
           hg ci -m "snapshot after: $caller"
          ;;
        *)
           echo "hg-snapshot-script found changed files:"
           hg st
           hg ci -m "snapshot on $(date '+%Y-%m-%d - %H:%M:%S')"
          ;;
   esac
fi
"""

APT_HOOK = """
DPkg {
  Pre-Invoke  {"cd /etc ; ./apt/hg-snapshot-script pre";};
  Post-Invoke {"cd /etc ; ./apt/hg-snapshot-script post";};
}
"""

# No distro currently ship with Mercurial 0.9.5 so we install it
def install_hg(apt=True):
    open(os.path.expanduser("~/.bashrc"), "a").write(XTRA_FUNCTS)

    try:
        vers = Popen(["hg", "--version"], stdout=PIPE).stdout.readline()
    except OSError:
        vers = ""

    if vers.find(HG_V) != -1:
        print HG_D, "already there; doing nothing"
        return

    if apt:
        call(["apt-get", "update"])
        if call(["apt-get", "build-dep", "mercurial"]):
            # no deb-src
            call(["apt-get", "install"] + DEPS)
    else:
        print "no apt support; make sure you install all the deps"

    d = tempfile.mkdtemp()
    data = urllib2.urlopen(HG_URL).read(2**22)
    path = os.path.join(d, HG_PACK)
    open(path, "w").write(data)

    old_d = os.path.abspath(os.curdir)
    os.chdir(d)
    if call(["tar", "-zxf", path]):
        raise RuntimeError("shit happened")

    os.chdir(HG_D)
    res1 = call([PY_EXE, "setup.py", "build"])
    res2 = call([PY_EXE, "setup.py", "install", "--prefix=/opt/%s"%HG_D])
    if res1 or res2:
        raise RuntimeError("can't build Mercurial")

    os.chdir(old_d)
    shutil.rmtree(d)

    open(os.path.expanduser("~/.bashrc"), "a").write(XTRA_VARS)
    os.environ["PATH"] = "/opt/%s/bin:%s" % (HG_D, os.environ["PATH"])
    PYTHONPATH = "/opt/%s/lib/python%d.%d/site-packages:%s"
    PYTHONPATH = PYTHONPATH % (HG_D, PY_MAJ, PY_MIN,
                               os.environ.get("PYTHONPATH", ""))
    os.environ["PYTHONPATH"]= PYTHONPATH


def private_files():
    def helper(privs, dirname, fnames):
        for f in fnames:
            path = os.path.join(dirname, f)
            try:
                private = not (os.stat(path).st_mode & 7)
            except OSError:
                private = True
            if private:
                privs.append(path)
    privs = []
    os.path.walk("/etc", helper, privs)
    return privs


def ignore_body():
    ignore = ["*~", "*.dpkg-new", "*.dpkg-old", "localtime",
              "mtab", "ls.so.cache", "lvmtab", "*.db", "*.lock",
              "cdb", "*.cdb", "*.gz", "*.secret", "blkid.tab*",
              "*.pp", "*.elc", "*.key", "*.gpg", "*.png", "*.dat",
              ]
    ignore += [f[5:] for f in private_files()]
    data = "syntax: glob\n%s\n" % "\n".join(ignore)
    return data


def seed_ignore():
    if os.path.isfile(IGNORE):
        print IGNORE, "alread exists; doing nothing"
    data = ignore_body()
    open(IGNORE, "w").write(data)
    os.chmod(IGNORE, 0700)


def setup_apt():
    open(APT_SNAP_PATH, "w").write(APT_SNAP)
    os.chmod(APT_SNAP_PATH, 0755)
    open("/etc/apt/apt.conf", "a").write(APT_HOOK)


def kickstart():    
    install_hg(os.path.isfile(APT_PATH))
    seed_ignore()
    
    try:
        setup_apt()
    except IOError:
        print "Non-apt distro; be sure to commit after every package update"

    if os.path.isdir("/etc/.hg"):
        print "/etc already tracked; doing nothing"
    else:
        os.chdir("/etc")
        call(["hg", "init"])
        call(["hg", "add"])
        os.chmod("/etc/.hg", 0700)
        print "Review the list of added files and commit it:"
        print ". ~/.bashrc"
        print "ecom someone -m 'original import'"
    

def main():
    parser = OptionParser()
    
    parser.add_option("-s", "--list-secret",
                      action="store_true", dest="list_secret",
                      default=False,
                      help="print the list of secret files and exit")

    parser.add_option("-S", "--list-new-secret",
                      action="store_true", dest="list_new_secret",
                      default=False,
                      help=("print the list of untracked"
                            " secret files and exit"))

    parser.add_option("-V", "--version",
                      action="store_true", dest="version",
                      default=False,
                      help="print software version and exit")

    parser.add_option("-i", "--ignore",
                      action="store_true", dest="ignore",
                      default=False,
                      help="print a reasonable .hgignore and exit")

    parser.add_option("-k", "--kickstart",
                      action="store_true", dest="kickstart",
                      default=False,
                      help="start tracking /etc with hg")

    (opts, args) = parser.parse_args()
    if args:
        parser.error("Unrecognized arguments: %s" % ", ".join(args))
    
    if opts.version:
        print "hgetc %s" % __version__
        sys.exit(0)

    if opts.ignore:
        print ignore_body()
        sys.exit(0)

    if opts.list_secret:
        print "\n".join(private_files())
        sys.exit(0)

    if opts.list_new_secret:
        # TODO: this is not completely right
        old = set(["/etc/"+l.strip()
                   for l in open(IGNORE).readlines()])
        print "\n".join([l for l in private_files() if l not in old])
        sys.exit(0)

    if opts.kickstart:
        kickstart()
        sys.exit(0)

    print "You probably want to try --help or --kickstart"
    

if __name__ == "__main__":
    main()
