# CCSDS-NDM: CCSDS Navigation Data Messages Read/Write Library
#
# Copyright (C) Egemen Imre
#
# Licensed under GNU GPL v3.0. See LICENSE.rst for more info.
"""
KVN handler registry for the ndmxml 2.0 schema.

Maps xsdata class name (str) → TypeHandler and root class name → dispatch
strategy.  Classes absent from the registry implicitly use DEFAULT_HANDLER.

All-default types (no explicit entry needed)
--------------------------------------------
NdmHeader, OpmMetadata, OemMetadata, AemMetadata, OmmMetadata,
ApmMetadata, TdmMetadata, CdmMetadata,
StateVectorType (OPM — plain KEY=VALUE, not packed),
KeplerianElementsType, ManeuverParametersType,
SpacecraftParametersType, OdParametersType,
OpmCovarianceMatrixType (plain KEY=VALUE, no packed rows),
CdmCovarianceMatrixType (plain KEY=VALUE lower-triangular fields),
UserDefinedType, UserDefinedParameterType,
QuaternionType, QuaternionRateType,
QuaternionEphemerisType, QuaternionDerivativeType, QuaternionEulerRateType,
EulerAngleType, EulerAngleRateType,
SpinType, SpinNutationType,
and all scalar-with-units leaf types (PositionType, VelocityType, …).

Differences from ndmxml1
------------------------
- Covers OPM v2.0, OEM v2.0, OMM v2.0 (ndmxml1 has OPM v1.0 / OEM v2.0 but
  different schema generation; ndmxml2 adds OMM v2.0).
- CDM covariance class is ``CdmCovarianceMatrixType`` (ndmxml1 uses
  ``CdmCovarianceType`` — same KVN structure, renamed in schema v2).
- APM and AEM are first introduced in ndmxml2 (ndmxml1 also defines them but
  the mapping only routes AEMv1 / APMv1 through ndmxml2 classes).
- The registry and dispatch for this schema are structurally identical to
  ndmxml1 because the special handler types share the same class names.
"""

from __future__ import annotations

from ccsds_ndm.kvn_handlers import (
    DEFAULT_HANDLER,
    DISPATCH_CDM,
    DISPATCH_FLAT,
    DISPATCH_SEGMENTED,
    LOC_COVARIANCE,
    LOC_DEFAULT,
    LOC_PACKED_ATTITUDE,
    LOC_PACKED_STATE,
    LOC_TDM_OBS,
    PARSE_COVARIANCE,
    PARSE_PACKED_ATTITUDE,
    PARSE_PACKED_STATE,
    PARSE_ROTATION_ANGLE,
    PARSE_ROTATION_RATE,
    PARSE_TDM_OBS,
    WRITE_COVARIANCE,
    WRITE_PACKED_ATTITUDE,
    WRITE_PACKED_STATE,
    WRITE_ROTATION_ANGLE,
    WRITE_ROTATION_RATE,
    WRITE_TDM_OBS,
    TypeHandler,
)


class NdmXml2Registry:
    """KVN handler registry for the ndmxml 2.0 schema."""

    def __init__(self) -> None:
        self.version: int = 2

        # NDM type ids that use segmented dispatch (META_START/STOP blocks)
        self.segmented_ids: frozenset[str] = frozenset({"aem", "oem", "tdm"})

        # NDM type ids that use CDM dispatch (split on OBJECT_1/OBJECT_2)
        self.cdm_ids: frozenset[str] = frozenset({"cdm"})

        # Maps xsdata class name → TypeHandler (absent → DEFAULT_HANDLER)
        self.registry: dict[str, TypeHandler] = {
            # -- OEM: packed epoch-led state vector rows
            "StateVectorAccType": TypeHandler(
                locate=LOC_PACKED_STATE,
                parse=PARSE_PACKED_STATE,
                write=WRITE_PACKED_STATE,
            ),
            # -- AEM: packed attitude rows (column template from ctx['meta'].attitude_type)
            "AttitudeStateType": TypeHandler(
                locate=LOC_PACKED_ATTITUDE,
                parse=PARSE_PACKED_ATTITUDE,
                write=WRITE_PACKED_ATTITUDE,
            ),
            # -- OEM covariance block (EPOCH line + lower-triangular numeric rows)
            "OemCovarianceMatrixType": TypeHandler(
                locate=LOC_COVARIANCE,
                parse=PARSE_COVARIANCE,
                write=WRITE_COVARIANCE,
            ),
            # -- TDM observation lines: KEY = EPOCH VALUE
            "TrackingDataObservationType": TypeHandler(
                locate=LOC_TDM_OBS,
                parse=PARSE_TDM_OBS,
                write=WRITE_TDM_OBS,
            ),
            # -- APM Euler angle container: keyword from rotation.angle discriminator
            #    (X_ANGLE / Y_ANGLE / Z_ANGLE); locate is default (keywords are in kw set)
            "RotationAngleType": TypeHandler(
                locate=LOC_DEFAULT,
                parse=PARSE_ROTATION_ANGLE,
                write=WRITE_ROTATION_ANGLE,
            ),
            # -- APM Euler rate container: keyword from rotation.rate discriminator
            #    (X_RATE / Y_RATE / Z_RATE)
            "RotationRateType": TypeHandler(
                locate=LOC_DEFAULT,
                parse=PARSE_ROTATION_RATE,
                write=WRITE_ROTATION_RATE,
            ),
        }

        # Maps root class name → dispatch strategy sentinel
        self.dispatch: dict[str, str] = {
            # Flat: single segment, no META_START/STOP in KVN body
            "Opm": DISPATCH_FLAT,
            "Omm": DISPATCH_FLAT,
            "Apm": DISPATCH_FLAT,
            "Rdm": DISPATCH_FLAT,
            # Segmented: META_START/META_STOP/DATA_START/DATA_STOP state machine
            "Oem": DISPATCH_SEGMENTED,
            "Aem": DISPATCH_SEGMENTED,
            "Tdm": DISPATCH_SEGMENTED,
            # Special CDM structure
            "Cdm": DISPATCH_CDM,
        }

    def get_handler(self, cls_name: str) -> TypeHandler:
        """Return the TypeHandler for ``cls_name``, falling back to DEFAULT_HANDLER."""
        return self.registry.get(cls_name, DEFAULT_HANDLER)


# Module-level singleton
REGISTRY_INSTANCE = NdmXml2Registry()
