#!/usr/bin/python
import logging
import time
import plistlib
import os
import subprocess
import shutil

firstboot_dir = '/private/var/.imagr/first-boot'
config_plist = os.path.join(firstboot_dir, 'config.plist')
items_dir = os.path.join(firstboot_dir, 'items')
items_done = None
installer = '/usr/sbin/installer'
logfile = '/var/log/first-boot.log'

# Logging Config
log = logging.getLogger('imagr-first-boot')
log.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(message)s')
handler_stream = logging.StreamHandler()
handler_stream.setFormatter(formatter)
handler_stream.setLevel(logging.ERROR)
log.addHandler(handler_stream)
handler_file = logging.FileHandler(logfile)
handler_file.setFormatter(formatter)
log.addHandler(handler_file)

def ip_addresses():
    command = "ifconfig  | grep -E 'inet.[0-9]' | grep -v '127.0.0.1' | awk '{ print $2}' | wc -l"
    proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
    return proc.communicate()[0].replace('\n', '')


def remove_item(path):
    if os.path.exists(path):
        os.remove(path)


def all_done(reboot):
    if not os.listdir(items_dir):
        sleep_time = 10
        cleanup()
        time.sleep(sleep_time)
        if reboot is True:
            log.info('Triggering Reboot.')
            subprocess.call(['/sbin/reboot'])
        else:
            log.info('Killing LoginLog.')
            subprocess.call(['/usr/bin/killall', 'LoginLog'])



def cleanup():
    log_path = open(logfile, 'a')
    log.info('%{color=magenta}No more packages or scripts have been found, cleaning up.')
    # remove launchdaemon
    remove_item('/Library/LaunchDaemons/com.grahamgilbert.imagr-first-boot-pkg.plist')
    # remove launchagents
    remove_item('/Library/LaunchAgents/se.gu.it.LoginLog.plist')
    remove_item('/Library/LaunchAgents/se.gu.it.LoginLog.login.plist')
    # remove loginlog.app
    if os.path.exists('/Library/PrivilegedHelperTools/LoginLog.app'):
        shutil.rmtree('/Library/PrivilegedHelperTools/LoginLog.app')
    # remove firstboot_dir
    if os.path.exists(firstboot_dir):
        shutil.rmtree(firstboot_dir)


def main():
    log.info(('''
######################################################
Imagr First Boot %s
######################################################
''') % time.strftime("%Y-%m-%d %H:%M:%S"))
    plist_opts = plistlib.readPlist(config_plist)
    # Detect new network hardware
    if plist_opts.get('Network') is True:
        subprocess.call(['/usr/sbin/networksetup', '-detectnewhardware'])
        # Check if the network is up
        while True:
            if ip_addresses().strip() != "0":
                log.info('%{color=green}Network connection is active. ')
                break
            else:
                log.info('%{color=orange}Network connection is inactive, retrying...')
                time.sleep(1)
    retry_limit = plist_opts.get('RetryCount')
    # if the packages dir isn't empty, loop over them
    if not os.path.exists(items_dir):
        os.makedirs(items_dir)

    reboot = True
    if plist_opts.get('Reboot') is False:
        log.info('%{color=green}Skipping reboot due to preference. ')
        reboot = False

    sleep_time = 20
    if os.listdir(items_dir):
        # We're installing, wait to see the output
        for item in sorted(os.listdir(items_dir)):
            if item.endswith(".pkg") or item.endswith(".mpkg"):
                log.info(('%%{color=magenta}Installing %s...') % item)
                package_path = os.path.join(items_dir, item)
                command = [installer, '-pkg', package_path, '-target', '/']
                # check if we've reached the retry limit
                retry_key = item + '_count'
                retry_count = plist_opts.get(retry_key, 0)
                if retry_count <= retry_limit:
                    log_path = open(logfile, 'a')
                    # so that data written to it will be appended
                    task = subprocess.Popen(command, stdout=log_path,
                                            stderr=log_path)
                    task.communicate()
                    if task.returncode == 0:
                        # Package installed ok, we're good to remove it
                        log.info(('%%{color=green}%s installed successfully.') % item)
                        try:
                            os.remove(package_path)
                        except:
                            shutil.rmtree(package_path)
                    else:
                        log.info(('%%{color=red}%s didn\'t install successfully. Will retry on next boot.') % package)
                        retry_count = retry_count + 1
                        plist_opts[retry_key] = retry_count
                        plistlib.writePlist(plist_opts, os.path.join(firstboot_dir, 'config.plist'))
                else:
                    log.info(('%%{color=red}Error limit for %s has been reached, removing the package.') % package)
                    try:
                        os.remove(package_path)
                    except:
                        shutil.rmtree(package_path)
            else:
                log.info(('%%{color=magenta}Running %s...') % item)
                script_path = os.path.join(items_dir, item)
                log_path = open(logfile, 'a')
                # so that data written to it will be appended
                task = subprocess.Popen(script_path, stdout=log_path,
                                        stderr=log_path, shell=True)
                task.communicate()
                if task.returncode == 0:
                    try:
                        os.remove(script_path)
                    except:
                        shutil.rmtree(script_path)
            all_done(reboot)

    # We're done, reboot. Wait 20 seconds so the log window can be read.
    time.sleep(sleep_time)
    all_done(reboot)


if __name__ == '__main__':
    main()
