###############################################################################
#
# ContentTypes - A class for writing the Excel XLSX ContentTypes file.
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2013-2025, John McNamara, jmcnamara@cpan.org
#

import copy
from typing import Dict, Tuple

from . import xmlwriter

# Long namespace strings used in the class.
APP_PACKAGE = "application/vnd.openxmlformats-package."
APP_DOCUMENT = "application/vnd.openxmlformats-officedocument."

defaults = [
    ("rels", APP_PACKAGE + "relationships+xml"),
    ("xml", "application/xml"),
]

overrides = [
    ("/docProps/app.xml", APP_DOCUMENT + "extended-properties+xml"),
    ("/docProps/core.xml", APP_PACKAGE + "core-properties+xml"),
    ("/xl/styles.xml", APP_DOCUMENT + "spreadsheetml.styles+xml"),
    ("/xl/theme/theme1.xml", APP_DOCUMENT + "theme+xml"),
    ("/xl/workbook.xml", APP_DOCUMENT + "spreadsheetml.sheet.main+xml"),
]


class ContentTypes(xmlwriter.XMLwriter):
    """
    A class for writing the Excel XLSX ContentTypes file.


    """

    ###########################################################################
    #
    # Public API.
    #
    ###########################################################################

    def __init__(self) -> None:
        """
        Constructor.

        """

        super().__init__()

        # Copy the defaults in case we need to change them.
        self.defaults = copy.deepcopy(defaults)
        self.overrides = copy.deepcopy(overrides)

    ###########################################################################
    #
    # Private API.
    #
    ###########################################################################

    def _assemble_xml_file(self) -> None:
        # Assemble and write the XML file.

        # Write the XML declaration.
        self._xml_declaration()

        self._write_types()
        self._write_defaults()
        self._write_overrides()

        self._xml_end_tag("Types")

        # Close the file.
        self._xml_close()

    def _add_default(self, default: Tuple[str, str]) -> None:
        # Add elements to the ContentTypes defaults.
        self.defaults.append(default)

    def _add_override(self, override: Tuple[str, str]) -> None:
        # Add elements to the ContentTypes overrides.
        self.overrides.append(override)

    def _add_worksheet_name(self, worksheet_name: str) -> None:
        # Add the name of a worksheet to the ContentTypes overrides.
        worksheet_name = "/xl/worksheets/" + worksheet_name + ".xml"

        self._add_override(
            (worksheet_name, APP_DOCUMENT + "spreadsheetml.worksheet+xml")
        )

    def _add_chartsheet_name(self, chartsheet_name: str) -> None:
        # Add the name of a chartsheet to the ContentTypes overrides.
        chartsheet_name = "/xl/chartsheets/" + chartsheet_name + ".xml"

        self._add_override(
            (chartsheet_name, APP_DOCUMENT + "spreadsheetml.chartsheet+xml")
        )

    def _add_chart_name(self, chart_name: str) -> None:
        # Add the name of a chart to the ContentTypes overrides.
        chart_name = "/xl/charts/" + chart_name + ".xml"

        self._add_override((chart_name, APP_DOCUMENT + "drawingml.chart+xml"))

    def _add_drawing_name(self, drawing_name: str) -> None:
        # Add the name of a drawing to the ContentTypes overrides.
        drawing_name = "/xl/drawings/" + drawing_name + ".xml"

        self._add_override((drawing_name, APP_DOCUMENT + "drawing+xml"))

    def _add_vml_name(self) -> None:
        # Add the name of a VML drawing to the ContentTypes defaults.
        self._add_default(("vml", APP_DOCUMENT + "vmlDrawing"))

    def _add_comment_name(self, comment_name: str) -> None:
        # Add the name of a comment to the ContentTypes overrides.
        comment_name = "/xl/" + comment_name + ".xml"

        self._add_override((comment_name, APP_DOCUMENT + "spreadsheetml.comments+xml"))

    def _add_shared_strings(self) -> None:
        # Add the sharedStrings link to the ContentTypes overrides.
        self._add_override(
            ("/xl/sharedStrings.xml", APP_DOCUMENT + "spreadsheetml.sharedStrings+xml")
        )

    def _add_calc_chain(self) -> None:
        # Add the calcChain link to the ContentTypes overrides.
        self._add_override(
            ("/xl/calcChain.xml", APP_DOCUMENT + "spreadsheetml.calcChain+xml")
        )

    def _add_image_types(self, image_types: Dict[str, bool]) -> None:
        # Add the image default types.
        for image_type in image_types:
            extension = image_type

            if image_type in ("wmf", "emf"):
                image_type = "x-" + image_type

            self._add_default((extension, "image/" + image_type))

    def _add_table_name(self, table_name: str) -> None:
        # Add the name of a table to the ContentTypes overrides.
        table_name = "/xl/tables/" + table_name + ".xml"

        self._add_override((table_name, APP_DOCUMENT + "spreadsheetml.table+xml"))

    def _add_vba_project(self) -> None:
        # Add a vbaProject to the ContentTypes defaults.

        # Change the workbook.xml content-type from xlsx to xlsm.
        for i, override in enumerate(self.overrides):
            if override[0] == "/xl/workbook.xml":
                xlsm = "application/vnd.ms-excel.sheet.macroEnabled.main+xml"
                self.overrides[i] = ("/xl/workbook.xml", xlsm)

        self._add_default(("bin", "application/vnd.ms-office.vbaProject"))

    def _add_vba_project_signature(self) -> None:
        # Add a vbaProjectSignature to the ContentTypes overrides.
        self._add_override(
            (
                "/xl/vbaProjectSignature.bin",
                "application/vnd.ms-office.vbaProjectSignature",
            )
        )

    def _add_custom_properties(self) -> None:
        # Add the custom properties to the ContentTypes overrides.
        self._add_override(
            ("/docProps/custom.xml", APP_DOCUMENT + "custom-properties+xml")
        )

    def _add_metadata(self) -> None:
        # Add the metadata file to the ContentTypes overrides.
        self._add_override(
            ("/xl/metadata.xml", APP_DOCUMENT + "spreadsheetml.sheetMetadata+xml")
        )

    def _add_feature_bag_property(self) -> None:
        # Add the featurePropertyBag file to the ContentTypes overrides.
        self._add_override(
            (
                "/xl/featurePropertyBag/featurePropertyBag.xml",
                "application/vnd.ms-excel.featurepropertybag+xml",
            )
        )

    def _add_rich_value(self) -> None:
        # Add the richValue files to the ContentTypes overrides.
        self._add_override(
            (
                "/xl/richData/rdRichValueTypes.xml",
                "application/vnd.ms-excel.rdrichvaluetypes+xml",
            )
        )

        self._add_override(
            ("/xl/richData/rdrichvalue.xml", "application/vnd.ms-excel.rdrichvalue+xml")
        )

        self._add_override(
            (
                "/xl/richData/rdrichvaluestructure.xml",
                "application/vnd.ms-excel.rdrichvaluestructure+xml",
            )
        )

        self._add_override(
            (
                "/xl/richData/richValueRel.xml",
                "application/vnd.ms-excel.richvaluerel+xml",
            )
        )

    ###########################################################################
    #
    # XML methods.
    #
    ###########################################################################

    def _write_defaults(self) -> None:
        # Write out all of the <Default> types.

        for extension, content_type in self.defaults:
            self._xml_empty_tag(
                "Default", [("Extension", extension), ("ContentType", content_type)]
            )

    def _write_overrides(self) -> None:
        # Write out all of the <Override> types.
        for part_name, content_type in self.overrides:
            self._xml_empty_tag(
                "Override", [("PartName", part_name), ("ContentType", content_type)]
            )

    def _write_types(self) -> None:
        # Write the <Types> element.
        xmlns = "http://schemas.openxmlformats.org/package/2006/content-types"

        attributes = [
            (
                "xmlns",
                xmlns,
            )
        ]
        self._xml_start_tag("Types", attributes)

    def _write_default(self, extension, content_type) -> None:
        # Write the <Default> element.
        attributes = [
            ("Extension", extension),
            ("ContentType", content_type),
        ]

        self._xml_empty_tag("Default", attributes)

    def _write_override(self, part_name, content_type) -> None:
        # Write the <Override> element.
        attributes = [
            ("PartName", part_name),
            ("ContentType", content_type),
        ]

        self._xml_empty_tag("Override", attributes)
