#!/usr/bin/env python3

import os
import shlex
import subprocess
import threading

from shell_helpers import LF
import common
import thread_pool

class Main(common.BuildCliFunction):
    def __init__(self, *args, **kwargs):
        if not 'description' in kwargs:
            kwargs['description'] = '''\
Build our compiled userland examples.
'''
        if not 'defaults' in kwargs:
            kwargs['defaults'] = {}
        if not 'mode' in kwargs['defaults']:
            kwargs['defaults']['mode'] = 'userland'
        super().__init__(*args, **kwargs)
        self._add_argument('--ccflags')
        self._add_argument('--force-rebuild')
        self._add_argument('--optimization-level')
        self.add_argument(
            'targets',
            default=[],
            help='''\
https://cirosantilli.com/linux-kernel-module-cheat#build-userland
''',
            nargs='*',
        )

    def build(self):
        build_dir = self.get_build_dir()
        cc_flags = [
            '-I', self.env['root_dir'], LF,
        ] + self.env['ccflags']
        extra_obj_lkmc_common = os.path.join(
            build_dir,
            self.env['common_basename_noext'] + self.env['obj_ext']
        )
        self._build_one(
            in_path=self.env['common_c'],
            out_path=extra_obj_lkmc_common,
            cc_flags=cc_flags,
            extra_deps=[self.env['common_h']],
            link=False,
        )
        with thread_pool.ThreadPool(
            self._build_one,
            nthreads=self.env['nproc'],
            submit_raise_exit=self.env['quit_on_fail'],
        ) as my_thread_pool:
            for target in self.env['targets']:
                for path, in_dirnames, in_filenames in self.sh.walk(target):
                    for in_filename in in_filenames:
                        in_ext = os.path.splitext(in_filename)[1]
                        if not in_ext in self.env['build_in_exts']:
                            continue
                        in_path = os.path.join(path, in_filename)
                        my_thread_pool.submit({
                            'cc_flags': cc_flags,
                            'extra_objs_lkmc_common': [extra_obj_lkmc_common],
                            'in_path': in_path,
                            'out_path': self.resolve_userland_executable(in_path),
                        })
        exit_status = self._handle_thread_pool_errors(my_thread_pool)
        if exit_status != 0:
            return exit_status
        if self.env['copy_overlay']:
            self.sh.copy_dir_if_update(
                srcdir=build_dir,
                destdir=os.path.join(
                    self.env['out_rootfs_overlay_lkmc_dir'],
                    self.env['out_rootfs_overlay_dir_prefix']
                ),
                filter_ext=self.env['userland_executable_ext'],
            )
        return exit_status

    def clean(self):
        if self.env['in_tree']:
            for target in self.env['targets']:
                if os.path.exists(target):
                    if os.path.isfile(target):
                        self.sh.rmrf(self.resolve_userland_executable(target))
                    else:
                        for path, dirnames, filenames in self.sh.walk(target):
                            for filename in filenames:
                                if os.path.splitext(filename)[1] in self.env['userland_out_exts']:
                                    self.sh.rmrf(os.path.join(path, filename))
        else:
            for target in self.env['targets']:
                self.sh.rmrf(self.resolve_userland_executable(target))

    def get_build_dir(self):
        return self.env['userland_build_dir']

    def setup_one_build(self):
        self.env['targets'] = self.resolve_targets(
            [self.env['userland_source_dir']],
            self.env['targets']
        )

if __name__ == '__main__':
    Main().cli()
