#!/usr/bin/env python3
# Copyright: 2016, Maximiliano Curia <maxy@debian.org>
#
# License: ISC
#  Permission to use, copy, modify, and/or distribute this software for any
#  purpose with or without fee is hereby granted, provided that the above
#  copyright notice and this permission notice appear in all copies.
#  .
#  THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
#  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
#  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
#  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
#  OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
#  PERFORMANCE OF THIS SOFTWARE.

from __future__ import annotations

from .cmdoptions import process_options
from .core_types import Options
from .dep5 import Copyright, Group
from .file_policy import group_key
from .group_contracts import GroupProtocol
from .grouping import GroupKey
from .nodes import FileInfo
from .output import generate_output
from .renderer import renderer_for
from .tree import RootInfo


def prepare_output_groups(
    filetree: RootInfo,
    copyright_: Copyright,
    options: Options,
) -> dict[GroupKey, GroupProtocol]:
    # Files that remove_misplaced_files keeps in an existing group are recorded
    # here, so the loop below does not place them a second time.
    assigned: set[FileInfo] = set()
    copyright_.remove_misplaced_files(options, assigned)
    groups: dict[GroupKey, GroupProtocol] = copyright_.get_group_dict(options)

    for fileinfo in filetree.walk_files():
        if options.mode == "partial" and not fileinfo.included:
            continue

        # if already assigned, the file matched its copyright group
        if fileinfo in assigned:
            continue

        # If we reach this it means the file needs to be added to a group.
        # TODO (maxy): for diff mode
        file_key = group_key(fileinfo, options)
        group = groups.setdefault(file_key, Group(file_key))
        group.add_file(fileinfo)
        assigned.add(fileinfo)

    return groups


def run_pipeline(options: Options) -> None:
    """Run the full options -> tree -> dep5 -> output flow for `options`.

    Shared by main() and the benchmark/golden-output tooling so they all
    exercise the exact same code path.
    """
    filetree: RootInfo = RootInfo.build(options)
    copyright_ = Copyright.build(filetree, options)

    copyright_.process(filetree)
    filetree.process(options)

    groups = prepare_output_groups(filetree, copyright_, options)

    renderer = renderer_for(options)
    generate_output(groups, filetree, copyright_, options, renderer)


def main() -> int:
    options: Options = process_options()
    run_pipeline(options)
    return 0
