#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# Copyright 2019-2022 NXP
#
# SPDX-License-Identifier: BSD-3-Clause


"""Master Boot Image."""


from copy import deepcopy
from typing import Any, Dict, List, Tuple, Type, Union

from Crypto.Cipher import AES
from ruamel.yaml import YAML

from spsdk.crypto import SignatureProvider
from spsdk.exceptions import SPSDKValueError
from spsdk.image import IMG_DATA_FOLDER, MBIMG_SCH_FILE
from spsdk.image.exceptions import SPSDKUnsupportedImageType
from spsdk.image.keystore import KeySourceType, KeyStore
from spsdk.image.mbi_mixin import (
    MasterBootImageManifest,
    Mbi_ExportMixinAppCertBlockManifest,
    Mbi_ExportMixinAppTrustZone,
    Mbi_ExportMixinAppTrustZoneCertBlock,
    Mbi_ExportMixinCrcSign,
    Mbi_ExportMixinEccSign,
    Mbi_ExportMixinHmacKeyStoreFinalize,
    Mbi_ExportMixinRsaSign,
    Mbi_Mixin,
    Mbi_MixinApp,
    Mbi_MixinCertBlockV2,
    Mbi_MixinCertBlockV31,
    Mbi_MixinCtrInitVector,
    Mbi_MixinFwVersion,
    Mbi_MixinHmac,
    Mbi_MixinHmacMandatory,
    Mbi_MixinHwKey,
    Mbi_MixinIvt,
    Mbi_MixinKeyStore,
    Mbi_MixinLoadAddress,
    Mbi_MixinManifest,
    Mbi_MixinRelocTable,
    Mbi_MixinTrustZone,
    Mbi_MixinTrustZoneMandatory,
    MultipleImageTable,
)
from spsdk.image.trustzone import TrustZone
from spsdk.utils.crypto.cert_blocks import CertBlockV2, CertBlockV31
from spsdk.utils.misc import align_block, get_key_by_val
from spsdk.utils.schema_validator import ConfigTemplate, ValidationSchemas, check_config

PLAIN_IMAGE = (0x00, "Plain Image (either XIP or Load-to-RAM)")
SIGNED_RAM_IMAGE = (0x01, "Plain Signed Load-to-RAM Image")
CRC_RAM_IMAGE = (0x02, "Plain CRC Load-to-RAM Image")
ENCRYPTED_RAM_IMAGE = (0x03, "Encrypted Load-to-RAM Image")
SIGNED_XIP_IMAGE = (0x04, "Plain Signed XIP Image")
CRC_XIP_IMAGE = (0x05, "Plain CRC XIP Image")

DEVICE_FILE = IMG_DATA_FOLDER + "/database.yml"

# pylint: disable=too-many-ancestors
def get_mbi_class(config: Dict[str, Any]) -> Type["MasterBootImage"]:
    """Get Master Boot Image class.

    :raises SPSDKUnsupportedImageType: The invalid configuration.
    :return: MBI Class.
    """
    schema_cfg = ValidationSchemas.get_schema_file(MBIMG_SCH_FILE)
    with open(DEVICE_FILE) as f:
        device_cfg = YAML(typ="safe").load(f)
    # Validate needed configuration to recognize MBI class
    check_config(config, [schema_cfg["image_type"], schema_cfg["family"]])
    try:
        target = get_key_by_val(
            config["outputImageExecutionTarget"], device_cfg["map_tables"]["targets"]
        )
        authentication = get_key_by_val(
            config["outputImageAuthenticationType"], device_cfg["map_tables"]["authentication"]
        )
        family = config["family"]

        cls_name = device_cfg["devices"][family]["images"][target][authentication]
    except (KeyError, SPSDKValueError) as exc:
        raise SPSDKUnsupportedImageType(
            "The type of requested Master boot image is not supported for that device."
        ) from exc

    return globals()[cls_name]


