from volatility3.framework import interfaces, constants, configuration


class KernelModule(interfaces.automagic.AutomagicInterface):
    """Finds ModuleRequirements and ensures their layer, symbols and offsets"""

    priority = 100

    def __call__(self,
                 context: interfaces.context.ContextInterface,
                 config_path: str,
                 requirement: interfaces.configuration.RequirementInterface,
                 progress_callback: constants.ProgressCallback = None) -> None:
        new_config_path = interfaces.configuration.path_join(config_path, requirement.name)
        if not isinstance(requirement, configuration.requirements.ModuleRequirement):
            # Check subrequirements
            for req in requirement.requirements:
                self(context, new_config_path, requirement.requirements[req], progress_callback)
            return
        if not requirement.unsatisfied(context, config_path):
            return
        # The requirement is unfulfilled and is a ModuleRequirement

        context.config[interfaces.configuration.path_join(
            new_config_path, 'class')] = 'volatility3.framework.contexts.Module'

        for req in requirement.requirements:
            if requirement.requirements[req].unsatisfied(context, new_config_path) and req != 'offset':
                return

        # We now just have the offset requirement, but the layer requirement has been fulfilled.
        # Unfortunately we don't know the layer name requirement's exact name

        for req in requirement.requirements:
            if isinstance(requirement.requirements[req], configuration.requirements.TranslationLayerRequirement):
                layer_kvo_config_path = interfaces.configuration.path_join(new_config_path, req,
                                                                           'kernel_virtual_offset')
                offset_config_path = interfaces.configuration.path_join(new_config_path, 'offset')
                offset = context.config[layer_kvo_config_path]
                context.config[offset_config_path] = offset

        # Now construct the module based on the sub-requirements
        requirement.construct(context, config_path)
