# Copyright (c) 2015 SUSE Linux GmbH.  All rights reserved.
#
# This file is part of kiwi.
#
# kiwi is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# kiwi is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kiwi.  If not, see <http://www.gnu.org/licenses/>
#
import os
import logging

# project
from kiwi.command import Command
from kiwi.system.kernel import Kernel
from kiwi.boot.image.base import BootImageBase
from kiwi.defaults import Defaults
from kiwi.system.profile import Profile
from kiwi.system.setup import SystemSetup

log = logging.getLogger('kiwi')


class BootImageDracut(BootImageBase):
    """
    **Implements creation of dracut boot(initrd) images.**
    """
    def post_init(self):
        """
        Post initialization method

        Initialize empty list of dracut caller options
        """
        self.dracut_options = []
        self.included_files = []
        self.included_files_install = []
        self.modules = []
        self.install_modules = []
        self.omit_modules = []
        self.omit_install_modules = []

    def include_file(self, filename, install_media=False):
        """
        Include file to dracut boot image

        :param string filename: file path name
        """
        self.included_files.append('--install')
        self.included_files.append(filename)
        if install_media:
            self.included_files_install.append('--install')
            self.included_files_install.append(filename)

    def include_module(self, module, install_media=False):
        """
        Include module to dracut boot image

        :param string module: module to include
        :param bool install_media: include the module for install initrds
        """
        if install_media and module not in self.install_modules:
            self.install_modules.append(module)
        elif module not in self.modules:
            self.modules.append(module)

    def omit_module(self, module, install_media=False):
        """
        Omit module to dracut boot image

        :param string module: module to omit
        :param bool install_media: omit the module for install initrds
        """
        if install_media and module not in self.omit_install_modules:
            self.omit_install_modules.append(module)
        elif module not in self.omit_modules:
            self.omit_modules.append(module)

    def write_system_config_file(self, config, config_file=None):
        """
        Writes modules configuration into a dracut configuration file.

        :param dict config: a dictionary containing the modules to add and omit
        :param string conf_file: configuration file to write
        """
        dracut_config = []
        if not config_file:
            config_file = os.path.normpath(
                self.boot_root_directory + Defaults.get_dracut_conf_name()
            )
        if config.get('modules'):
            dracut_config.append(
                'add_dracutmodules+=" {0} "\n'.format(
                    ' '.join(config['modules'])
                )
            )
        if config.get('omit_modules'):
            dracut_config.append(
                'omit_dracutmodules+=" {0} "\n'.format(
                    ' '.join(config['omit_modules'])
                )
            )
        if config.get('install_items'):
            dracut_config.append(
                'install_items+=" {0} "\n'.format(
                    ' '.join(config['install_items'])
                )
            )
        if dracut_config:
            with open(config_file, 'w') as config:
                config.writelines(dracut_config)

    def prepare(self):
        """
        Prepare dracut caller environment

        * Create kiwi .profile environment to be included in dracut initrd
        * Setup machine_id(s) to be generic and rebuild by dracut on boot
        """
        profile = Profile(self.xml_state)
        defaults = Defaults()
        defaults.to_profile(profile)
        setup = SystemSetup(
            self.xml_state, self.boot_root_directory
        )
        setup.import_shell_environment(profile)
        setup.setup_machine_id()
        self.dracut_options.append('--install')
        self.dracut_options.append('/.profile')

    def create_initrd(self, mbrid=None, basename=None, install_initrd=False):
        """
        Call dracut as chroot operation to create the initrd and move
        the result into the image build target directory

        :param object mbrid: unused
        :param string basename: base initrd file name
        :param bool install_initrd: installation media initrd
        """
        if self.is_prepared():
            log.info('Creating generic dracut initrd archive')
            kernel_info = Kernel(self.boot_root_directory)
            kernel_details = kernel_info.get_kernel(raise_on_not_found=True)
            if basename:
                dracut_initrd_basename = basename
            else:
                dracut_initrd_basename = self.initrd_base_name
            if install_initrd:
                included_files = self.included_files_install
                modules_args = [
                    '--add', ' {0} '.format(' '.join(self.install_modules))
                ] if self.install_modules else []
                omit_modules_args = [
                    '--omit', ' {0} '.format(
                        ' '.join(self.omit_install_modules)
                    )
                ] if self.omit_install_modules else []
            else:
                included_files = self.included_files
                modules_args = [
                    '--add', ' {0} '.format(' '.join(self.modules))
                ] if self.modules else []
                omit_modules_args = [
                    '--omit', ' {0} '.format(' '.join(self.omit_modules))
                ] if self.omit_modules else []
            dracut_initrd_basename += '.xz'
            options = self.dracut_options + modules_args +\
                omit_modules_args + included_files
            dracut_call = Command.run(
                [
                    'chroot', self.boot_root_directory,
                    'dracut', '--verbose',
                    '--no-hostonly',
                    '--no-hostonly-cmdline',
                    '--xz'
                ] + options + [
                    dracut_initrd_basename,
                    kernel_details.version
                ],
                stderr_to_stdout=True
            )
            log.debug(dracut_call.output)
            Command.run(
                [
                    'mv',
                    os.sep.join(
                        [self.boot_root_directory, dracut_initrd_basename]
                    ),
                    self.target_dir
                ]
            )
            self.initrd_filename = os.sep.join(
                [self.target_dir, dracut_initrd_basename]
            )