def get_mbi_classes(family: str) -> Dict[str, Tuple[Type["MasterBootImage"], str, str]]:
    """Get all Master Boot Image supported classes for chip family.

    :param family: Chip family.
    :raises SPSDKValueError: The invalid family.
    :return: Dictionary with key like image name and values are Tuple with it's MBI Class
        and target and authentication type.
    """
    with open(DEVICE_FILE) as f:
        device_cfg = YAML(typ="safe").load(f)
    if not family in device_cfg["devices"]:
        raise SPSDKValueError("Not supported family for Master Boot Image")

    ret: Dict[str, Tuple[Type["MasterBootImage"], str, str]] = {}

    images: Dict[str, Dict[str, str]] = device_cfg["devices"][family]["images"]

    for target in images.keys():
        for authentication in images[target]:
            cls_name = images[target][authentication]

            ret[f"{family}_{target}_{authentication}"] = (
                globals()[cls_name],
                device_cfg["map_tables"]["targets"][target][0],
                device_cfg["map_tables"]["authentication"][authentication][0],
            )

    return ret


def mbi_get_validation_schemas(sch_names: List[str]) -> List[Dict[str, Any]]:
    """Get list of validation schemas by its names.

    :return: Validation list of schemas.
    """
    schemas = []
    schema_cfg = ValidationSchemas.get_schema_file(MBIMG_SCH_FILE)

    for sch_name in sch_names:
        schemas.append(schema_cfg[sch_name])

    return schemas


def mbi_generate_config_templates(family: str) -> Dict[str, str]:
    """Generate all possible configuration for selected family.

    :param family: Family description.
    :raises SPSDKError: [description]
    :return: Dictionary of individual templates (key is name of template, value is template itself).
    """
    ret: Dict[str, str] = {}
    # 1: Generate all configuration for MBI
    mbi_classes = get_mbi_classes(family)

    for key, mbi in mbi_classes.items():
        mbi_cls, target, authentication = mbi
        schemas = []
        schemas.extend(mbi_get_validation_schemas(["family", "image_type", "output_file"]))
        schemas.extend(mbi_cls.get_validation_schemas())

        override = {}
        override["family"] = family
        override["outputImageExecutionTarget"] = target
        override["outputImageAuthenticationType"] = authentication
        yaml_data = ConfigTemplate(
            f"Master Boot Image Configuration template for {family}, {mbi_cls.IMAGE_TYPE[1]}.",
            schemas,
            override,
        ).export_to_yaml()

        ret[key] = yaml_data

    return ret


class MasterBootImage:
    """Master Boot Image Interface."""

    IMAGE_TYPE = PLAIN_IMAGE

    @classmethod
    def _get_mixins(cls) -> List[Type[Mbi_Mixin]]:
        """Get the list of Mbi Mixin classes.

        :return: List of Mbi_Mixins.
        """
        return [x for x in cls.__bases__ if issubclass(x, Mbi_Mixin)]

    def __init__(self) -> None:
        """Initialization of MBI."""
        # Check if all needed class instation members are available (validation of class due to mixin problems)
        for base in self._get_mixins():
            for member in base.NEEDED_MEMBERS:
                assert hasattr(self, member)

    @property
    def total_len(self) -> int:
        """Compute final application data length.

        :return: Final image data length.
        """
        ret = 0
        for base in self._get_mixins():
            ret += base.mix_len(self)  # type: ignore
        return ret

    @property
    def app_len(self) -> int:
        """Application data length.

        :return: Application data length.
        """
        return self.total_len

    def load_from_config(self, config: Dict[str, Any]) -> None:
        """Load configuration from dictionary.

        :param config: Dictionary with configuration fields.
        """
        for base in self._get_mixins():
            base.mix_load_from_config(self, config)  # type: ignore

    collect_data: Any  # collect_data(self) -> bytes
    encrypt: Any  # encrypt(self, raw_image: bytes) -> bytes
    post_encrypt: Any  # post_encrypt(self, image: bytes) -> bytes
    sign: Any  # sign(self, image: bytes) -> bytes
    finalize: Any  # finalize(self, image: bytes) -> bytes

    def export(self) -> bytes:
        """Export final bootable image.

        :return: Bootable Image in bytes.
        """
        # 1: Validate the input data
        self.validate()
        # 2: Collect all input data into raw image
        raw_image = self.collect_data()
        # 3: Optionally encrypt the image
        encrypted_image = self.encrypt(raw_image)
        # 4: Optionally do some post encrypt image updates
        encrypted_image = self.post_encrypt(encrypted_image)
        # 5: Optionally sign image
        signed_image = self.sign(encrypted_image)
        # 6: Finalize image
        final_image = self.finalize(signed_image)

        return final_image

    def parse(self, data: bytes) -> None:
        """Parse the final image to individual fields.

        :param data: Final Image in bytes.
        :raises NotImplementedError: Derived class has to implement this method
        """
        raise NotImplementedError("Derived class has to implement this method.")

    @classmethod
    def get_validation_schemas(cls) -> List[Dict[str, Any]]:
        """Create the validation schema for current image type.

        :return: Validation schema.
        """
        schemas = []
        schema_cfg = ValidationSchemas.get_schema_file(MBIMG_SCH_FILE)

        for base in cls._get_mixins():
            for sch in base.VALIDATION_SCHEMAS:
                schemas.append(deepcopy(schema_cfg[sch]))
            schemas.extend(deepcopy(base.mix_get_extra_validation_schemas()))

        return schemas

    def validate(self) -> None:
        """Validate the setting of image."""
        for base in self._get_mixins():
            base.mix_validate(self)  # type: ignore


