import os
import sys
import string
import formatter
import imp
import struct
import random
import binascii
import re

class Error(Exception):
    pass

def random_secret():
    try:
        text = open('/dev/urandom').read(16)
    except:
        text = struct.pack('dd', random.random(), random.random())
    return binascii.hexlify(text)

def make_directory(name):
    parent, child = os.path.split(name)
    if child:
        make_directory(parent)
    try:
        os.stat(name)[0]
        return
    except (IOError, OSError):
        pass
    print 'creating directory %s' % name
    os.mkdir(name)

class File:
    def __init__(self, name, dest):
        self.name = name
        self.dest = dest

    def dest_pathname(self):
        if self.dest[-1] == os.sep:
            dirname, name = os.path.split(self.name)
            return os.path.join(self.dest, name)
        return self.dest

    def dest_dirname(self):
        dirname, name = os.path.split(self.dest_pathname())
        return dirname

    def copyfile(self, installer):
        data = open(self.name).read()
        if re.search(r'-=-secret-=-', data):
            secret = installer.get_secret()
            data = re.sub(r'-=-secret-=-', str(secret), data)
        data = re.sub(r'-=-install_dir-=-', self.dest_dirname(), data)

        dest = self.dest_pathname()
        open(dest, 'w').write(data)
        return dest

class ExecFile(File):
    def install(self, installer):
        dest = self.copyfile(installer)
        print 'copy exec %s to %s' % (self.name, dest)
        if os.name == 'posix':
            os.chmod(dest, 0755)

class ModuleFile(File):
    def install(self, installer):
        dest = self.copyfile(installer)
        print 'copy module %s to %s' % (self.name, dest)

class HtmlFile(File):
    def install(self, installer):
        dest = self.copyfile(installer)
        print 'copy html %s to %s' % (self.name, dest)

class DataFile(File):
    def install(self, installer):
        dest = self.copyfile(installer)
        print 'copy data %s to %s' % (self.name, dest)

class ImageFile(File):
    def install(self, installer):
        dest = self.copyfile(installer)
        print 'copy image %s to %s' % (self.name, dest)

class Config:
    def __init__(self):
        self.__has_changed = 0

    def __str__(self):
        parts = []
        for name in dir(self):
            if type(getattr(self, name)) is not type(''):
                continue
            parts.append('%s = "%s"' % (name, getattr(self, name)))
        parts.append('')
        return string.join(parts, '\n')

    def set_value(self, attr, value):
        if not hasattr(self, attr) or getattr(self, attr) != value:
            self.__has_changed = 1
            setattr(self, attr, value)

    def has_changed(self):
        return self.__has_changed

class Installer:
    def __init__(self, name, cgi = 0, apache = 0, doc = 0):
        msg = 'Installing %s' % name
        print msg
        print '=' * len(msg)
        self._name = name
        self._files = []
        self.load_config()
        if cgi:
            cgibin_dir = self.get_config('cgibin_dir')
            self.destdir = os.path.join(cgibin_dir, self._name)
        elif apache:
            modpython_dir = self.get_config('modpython_dir')
            self.destdir = os.path.join(modpython_dir, self._name)
        elif doc:
            doc_dir = self.get_config('doc_dir')
            self.destdir = os.path.join(doc_dir, self._name)
        else:
            raise Error('must specify either cgi = 1 or apache = 1 or doc = 1')

    def __del__(self):
        self.save_config()

    def get_config(self, name):
        if hasattr(self.config, name):
            return getattr(self.config, name)
        w = formatter.DumbWriter()
        f = formatter.AbstractFormatter(w)
        f.add_flowing_data('Please enter a value for ')
        f.add_flowing_data(name)
        f.add_flowing_data(' which is ')
        if name[-7:] == '_secret':
            f.add_flowing_data('the secret key used to sign pickles' \
                               ' sent to the browser from the ')
            f.add_flowing_data(self._name)
            f.add_flowing_data(' program.')
            default = '<random key>'
        elif name == 'cgibin_dir':
            f.add_flowing_data('the top level directory where CGI Albatross')
            f.add_flowing_data(' samples will be installed.')
            default = '/usr/lib/cgi-bin'
        elif name == 'modpython_dir':
            f.add_flowing_data('the top level directory where mod_python')
            f.add_flowing_data(' Albatross samples will be installed.')
            default = '/var/www'
        elif name == 'doc_dir':
            f.add_flowing_data('the top level directory where ')
            f.add_flowing_data(' Albatross html documents will be installed.')
            default = '/var/www'
        f.end_paragraph(1)
        value = raw_input('Enter %s [%s]: ' % (name, default))
        if not value:
            value = default
        self.config.set_value(name, value)
        return value

    def load_config(self):
        pathname = sys.modules['install'].__file__
        dirname, name = os.path.split(pathname)
        self.cfgname = os.path.join(dirname, 'install.cfg')
        self.config = Config()
        try:
            execfile(self.cfgname, globals(), self.config.__dict__)
        except IOError:
            pass

    def save_config(self):
        if self.config.has_changed():
            open(self.cfgname, 'w').write('%s' % self.config)

    def get_secret(self):
        dirname, name = os.path.split(self._name)
        attr = '%s_secret' % name
        secret = self.get_config(attr)
        if secret == '<random key>':
            self.config.set_value(attr, random_secret())
        return getattr(self.config, attr)

    def add_exec(self, name, dest = ''):
        dest = os.path.join(self.destdir, dest)
        self._files.append(ExecFile(name, dest))

    def add_module(self, name, dest = ''):
        dest = os.path.join(self.destdir, dest)
        self._files.append(ModuleFile(name, dest))

    def add_html(self, name, dest = ''):
        dest = os.path.join(self.destdir, dest)
        self._files.append(HtmlFile(name, dest))

    def add_data(self, name, dest = ''):
        dest = os.path.join(self.destdir, dest)
        self._files.append(DataFile(name, dest))

    def add_image(self, name, dest = ''):
        dest = os.path.join(self.destdir, dest)
        self._files.append(ImageFile(name, dest))

    def install(self):
        # Make sure all directories exist
        dirnames = {}
        for file in self._files:
            dirnames[file.dest_dirname()] = 1
        dirnames = dirnames.keys()
        dirnames.sort()
        for name in dirnames:
            make_directory(name)
        # Install files
        for file in self._files:
            file.install(self)
        print

class Catalog:
    def __init__(self):
        self.dirnames = []

    def append(self, name):
        self.dirnames.append(name)

    def install_all(self):
        self.dirnames.sort()
        for dirname in self.dirnames:
            if not self.install(dirname):
                break

    def install(self, dirname):
        cwd = os.getcwd()
        os.chdir(dirname)
        result = os.system('python install.py')
        os.chdir(cwd)
        return result == 0

# Walk the samples directory finding information about samples
def visit_dir(catalog, dirname, names):
    if dirname != rootdir and 'install.py' in names:
        catalog.append(dirname)

if __name__ == '__main__':
    # Find the root of the samples directory
    rootdir, basename = os.path.split(sys.argv[0])
    if not rootdir:
        rootdir = '.'
    catalog = Catalog()
    os.path.walk(rootdir, visit_dir, catalog)
    catalog.install_all()
