## vim: filetype=makopython

## This file contains extensions to the ``App`` class that allows the specific
## ``libadalang.App`` class to handle project files.

    def add_arguments(self):
        self.parser.add_argument(
            '-X', action='append',
            help="Scenario variables to pass along to GPR"
        )
        self.parser.add_argument(
            '-P', '--project', type=str, default='', help="GPR project file"
        )
        self.parser.add_argument(
            "--subproject",
            dest="subprojects",
            default=[],
            action="append",
            help="If passed, list of subprojects in which to look for source"
            " files. If not passed, start from the root project only."
        )
        self.parser.add_argument(
            "-U",
            "--recursive",
            dest="process_full_project_tree",
            action="store_true",
            help="Process all units in the project tree, excluding externally"
            " built projects unless the --process-runtime option is also"
            " passed.",
        )
        self.parser.add_argument(
            "--process-runtime",
            action="store_true",
            help= "Process the runtime files, and any other predefined"
            " sources.",
        )
        self.parser.add_argument(
            '-k', '--keep-going-on-missing-file', action='store_true',
            help="Behavior when encountering missing files. By default, exit"
                 " with an error on the first missing dependency. Continue"
                 " with a warning in the option is passed."
        )

    def create_unit_provider(self):
        if not self.args.project:
            self.project = None
            return None

        self.scenario_vars = {}
        if self.args.X:
            for var in self.args.X:
                k, v = var.split("=")
                self.scenario_vars[k] = v
        self.project = GPRProject(
            self.args.project, scenario_vars=self.scenario_vars
        )
        return self.project.create_unit_provider()

    def default_get_files(self):
        # If a project was loaded, process its source files. Otherwise, process
        # no source file by default.
        if self.project is None:
            return []

        if self.args.process_runtime:
            mode = SourceFilesMode.whole_project_with_runtime
        elif self.args.process_full_project_tree:
            mode = SourceFilesMode.default
        else:
            mode = SourceFilesMode.root_project

        return self.project.source_files(mode, self.args.subprojects)

    def create_event_handler(self) -> Opt[EventHandler]:
        return self.CommandLineEventHandler(
            self.args.keep_going_on_missing_file
        )

    class CommandLineEventHandler(EventHandler):
        """
        Event handler to warn for each missing file.
        """

        def __init__(self, keep_going_on_missing_file: bool):
            self.keep_going_on_missing_file = keep_going_on_missing_file
            self.already_seen_missing_files: Set[str] = set()

        def unit_requested_callback(self,
                                    context: AnalysisContext,
                                    name: str,
                                    from_unit: AnalysisUnit,
                                    found: bool,
                                    is_not_found_error: bool) -> None:
            # Warn only about missing files that are needed according to Ada
            # legality rules.
            if (
                found
                or not is_not_found_error
                or name in self.already_seen_missing_files
            ):
                return

            self.already_seen_missing_files.add(name)
            self.report_missing_file(name)

        def report_missing_file(self, filename: str) -> None:
            basename = os.path.basename(filename)
            prefix = "WARNING" if self.keep_going_on_missing_file else "ERROR"
            print(f"{prefix}: File {basename} not found")
            if not self.keep_going_on_missing_file:
                # This is a callback from the C world, so propagating a
                # SystemExit exception like ``sys.exit`` does is not going to
                # work. Use the OS-level exit system call instead to avoid
                # relying on exception propagation.
                #
                # Since we use the system call to exit, standard streams need
                # to be manually flushed so that buffered content is written
                # before the exit.
                sys.stdout.flush()
                sys.stderr.flush()
                os._exit(1)