########################################################################################################################
# Master Boot Image Class (LPC55)
########################################################################################################################

# pylint: disable=invalid-name
# pylint: disable=abstract-method
class Mbi_PlainXip(
    MasterBootImage, Mbi_MixinApp, Mbi_MixinIvt, Mbi_MixinTrustZone, Mbi_ExportMixinAppTrustZone
):
    """Master Boot Plain XiP Image for LPC55xxx family."""

    def __init__(self, app: bytes = None, trust_zone: TrustZone = None) -> None:
        """Constructor for Master Boot Plain XiP Image for LPC55xxx family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        """
        self.app = align_block(app) if app else None
        self.tz = trust_zone or TrustZone.enabled()
        super().__init__()


class Mbi_CrcXip(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinTrustZone,
    Mbi_ExportMixinAppTrustZone,
    Mbi_ExportMixinCrcSign,
):
    """Master Boot CRC XiP Image for LPC55xxx family."""

    IMAGE_TYPE = CRC_XIP_IMAGE

    def __init__(self, app: bytes = None, trust_zone: TrustZone = None) -> None:
        """Constructor for Master Boot CRC XiP Image for LPC55xxx family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        """
        self.app = align_block(app) if app else None
        self.tz = trust_zone or TrustZone.enabled()
        super().__init__()


class Mbi_SignedXip(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinTrustZone,
    Mbi_MixinCertBlockV2,
    Mbi_ExportMixinAppTrustZoneCertBlock,
    Mbi_ExportMixinRsaSign,
):
    """Master Boot Signed XiP Image for LPC55xxx family."""

    IMAGE_TYPE = SIGNED_XIP_IMAGE

    def __init__(
        self,
        app: bytes = None,
        trust_zone: TrustZone = None,
        cert_block: CertBlockV2 = None,
        priv_key_data: bytes = None,
    ) -> None:
        """Constructor for Master Boot Signed XiP Image for LPC55xxx family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param cert_block: Certification block of image, defaults to None
        :param priv_key_data: Private key used to sign image, defaults to None
        """
        self.app = align_block(app) if app else None
        self.tz = trust_zone or TrustZone.enabled()
        self.cert_block = cert_block
        self.priv_key_data = priv_key_data
        super().__init__()


########################################################################################################################
# Master Boot Image Class (i.MXRT5xx/i.MXRT6xx)
########################################################################################################################


class Mbi_PlainRamRtxxx(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinTrustZone,
    Mbi_MixinLoadAddress,
    Mbi_MixinHwKey,
    Mbi_ExportMixinAppTrustZone,
):
    """Master Boot Plain Image for RTxxx."""

    def __init__(
        self,
        app: bytes = None,
        trust_zone: TrustZone = None,
        load_addr: int = None,
        hwk: bool = False,
    ) -> None:
        """Constructor for Master Boot Plain XiP Image for RTxxx family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param load_addr: Load/Execution address in RAM of image, defaults to 0
        :param hwk: Enable HW user mode keys, defaults to false
        """
        self.app = align_block(app) if app else None
        self.tz = trust_zone or TrustZone.enabled()
        self.load_address = load_addr
        self.user_hw_key_enabled = hwk
        super().__init__()


