"""This task makes include guards follow the style stipulated by the Google
style guide.
"""

import os
import re
from enum import Enum

from wpiformat.config import Config
from wpiformat.task import PipelineTask


class State(Enum):
    FINDING_IFNDEF = 1
    FINDING_ENDIF = 2
    DONE = 3


class IncludeGuard(PipelineTask):
    @staticmethod
    def should_process_file(config_file: Config, filename: str):
        return config_file.is_header_file(filename)

    def run_pipeline(
        self, config_file: Config, filename: str, lines: str
    ) -> tuple[str, bool]:
        linesep = super().get_linesep(lines)
        lines_list = lines.split(linesep)
        output_list = lines_list

        state = State.FINDING_IFNDEF
        ifndef_regex = re.compile(r"#ifndef \w+", re.ASCII)
        define_regex = re.compile(r"#define \w+", re.ASCII)

        if_preproc_count = 0
        for i in range(len(lines_list)):
            if state == State.FINDING_IFNDEF:
                if lines_list[i].lstrip().startswith("#ifndef ") and lines_list[
                    i + 1
                ].lstrip().startswith("#define "):
                    state = State.FINDING_ENDIF

                    guard = self.make_include_guard(config_file, filename)
                    output_list[i] = ifndef_regex.sub("#ifndef " + guard, lines_list[i])
                    output_list[i + 1] = define_regex.sub(
                        "#define " + guard, lines_list[i + 1]
                    )
                    if_preproc_count += 1
                elif lines_list[i].lstrip().startswith("#pragma once"):
                    state = State.DONE
            elif state == State.FINDING_ENDIF:
                if "#if" in lines_list[i]:
                    if_preproc_count += 1
                elif "#endif" in lines_list[i]:
                    if_preproc_count -= 1

                if if_preproc_count == 0:
                    state = State.DONE
                    output_list[i] = "#endif  // " + guard
                else:
                    output_list[i] = lines_list[i]
            else:
                output_list[i] = lines_list[i]

        # If include guard not found
        if state == State.FINDING_IFNDEF:
            print(f"error: {filename}: doesn't contain include guard or '#pragma once'")
            return lines, False

        output = linesep.join(output_list).rstrip() + linesep
        return output, True

    def make_include_guard(self, config_file: Config, filename: str) -> str:
        """Returns properly formatted include guard based on repository root and
        filename.

        Keyword arguments:
        config_file -- Config object
        filename -- filename
        """
        repo_root_name_override = config_file.group("repoRootNameOverride")

        repo_root = super().get_repo_root()
        guard_root = os.path.relpath(filename, repo_root)
        if not repo_root_name_override:
            guard_path = os.path.basename(repo_root) + os.sep
        else:
            guard_path = repo_root_name_override[0] + os.sep
        include_roots = config_file.group("includeGuardRoots")

        if include_roots:
            prefix = ""
            for include_root in include_roots:
                if guard_root.startswith(include_root) and len(include_root) > len(
                    prefix
                ):
                    prefix = include_root
            guard_path += guard_root[len(prefix) :]
            return (re.sub(r"[^a-zA-Z0-9]", "_", guard_path).upper() + "_").lstrip("_")

        # No include guard roots matched, so append full name
        guard_path += guard_root
        return re.sub(r"[^a-zA-Z0-9]", "_", guard_path).upper() + "_"
