import os
import subprocess
import re

PRE_INSTALL = "pre-install"
POST_INSTALL = "post-install"

LOG_LEVEL_DESC = ["emerg", "alert", "crit", "err", "warning", "notice", "info", "debug"]
LOG_FILE_NAME = "/var/log/installer.log"
TDNF_LOG_FILE_NAME = "/var/log/tdnf.log"
TDNF_CMDLINE_FILE_NAME = "/var/log/tdnf.cmdline"
KS_POST_INSTALL_LOG_FILE_NAME = "/var/log/installer-kickstart.log"
SIGNATURE   = "localhost echo"
LOG_EMERG   = 0
LOG_ALERT   = 1
LOG_CRIT    = 2
LOG_ERROR   = 3
LOG_WARNING = 4
LOG_NOTICE  = 5
LOG_INFO    = 6
LOG_DEBUG   = 7

default_partitions = [
    {"mountpoint": "/", "size": 0, "filesystem": "ext4"},
    ]

def partition_compare(p):
    if 'mountpoint' in p:
        return (1, len(p['mountpoint']), p['mountpoint'])
    return (0, 0, "A")

def partition_disk(disk, partitions):
    partitions_data = {}
    partitions_data['disk'] = disk
    partitions_data['partitions'] = partitions
    output = open(os.devnull, 'w')

    # Clear the disk
    process = subprocess.Popen(['sgdisk', '-o', '-g', disk], stderr=output, stdout=output)
    retval = process.wait()
    if retval != 0:
        log(LOG_ERROR, "Failed clearing disk {0}".format(disk))
        return None

    # Partitioning the disk
    extensible_partition = None
    partitions_count = len(partitions)
    partition_number = 2
    # Add part size and grub flags
    grub_flag = ':ef02'
    part_size = '+2M'
    if os.path.isdir("/sys/firmware/efi"):
        grub_flag = ':ef00'
        part_size = '+3M'

    # Adding the bios partition
    partition_cmd = ['sgdisk', '-n 1::' + part_size]
    # Adding the known size partitions
    for partition in partitions:
        if partition['size'] == 0:
            # Can not have more than 1 extensible partition
            if extensible_partition != None:
                log(LOG_ERROR, "Can not have more than 1 extensible partition")
                return None
            extensible_partition = partition
        else:
            partition_cmd.extend(['-n', '{}::+{}M'.format(partition_number, partition['size'])])

        partition['partition_number'] = partition_number
        prefix = ''
        if 'nvme' in disk:
            prefix = 'p'
        partition['path'] = disk + prefix + repr(partition_number)
        partition_number = partition_number + 1

    # Adding the last extendible partition
    if extensible_partition:
        partition_cmd.extend(['-n', repr(extensible_partition['partition_number'])])

    partition_cmd.extend(['-p', disk])

    # Run the partitioning command
    process = subprocess.Popen(partition_cmd, stderr=output, stdout=output)
    retval = process.wait()
    if retval != 0:
        log(LOG_ERROR, "Faild partition disk, command: {0}". format(partition_cmd))
        return None

    process = subprocess.Popen(['sgdisk', '-t1' + grub_flag, disk], stderr=output, stdout=output)
    retval = process.wait()
    if retval != 0:
        log(LOG_ERROR, "Failed to setup grub partition")
        return None

    # Format the filesystem
    for partition in partitions:
        if "mountpoint" in partition:
            if partition['mountpoint'] == '/':
                partitions_data['root'] = partition['path']
                partitions_data['root_partition_number'] = partition['partition_number']
            elif partition['mountpoint'] == '/boot':
                partitions_data['boot'] = partition['path']
                partitions_data['boot_partition_number'] = partition['partition_number']
                partitions_data['bootdirectory'] = '/'
        if partition['filesystem'] == "swap":
            process = subprocess.Popen(['mkswap', partition['path']], stderr=output, stdout=output)
            retval = process.wait()
            if retval != 0:
                log(LOG_ERROR, "Failed to create swap partition @ {}".format(partition['path']))
                return None
        else:
            mkfs_cmd = ['mkfs', '-t', partition['filesystem'], partition['path']]
            process = subprocess.Popen(mkfs_cmd, stderr=output, stdout=output)
            retval = process.wait()
            if retval != 0:
                log(LOG_ERROR,
                    "Failed to format {} partition @ {}".format(partition['filesystem'],
                                                                partition['path']))
                return None

    # Check if there is no root partition
    if 'root' not in partitions_data:
        log(LOG_ERROR, "There is no partition assigned to root '/'")
        return None

    if 'boot' not in partitions_data:
        partitions_data['boot'] = partitions_data['root']
        partitions_data['boot_partition_number'] = partitions_data['root_partition_number']
        partitions_data['bootdirectory'] = '/boot/'

    partitions.sort(key=lambda p: partition_compare(p))

    return partitions_data

def replace_string_in_file(filename, search_string, replace_string):
    with open(filename, "r") as source:
        lines = source.readlines()

    with open(filename, "w") as destination:
        for line in lines:
            destination.write(re.sub(search_string, replace_string, line))

def log(type, message):
    command = 'systemd-cat echo \"<{}> {} : {}\"'.format(type, LOG_LEVEL_DESC[type], message)
    process = subprocess.Popen([command], shell=True)
    retval = process.wait()
    return retval

def dump(type, filename):
    command = ("journalctl -p {0} | grep --line-buffered \"{1}\" > {2}"
               .format(LOG_LEVEL_DESC[type], SIGNATURE, filename))
    process = subprocess.Popen([command], shell=True)
    retval = process.wait()
    return retval

def dump(filename):
    command = "journalctl | grep --line-buffered \"{0}\" > {1}".format(SIGNATURE, filename)
    process = subprocess.Popen([command], shell=True)
    retval = process.wait()
    return retval