class Mbi_PlainSignedRamRtxxx(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinRelocTable,
    Mbi_MixinLoadAddress,
    Mbi_MixinIvt,
    Mbi_MixinTrustZone,
    Mbi_MixinCertBlockV2,
    Mbi_MixinHmac,
    Mbi_MixinKeyStore,
    Mbi_MixinHwKey,
    Mbi_ExportMixinAppTrustZoneCertBlock,
    Mbi_ExportMixinRsaSign,
    Mbi_ExportMixinHmacKeyStoreFinalize,
):
    """Master Boot Plain Signed RAM Image for RTxxx family."""

    IMAGE_TYPE = SIGNED_RAM_IMAGE

    def __init__(
        self,
        app: bytes = None,
        app_table: MultipleImageTable = None,
        trust_zone: TrustZone = None,
        load_addr: int = None,
        cert_block: CertBlockV2 = None,
        priv_key_data: bytes = None,
        hmac_key: Union[bytes, str] = None,
        key_store: KeyStore = None,
        hwk: bool = False,
    ) -> None:
        """Constructor for Master Boot Plain Signed RAM Image for RTxxx family.

        :param app: Application image data, defaults to None
        :param app_table: Application table for additional application binaries, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param load_addr: Load/Execution address in RAM of image, defaults to 0
        :param cert_block: Certification block of image, defaults to None
        :param priv_key_data: Private key used to sign image, defaults to None
        :param hmac_key: HMAC key of image, defaults to None
        :param key_store: Optional KeyStore object for image, defaults to None
        :param hwk: Enable HW user mode keys, defaults to false
        """
        self.app = align_block(app) if app else None
        self.app_table = app_table
        self.load_address = load_addr
        self.tz = trust_zone or TrustZone.enabled()
        self.cert_block = cert_block
        self.priv_key_data = priv_key_data
        self.hmac_key = bytes.fromhex(hmac_key) if isinstance(hmac_key, str) else hmac_key
        self.key_store = key_store
        self.user_hw_key_enabled = hwk
        super().__init__()

    @property
    def app_len(self) -> int:
        """Application data length.

        :return: Application data length.
        """
        assert self.cert_block
        return self.get_app_length() + len(self.cert_block.export()) + len(self.tz.export())


class Mbi_CrcRamRtxxx(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinRelocTable,
    Mbi_MixinLoadAddress,
    Mbi_MixinIvt,
    Mbi_MixinTrustZone,
    Mbi_MixinHmac,
    Mbi_MixinKeyStore,
    Mbi_MixinHwKey,
    Mbi_ExportMixinAppTrustZone,
    Mbi_ExportMixinCrcSign,
    Mbi_ExportMixinHmacKeyStoreFinalize,
):
    """Master Boot CRC RAM Image for RTxxx family."""

    IMAGE_TYPE = CRC_RAM_IMAGE

    def __init__(
        self,
        app: bytes = None,
        app_table: MultipleImageTable = None,
        trust_zone: TrustZone = None,
        load_addr: int = None,
        hmac_key: Union[bytes, str] = None,
        key_store: KeyStore = None,
        hwk: bool = False,
    ) -> None:
        """Constructor for Master Boot CRC RAM Image for RTxxx family.

        :param app: Application image data, defaults to None
        :param app_table: Application table for additional application binaries, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param load_addr: Load/Execution address in RAM of image, defaults to 0
        :param hmac_key: HMAC key of image, defaults to None
        :param key_store: Optional KeyStore object for image, defaults to None
        :param hwk: Enable HW user mode keys, defaults to false
        """
        self.app = align_block(app) if app else None
        self.app_table = app_table
        self.tz = trust_zone or TrustZone.enabled()
        self.user_hw_key_enabled = hwk
        self.load_address = load_addr
        self.hmac_key = bytes.fromhex(hmac_key) if isinstance(hmac_key, str) else hmac_key
        self.key_store = key_store
        super().__init__()


