#!/usr/bin/python

import sys
import shutil
import os
import subprocess
import debconf

class CommandFailed(Exception): pass

class Installer:
    source = '/source'
    target = '/target'

    def install(self, mountpoints, timezone, keymap, username, password, fullname,
                hostname):
        self.mount_target(mountpoints)
        self.mount_source()
        try:
            self.copy()
        finally:
            self.unmount_source()

        try:
            self.configure_fstab(mountpoints)
            self.configure_timezone(timezone)
            self.configure_keymap(keymap)
            self.configure_user(username, password, fullname)
            self.configure_hostname(hostname)
            self.configure_hardware()
        finally:
            self.unmount_target(mountpoints)

    def configure_fstab(self, mountpoints):
        fstab = open(os.path.join(self.target,'etc/fstab'), 'w')
        for path, device in mountpoints.items():
            if path == '/':
                passno = 1
            else:
                passno = 2

            filesystem = 'ext3'
            options = 'defaults'
            
            print >>fstab, '%s\t%s\t%s\t%s\t%d\t%d' % (device, path, filesystem, options, 0, passno)
        fstab.close()

    def configure_timezone(self, timezone):
        # tzsetup ignores us if these exist
        for tzfile in ('etc/timezone', 'etc/localtime'):
            path = os.path.join(self.target, tzfile)
            if os.path.exists(path):
                os.unlink(path)

        self.set_debconf('base-config', 'tzconfig/preseed_zone', timezone)
        self.chrex('tzsetup', '-y')

    def configure_keymap(self, keymap):
        self.set_debconf('debian-installer', 'debian-installer/keymap', keymap)
        self.chrex('install-keymap', keymap)

    def configure_user(self, username, password, fullname):
        self.chrex('passwd', '-l', 'root')
        self.set_debconf('passwd', 'passwd/username', username)
        self.set_debconf('passwd', 'passwd/user-fullname', fullname)
        self.set_debconf('passwd', 'passwd/user-password', password)
        self.set_debconf('passwd', 'passwd/user-password-again', password)
        self.reconfigure('passwd')

    def configure_hostname(self, hostname):
        fp = open(os.path.join(self.target, 'etc/hostname'), 'w')
        print >>fp, hostname
        fp.close()

    def configure_hardware(self):
        self.chrex('mount', '-t', 'proc', 'proc', '/proc')
        self.chrex('mount', '-t', 'sysfs', 'sysfs', '/sys')

        uname = subprocess.Popen(['uname', '-r'], stdout=subprocess.PIPE)
        kernel_version = uname.stdout.read().rstrip()
        uname.wait()
        
        try:
            for package in ('gnome-panel', 'xserver-xorg', 'linux-image-' + kernel_version):
                self.copy_debconf(package)
                self.reconfigure(package)
        finally:
            self.chrex('umount', '/proc')
            self.chrex('umount', '/sys')
    
    def mount_target(self, mountpoints):
        os.mkdir(self.target)
        self.ex('mount', mountpoints['/'], self.target)

        for path, device in mountpoints.items():
            if path in ('/', 'swap'):
                continue
            path = os.path.join(self.target, path[1:])
            os.mkdir(path)
            self.ex('mount', device, path)

    def unmount_target(self, mountpoints):
        for path, device in mountpoints.items():
            if path in ('/', 'swap'):
                continue
            path = os.path.join(self.target, path[1:])
            self.ex('umount', path)
        self.ex('umount', self.target)

    def copy(self):
        # Cheat
        rsync = subprocess.Popen(['rsync', '-avx', self.source + '/', self.target])
        rsync.wait()
        return
    
        total_files = 0
        for dirpath, dirnames, filenames in os.walk(self.source):
            if dirpath == self.source:
                continue
            total_files += len(filenames) + len(dirnames)

    def mount_source(self):
        self.ex('losetup', '/dev/cloop1', '/cdrom/casper/filesystem.cloop')
        os.mkdir(self.source)
        self.ex('mount', '/dev/cloop1', self.source)

    def unmount_source(self):
        self.ex('umount', self.source)
        self.ex('losetup', '-d', '/dev/cloop1')

    def ex(self, *args):
        status = subprocess.call(args)
        if status != 0:
            raise CommandFailed(str(args))

    def chrex(self, *args):
        self.ex('chroot', self.target, *args)

    def copy_debconf(self, package):
        targetdb = os.path.join(self.target, 'var/cache/debconf/config.dat')
        self.ex('debconf-copydb', 'configdb', 'targetdb', '-p', '^%s/' % package,
                '--config=Name:targetdb', '--config=Driver:File','--config=Filename:' + targetdb)

    def set_debconf(self, owner, question, value):
        dccomm = subprocess.Popen(['chroot', self.target, 'debconf-communicate', '-fnoninteractive', owner],
                                  stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)
        dc = debconf.Debconf(read=dccomm.stdout, write=dccomm.stdin)
        dc.set(question, value)
        dc.fset(question, 'seen', 'true')
        dccomm.stdin.close()
        dccomm.wait()

    def reconfigure(self, package):
        self.chrex('dpkg-reconfigure', '-fnoninteractive', package)

if __name__ == '__main__':
    Installer().install({'/' : sys.argv[1]}, 'US/Pacific', 'dvorak', 'mdz', 'mdz2', 'Matt Zimmerman', 'ubuntu')