class Mbi_EncryptedRamRtxxx(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinRelocTable,
    Mbi_MixinLoadAddress,
    Mbi_MixinIvt,
    Mbi_MixinTrustZone,
    Mbi_MixinCertBlockV2,
    Mbi_MixinHwKey,
    Mbi_MixinKeyStore,
    Mbi_MixinHmacMandatory,
    Mbi_MixinCtrInitVector,
    Mbi_ExportMixinRsaSign,
    Mbi_ExportMixinHmacKeyStoreFinalize,
):
    """Master Boot Encrypted RAM Image for RTxxx family."""

    IMAGE_TYPE = ENCRYPTED_RAM_IMAGE

    def __init__(
        self,
        app: bytes = None,
        app_table: MultipleImageTable = None,
        trust_zone: TrustZone = None,
        load_addr: int = None,
        cert_block: CertBlockV2 = None,
        priv_key_data: bytes = None,
        hmac_key: Union[bytes, str] = None,
        key_store: KeyStore = None,
        ctr_init_vector: bytes = None,
        hwk: bool = False,
    ) -> None:
        """Constructor for Master Boot Encrypted RAM Image for RTxxx family..

        :param app: Application image data, defaults to None
        :param app_table: Application table for additional application binaries, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param load_addr: Load/Execution address in RAM of image, defaults to 0
        :param cert_block: Certification block of image, defaults to None
        :param priv_key_data: Private key used to sign image, defaults to None
        :param hwk: Enable HW user mode keys, defaults to false
        :param key_store: Optional KeyStore object for image, defaults to None
        :param hmac_key: HMAC key of image, defaults to None
        :param ctr_init_vector: Counter initialization vector of image, defaults to None
        """
        self.app = align_block(app) if app else None
        self.load_address = load_addr
        self.app_table = app_table
        self.tz = trust_zone or TrustZone.enabled()
        self.cert_block = cert_block
        self.priv_key_data = priv_key_data
        self.user_hw_key_enabled = hwk
        self.key_store = key_store
        self.hmac_key = bytes.fromhex(hmac_key) if isinstance(hmac_key, str) else hmac_key
        self.store_ctr_init_vector(ctr_init_vector)
        self.img_len = 0
        super().__init__()

    def collect_data(self) -> bytes:
        """Collect basic data to create image.

        :return: Collected raw image.
        """
        assert self.cert_block
        self.cert_block.alignment = 4  # type: ignore   # this value is used by elf-to-sb-gui

        self.img_len = (
            self.total_len + self.cert_block.signature_size + 56 + 16
        )  # Encrypted IVT + IV

        return self.update_ivt(
            app_data=self.get_app_data() + self.tz.export(),
            total_len=self.img_len,
            crc_val_cert_offset=self.get_app_length(),
        )

    def encrypt(self, raw_image: bytes) -> bytes:
        """Encrypt image if needed.

        :param raw_image: Input raw image to encrypt.
        :return: Encrypted image.
        """
        assert self.hmac_key and self.ctr_init_vector
        key = self.hmac_key
        if not self.key_store or self.key_store.key_source == KeySourceType.OTP:
            key = KeyStore.derive_enc_image_key(key)
        aes = AES.new(key, AES.MODE_CTR, initial_value=self.ctr_init_vector, nonce=bytes())
        return aes.encrypt(raw_image + self.tz.export())

    def post_encrypt(self, image: bytes) -> bytes:
        """Optionally do some post encrypt image updates.

        :param image: Encrypted image.
        :return: Updated encrypted image.
        """
        assert self.cert_block
        enc_ivt = self.update_ivt(
            app_data=image[: self.HMAC_OFFSET],
            total_len=self.img_len,
            crc_val_cert_offset=self.get_app_length(),
        )
        # Create encrypted cert block (Encrypted IVT table + IV)
        encrypted_header = image[:56] + self.ctr_init_vector

        self.cert_block.image_length = (
            len(image) + len(self.cert_block.export()) + len(encrypted_header)
        )
        enc_cert = self.cert_block.export() + encrypted_header

        return (
            enc_ivt
            + image[self.HMAC_OFFSET : self.get_app_length()]  # header  # encrypted image
            + enc_cert  # certificate + encoded image header + CTR init vector
            + image[self.get_app_length() :]  # TZ encoded data
        )


class Mbi_PlainXipRtxxx(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinTrustZone,
    Mbi_MixinHwKey,
    Mbi_ExportMixinAppTrustZone,
):
    """Master Boot Plain XiP Image for RTxxx."""

    def __init__(self, app: bytes = None, trust_zone: TrustZone = None, hwk: bool = False) -> None:
        """Constructor for Master Boot Plain XiP Image for RTxxx family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param hwk: Enable HW user mode keys, defaults to false
        """
        self.app = align_block(app) if app else None
        self.tz = trust_zone or TrustZone.enabled()
        self.user_hw_key_enabled = hwk
        super().__init__()


class Mbi_PlainSignedXipRtxxx(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinTrustZone,
    Mbi_MixinCertBlockV2,
    Mbi_MixinHwKey,
    Mbi_ExportMixinAppTrustZoneCertBlock,
    Mbi_ExportMixinRsaSign,
):
    """Master Boot Plain Signed XiP Image for RTxxx family."""

    IMAGE_TYPE = SIGNED_XIP_IMAGE

    def __init__(
        self,
        app: bytes = None,
        trust_zone: TrustZone = None,
        cert_block: CertBlockV2 = None,
        priv_key_data: bytes = None,
        hwk: bool = False,
    ) -> None:
        """Constructor for Master Boot Plain Signed XiP Image for RTxxx family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param cert_block: Certification block of image, defaults to None
        :param priv_key_data: Private key used to sign image, defaults to None
        :param hwk: Enable HW user mode keys, defaults to false
        """
        self.app = align_block(app) if app else None
        self.tz = trust_zone or TrustZone.enabled()
        self.cert_block = cert_block
        self.priv_key_data = priv_key_data
        self.user_hw_key_enabled = hwk
        super().__init__()


class Mbi_CrcXipRtxxx(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinTrustZone,
    Mbi_MixinHwKey,
    Mbi_ExportMixinAppTrustZone,
    Mbi_ExportMixinCrcSign,
):
    """Master Boot CRC XiP Image for RTxxx."""

    IMAGE_TYPE = CRC_XIP_IMAGE

    def __init__(self, app: bytes = None, trust_zone: TrustZone = None, hwk: bool = False) -> None:
        """Constructor for Master Boot CRC XiP Image for RTxxx family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param hwk: Enable HW user mode keys, defaults to false
        """
        self.app = align_block(app) if app else None
        self.tz = trust_zone or TrustZone.enabled()
        self.user_hw_key_enabled = hwk
        super().__init__()


########################################################################################################################
# Master Boot Image Class (LPC55x3x)
########################################################################################################################
class Mbi_PlainRamLpc55s3x(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinTrustZoneMandatory,
    Mbi_MixinLoadAddress,
    Mbi_MixinFwVersion,
    Mbi_ExportMixinAppTrustZone,
):
    """Master Boot Plain RAM Image for LPC55s3x family."""

    def __init__(
        self,
        app: bytes = None,
        trust_zone: TrustZone = None,
        load_addr: int = 0,
        firmware_version: int = 0,
    ) -> None:
        """Constructor for Master Boot Plain RAM Image for LPC55s3x family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param load_addr: Load/Execution address in RAM of image, defaults to 0
        :param firmware_version: Firmware version of image, defaults to 0
        """
        self.app = align_block(app) if app else None
        self.tz = trust_zone or TrustZone.enabled()
        self.firmware_version = firmware_version
        self.load_address = load_addr
        super().__init__()


class Mbi_CrcRamLpc55s3x(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinTrustZoneMandatory,
    Mbi_MixinLoadAddress,
    Mbi_MixinFwVersion,
    Mbi_ExportMixinAppTrustZone,
    Mbi_ExportMixinCrcSign,
):
    """Master Boot CRC RAM Image for LPC55s3x family."""

    IMAGE_TYPE = CRC_RAM_IMAGE

    def __init__(
        self,
        app: bytes = None,
        trust_zone: TrustZone = None,
        load_addr: int = 0,
        firmware_version: int = 0,
    ) -> None:
        """Constructor for Master Boot Signed RAM Image for LPC55s3x family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param load_addr: Load/Execution address in RAM of image, defaults to 0
        :param firmware_version: Firmware version of image, defaults to 0
        """
        self.app = align_block(app) if app else None
        self.tz = trust_zone or TrustZone.enabled()
        self.load_address = load_addr
        self.firmware_version = firmware_version
        super().__init__()


class Mbi_PlainXipSignedLpc55s3x(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinCertBlockV31,
    Mbi_MixinManifest,
    Mbi_MixinFwVersion,
    Mbi_ExportMixinAppCertBlockManifest,
    Mbi_ExportMixinEccSign,
):
    """Master Boot Signed XIP Image for LPC55s3x family."""

    IMAGE_TYPE = SIGNED_XIP_IMAGE

    def __init__(
        self,
        app: bytes = None,
        firmware_version: int = 0,
        cert_block: CertBlockV31 = None,
        manifest: MasterBootImageManifest = None,
        signature_provider: SignatureProvider = None,
    ) -> None:
        """Constructor for Master Boot Signed XIP Image for LPC55s3x family.

        :param app: Application image data, defaults to None
        :param firmware_version: Firmware version of image, defaults to 0
        :param cert_block: Certification block of image, defaults to None
        :param manifest: Manifest of image, defaults to None
        :param signature_provider: Signature provider to sign final image, defaults to None
        """
        self.app = align_block(app) if app else None
        self.firmware_version = firmware_version
        self.cert_block = cert_block
        self.manifest = manifest
        self.signature_provider = signature_provider
        super().__init__()


class Mbi_PlainExtXipSignedLpc55s3x(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinCertBlockV31,
    Mbi_MixinManifest,
    Mbi_MixinFwVersion,
    Mbi_ExportMixinAppCertBlockManifest,
    Mbi_ExportMixinEccSign,
):
    """Master Boot Signed XIP Image stored in external memory for LPC55s3x family."""

    IMAGE_TYPE = SIGNED_XIP_IMAGE

    def __init__(
        self,
        app: bytes = None,
        firmware_version: int = 0,
        cert_block: CertBlockV31 = None,
        manifest: MasterBootImageManifest = None,
        signature_provider: SignatureProvider = None,
    ) -> None:
        """Constructor for Master Boot Signed XIP Image for LPC55s3x family.

        :param app: Application image data, defaults to None
        :param firmware_version: Firmware version of image, defaults to 0
        :param cert_block: Certification block of image, defaults to None
        :param manifest: Manifest of image, defaults to None
        :param signature_provider: Signature provider to sign final image, defaults to None
        """
        self.app = align_block(app) if app else None
        self.app_ext_memory_align = 0x1000
        self.firmware_version = firmware_version
        self.cert_block = cert_block
        self.manifest = manifest
        self.signature_provider = signature_provider
        super().__init__()


class Mbi_CrcXipLpc55s3x(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinTrustZoneMandatory,
    Mbi_MixinFwVersion,
    Mbi_ExportMixinAppTrustZone,
    Mbi_ExportMixinCrcSign,
):
    """Master Boot CRC XiP Image for LPC55s3x family."""

    IMAGE_TYPE = CRC_XIP_IMAGE

    def __init__(
        self, app: bytes = None, trust_zone: TrustZone = None, firmware_version: int = 0
    ) -> None:
        """Constructor for Master Boot CRC XiP Image for LPC55s3x family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param firmware_version: Firmware version of image, defaults to 0
        """
        self.app = align_block(app) if app else None
        self.tz = trust_zone or TrustZone.enabled()
        self.firmware_version = firmware_version
        super().__init__()


class Mbi_CrcExtXipLpc55s3x(
    MasterBootImage,
    Mbi_MixinApp,
    Mbi_MixinIvt,
    Mbi_MixinTrustZoneMandatory,
    Mbi_MixinFwVersion,
    Mbi_ExportMixinAppTrustZone,
    Mbi_ExportMixinCrcSign,
):
    """Master Boot CRC XiP Image for LPC55s3x family."""

    IMAGE_TYPE = CRC_XIP_IMAGE

    def __init__(
        self, app: bytes = None, trust_zone: TrustZone = None, firmware_version: int = 0
    ) -> None:
        """Constructor for Master Boot CRC XiP Image stored in external memory for LPC55s3x family.

        :param app: Application image data, defaults to None
        :param trust_zone: TrustZone object, defaults to None
        :param firmware_version: Firmware version of image, defaults to 0
        """
        self.app = align_block(app) if app else None
        self.app_ext_memory_align = 0x1000
        self.tz = trust_zone or TrustZone.enabled()
        self.firmware_version = firmware_version
        super().__init__()
