from datetime import datetime
from typing import Optional, Dict, Any
from uuid import uuid4
from lxml import etree
from osgeo import gdal
from sqlalchemy import Column, String, Enum, Date, ForeignKey, CheckConstraint, Boolean, Table, event, PrimaryKeyConstraint, inspect, Float, Integer
from sqlalchemy.dialects.postgresql import UUID, BYTEA
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import relationship, deferred, declared_attr
from SAGisXPlanung import Base, XPlanVersion
from SAGisXPlanung.MapLayerRegistry import MapLayerRegistry
from SAGisXPlanung.XPlan.codelists import CodeListLegacy
from SAGisXPlanung.XPlan.enums import XP_ExterneReferenzArt, XP_ExterneReferenzTyp, XP_SPEMassnahmenTypen, XP_ArtHoehenbezug, XP_ArtHoehenbezugspunkt, XP_RechtscharakterPlanaenderung, XP_Aenderungsarten
from SAGisXPlanung.core.mixins.mixins import RelationshipMixin, ElementOrderMixin
from SAGisXPlanung.XPlan.types import RefURL, RegExString, ConformityException, LargeString, Length, XPEnum
XP_PlanXP_GemeindeAssoc = Table('xp_plan_gemeinde', Base.metadata, Column('bp_plan_id', UUID(as_uuid=True), ForeignKey('bp_plan.id', ondelete='CASCADE')), Column('fp_plan_id', UUID(as_uuid=True), ForeignKey('fp_plan.id', ondelete='CASCADE')), Column('lp_plan_id', UUID(as_uuid=True), ForeignKey('lp_plan.id', ondelete='CASCADE'), info={'xplan_version': XPlanVersion.SIX}), Column('gemeinde_id', UUID(as_uuid=True), ForeignKey('xp_gemeinde.id')))
XP_PlanXP_GesetzlicheGrundlageAssoc = Table('xp_plan_gesetzlichegrundlage', Base.metadata, Column('bp_plan_id', UUID(as_uuid=True), ForeignKey('bp_plan.id', ondelete='CASCADE'), nullable=True), Column('fp_plan_id', UUID(as_uuid=True), ForeignKey('fp_plan.id', ondelete='CASCADE'), nullable=True), Column('gesetzlichegrundlage_id', UUID(as_uuid=True), ForeignKey('xp_gesetzliche_grundlage.id'), nullable=False))

class XP_Gemeinde(RelationshipMixin, ElementOrderMixin, Base):
    """ Spezifikation einer für die Aufstellung des Plans zuständigen Gemeinde. """
    __tablename__ = 'xp_gemeinde'
    __avoidRelation__ = ['bp_plans', 'fp_plans', 'lp_plans']
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    bp_plans = relationship('BP_Plan', back_populates='gemeinde', secondary=XP_PlanXP_GemeindeAssoc)
    fp_plans = relationship('FP_Plan', back_populates='gemeinde', secondary=XP_PlanXP_GemeindeAssoc)
    lp_plans = relationship('LP_Plan', back_populates='gemeinde', secondary=XP_PlanXP_GemeindeAssoc)
    ags = Column(RegExString('\\d{8}\\b', 8, error_msg='Gemeindeschlüssel muss aus 8 Ziffern bestehen'), doc='Gemeindeschlüssel (AGS)')
    rs = Column(RegExString('\\d{12}\\b', 12, error_msg='Regionalschlüssel muss aus 12 Ziffern bestehen'), doc='Regionalschlüssel')
    gemeindeName = Column(String(), nullable=False, doc='Name der Gemeinde')
    ortsteilName = Column(String(), doc='Ortsteilbezeichnung')

    def __str__(self):
        return self.gemeindeName + f'{(f', OT {self.ortsteilName}' if self.ortsteilName else '')}'

    def __eq__(self, other):
        if type(other) is type(self):
            return self.ags == other.ags and self.ortsteilName == other.ortsteilName
        return False

    def validate(self):
        if not self.ags and (not self.rs):
            raise ConformityException('Die Attribute <code>ags</code> und <code>rs</code> dürfen nicht beide unbelegt sein', '3.2.5.1', self.__class__.__name__)

    @classmethod
    def avoid_export(cls):
        return ['bp_plans', 'fp_plans', 'lp_plans']

@event.listens_for(XP_Gemeinde, 'before_insert')
@event.listens_for(XP_Gemeinde, 'before_update')
def checkIntegrityXP_Gemeinde(mapper, connection, xp_gemeinde):
    if not xp_gemeinde.ags and (not xp_gemeinde.rs):
        raise IntegrityError("Die Attribute 'ags' und 'rs' dürfen nicht beide unbelegt sein", {'name': 'Konsistenz  der  Attribute  ags  (Amtlicher  Gemeindeschlüssel)  und  rs  (Regionalschlüssel)', 'nummer': '3.2.5.1'}, xp_gemeinde)

class XP_ExterneReferenz(RelationshipMixin, ElementOrderMixin, Base):
    """ XP_ExterneReferenz

    Verweis auf ein extern gespeichertes Dokument oder einen extern gespeicherten, georeferenzierten Plan.
    Einer der beiden Attribute "referenzName" bzw. "referenzURL" muss belegt sein.
    """
    __tablename__ = 'xp_externe_referenz'
    __table_args__ = (CheckConstraint('NOT("referenzName" IS NULL AND "referenzURL" IS NULL)'),)
    __avoidRelation__ = ['bereich', 'baugebiet', 'bp_schutzflaeche_massnahme', 'bp_schutzflaeche_plan', 'veraenderungssperre', 'grundstueck_ueberbaubar', 'xp_text_abschnitt', 'bp_wohngebaeude_flaeche', 'xp_rasterdarstellung_scan', 'xp_rasterdarstellung_text', 'xp_rasterdarstellung_legende']
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    georefURL = Column(RefURL, doc='Verweis auf Georeferenzierungs-Datei')
    art = Column(Enum(XP_ExterneReferenzArt), doc='Art der Referenz')
    referenzName = Column(String, nullable=False, doc='Name bzw. Titel')
    referenzURL = Column(RefURL, nullable=False, doc='URI der Referenz')
    referenzMimeType_id = Column(UUID(as_uuid=True), ForeignKey('codelist_values.id'))
    referenzMimeType = relationship('XP_MimeTypes', back_populates='ext_refs', foreign_keys=[referenzMimeType_id], info={'form-type': 'inline'})
    beschreibung = Column(LargeString, doc='Beschreibung')
    datum = Column(Date, doc='Datum')
    file = deferred(Column(BYTEA))
    georef_file = deferred(Column(BYTEA))
    bereich_id = Column(UUID(as_uuid=True), ForeignKey('xp_bereich.id', ondelete='CASCADE'))
    bereich = relationship('XP_Bereich', back_populates='refScan')
    baugebiet_id = Column(UUID(as_uuid=True), ForeignKey('bp_baugebiet.id', ondelete='CASCADE'))
    baugebiet = relationship('BP_BaugebietsTeilFlaeche', back_populates='refGebaeudequerschnitt')
    grundstueck_ueberbaubar_id = Column(UUID(as_uuid=True), ForeignKey('bp_grundstueck_ueberbaubar.id', ondelete='CASCADE'))
    grundstueck_ueberbaubar = relationship('BP_UeberbaubareGrundstuecksFlaeche', back_populates='refGebaeudequerschnitt')
    bp_schutzflaeche_massnahme_id = Column(UUID(as_uuid=True), ForeignKey('bp_schutzflaeche.id', ondelete='CASCADE'))
    bp_schutzflaeche_massnahme = relationship('BP_SchutzPflegeEntwicklungsFlaeche', foreign_keys=[bp_schutzflaeche_massnahme_id], back_populates='refMassnahmenText')
    bp_schutzflaeche_plan_id = Column(UUID(as_uuid=True), ForeignKey('bp_schutzflaeche.id', ondelete='CASCADE'))
    bp_schutzflaeche_plan = relationship('BP_SchutzPflegeEntwicklungsFlaeche', foreign_keys=[bp_schutzflaeche_plan_id], back_populates='refLandschaftsplan')
    veraenderungssperre_id = Column(UUID(as_uuid=True), ForeignKey('bp_veraenderungssperre_daten.id', ondelete='CASCADE'))
    veraenderungssperre = relationship('BP_VeraenderungssperreDaten', back_populates='refBeschluss')
    xp_text_abschnitt_id = Column(UUID(as_uuid=True), ForeignKey('xp_text_abschnitt.id', ondelete='CASCADE'))
    xp_text_abschnitt = relationship('XP_TextAbschnitt', back_populates='refText')
    bp_wohngebaeude_flaeche_id = Column(UUID(as_uuid=True), ForeignKey('bp_wohngebaeude_flaeche.id', ondelete='CASCADE'))
    bp_wohngebaeude_flaeche = relationship('BP_WohngebaeudeFlaeche', back_populates='refGebaeudequerschnitt', info={'xplan_version': XPlanVersion.SIX})
    xp_rasterdarstellung_scan_id = Column(UUID(as_uuid=True), ForeignKey('xp_rasterdarstellung.id', ondelete='CASCADE'))
    xp_rasterdarstellung_scan = relationship('XP_Rasterdarstellung', foreign_keys=[xp_rasterdarstellung_scan_id], back_populates='refScan', info={'xplan_version': XPlanVersion.FIVE_THREE})
    xp_rasterdarstellung_text_id = Column(UUID(as_uuid=True), ForeignKey('xp_rasterdarstellung.id', ondelete='CASCADE'))
    xp_rasterdarstellung_text = relationship('XP_Rasterdarstellung', foreign_keys=[xp_rasterdarstellung_text_id], back_populates='refText', info={'xplan_version': XPlanVersion.FIVE_THREE})
    xp_rasterdarstellung_legende_id = Column(UUID(as_uuid=True), ForeignKey('xp_rasterdarstellung.id', ondelete='CASCADE'))
    xp_rasterdarstellung_legende = relationship('XP_Rasterdarstellung', foreign_keys=[xp_rasterdarstellung_legende_id], back_populates='refLegende', info={'xplan_version': XPlanVersion.FIVE_THREE})
    type = Column(String(50))
    __mapper_args__ = {'polymorphic_identity': 'xp_externe_referenz', 'polymorphic_on': type}

    def validate(self):
        if not self.referenzName and (not self.referenzURL):
            raise ConformityException('Mindestens eines der Attribute <code>referenzName</code> und <code>referenzURL</code> muss belegt sein.', '3.2.4.2', self.__class__.__name__)

    def set_file_data(self, attr_name: str, file_data: bytes):
        if attr_name == 'referenzURL':
            setattr(self, 'file', file_data)
        elif attr_name == 'georefURL':
            setattr(self, 'georef_file', file_data)

    def get_file_data(self) -> Dict[str, Any]:
        result = {}
        if self.referenzURL is not None:
            result[self.referenzURL] = self.file
            if self.georefURL is not None:
                result[self.georefURL] = self.georef_file
        return result

    @classmethod
    def hidden_inputs(cls):
        return ['file', 'georef_file']

    @classmethod
    def avoid_export(cls):
        return ['file', 'georef_file', 'bereich', 'baugebiet', 'bp_schutzflaeche_massnahme', 'bp_schutzflaeche_plan', 'veraenderungssperre', 'grundstueck_ueberbaubar', 'xp_text_abschnitt', 'bp_wohngebaeude_flaeche', 'xp_rasterdarstellung_scan', 'xp_rasterdarstellung_text', 'xp_rasterdarstellung_legende']

@event.listens_for(XP_ExterneReferenz, 'after_update', propagate=True)
def file_data_changed(mapper, connection, xp_ref: XP_ExterneReferenz):
    layer = MapLayerRegistry().layer_by_orm_id(str(xp_ref.id))
    if layer is None:
        return
    history = [inspect(xp_ref).attrs.file.history, inspect(xp_ref).attrs.georef_file.history]
    if not any((h.has_changes() for h in history)):
        return
    for filename, file_data in xp_ref.get_file_data().items():
        if file_data is not None:
            vsi_path = f'/vsimem/{filename}'
            gdal.FileFromMemBuffer(vsi_path, file_data)
            layer.setDataSource(vsi_path, layer.name(), layer.providerType())

class XP_SpezExterneReferenz(XP_ExterneReferenz):
    """ Ergänzung des Datentyps XP_ExterneReferenz um ein Attribut zur semantischen Beschreibung des referierten Dokuments. """
    __tablename__ = 'xp_spez_externe_referenz'
    __mapper_args__ = {'polymorphic_identity': 'xp_spez_externe_referenz'}
    __avoidRelation__ = ['plan']
    id = Column(ForeignKey('xp_externe_referenz.id', ondelete='CASCADE'), primary_key=True)
    typ = Column(Enum(XP_ExterneReferenzTyp), doc='Typ/Inhalt der Referenz')
    plan_id = Column(UUID(as_uuid=True), ForeignKey('xp_plan.id', ondelete='CASCADE'))
    plan = relationship('XP_Plan', back_populates='externeReferenz')

    @classmethod
    def avoid_export(cls):
        base = super(XP_SpezExterneReferenz, cls).avoid_export()
        return base + ['plan']

class XP_Plangeber(RelationshipMixin, ElementOrderMixin, Base):
    """ Spezifikation der Institution, die für den Plan verantwortlich ist. """
    __tablename__ = 'xp_plangeber'
    __avoidRelation__ = ['bp_plans', 'fp_plans', 'lp_plans']
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    bp_plans = relationship('BP_Plan', back_populates='plangeber')
    fp_plans = relationship('FP_Plan', back_populates='plangeber')
    lp_plans = relationship('LP_Plan', back_populates='plangeber')
    name = Column(String, nullable=False, doc='Name')
    kennziffer = Column(String, doc='Kennziffer')

    def __str__(self):
        return self.name

    def __eq__(self, other):
        if type(other) is type(self):
            return self.name == other.name
        return False

    @classmethod
    def avoid_export(cls):
        return ['bp_plans', 'fp_plans', 'lp_plans']

@event.listens_for(XP_Plangeber, 'before_insert')
@event.listens_for(XP_Plangeber, 'before_update')
def checkIntegrityXP_Plangeber(mapper, connection, xp_plangeber):
    if not xp_plangeber.name:
        raise IntegrityError("Das Attribut 'name' benötigt einen Wert", None, xp_plangeber)

class XP_VerfahrensMerkmal(RelationshipMixin, ElementOrderMixin, Base):
    """ Vermerk eines am Planungsverfahrens beteiligten Akteurs. """
    __tablename__ = 'xp_verfahrens_merkmal'
    __avoidRelation__ = ['plan']
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    plan_id = Column(UUID(as_uuid=True), ForeignKey('xp_plan.id', ondelete='CASCADE'))
    plan = relationship('XP_Plan', back_populates='verfahrensMerkmale')
    vermerk = Column(LargeString, nullable=False)
    datum = Column(Date, nullable=False)
    signatur = Column(String, nullable=False)
    signiert = Column(Boolean, nullable=False)

    @classmethod
    def avoid_export(cls):
        return ['plan']

class XP_SPEMassnahmenDaten(RelationshipMixin, ElementOrderMixin, Base):
    """ Spezifikation der Attribute für einer Schutz-, Pflege- oder Entwicklungsmaßnahme """
    __tablename__ = 'xp_spe_daten'
    __avoidRelation__ = ['bp_schutzflaeche', 'fp_schutzflaeche']
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    klassifizMassnahme = Column(Enum(XP_SPEMassnahmenTypen))
    massnahmeText = Column(String)
    massnahmeKuerzel = Column(String)
    bp_schutzflaeche_id = Column(UUID(as_uuid=True), ForeignKey('bp_schutzflaeche.id', ondelete='CASCADE'))
    bp_schutzflaeche = relationship('BP_SchutzPflegeEntwicklungsFlaeche', back_populates='massnahme')
    fp_schutzflaeche_id = Column(UUID(as_uuid=True), ForeignKey('fp_schutzflaeche.id', ondelete='CASCADE'))
    fp_schutzflaeche = relationship('FP_SchutzPflegeEntwicklung', back_populates='massnahme')

    @classmethod
    def avoid_export(cls):
        return ['bp_schutzflaeche', 'fp_schutzflaeche']

class XP_GesetzlicheGrundlage(RelationshipMixin, ElementOrderMixin, Base):
    """ Angeben zur Spezifikation der gesetzlichen Grundlage eines Planinhalts. """
    __tablename__ = 'xp_gesetzliche_grundlage'
    __avoidRelation__ = ['xp_objekts', 'fp_bau_nvo', 'fp_bau_gb', 'fp_bau_sonst', 'bp_bau_nvo', 'bp_bau_gb', 'bp_bau_sonst']
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    xp_objekts = relationship('XP_Objekt', back_populates='gesetzlicheGrundlage')
    fp_bau_nvo = relationship('FP_Plan', back_populates='versionBauNVO', foreign_keys='FP_Plan.versionBauNVO_id')
    fp_bau_gb = relationship('FP_Plan', back_populates='versionBauGB', foreign_keys='FP_Plan.versionBauGB_id')
    fp_bau_sonst = relationship('FP_Plan', secondary=XP_PlanXP_GesetzlicheGrundlageAssoc, back_populates='versionSonstRechtsgrundlage', info={'xplan_version': XPlanVersion.SIX})
    bp_bau_nvo = relationship('BP_Plan', back_populates='versionBauNVO', foreign_keys='BP_Plan.versionBauNVO_id')
    bp_bau_gb = relationship('BP_Plan', back_populates='versionBauGB', foreign_keys='BP_Plan.versionBauGB_id')
    bp_bau_sonst = relationship('BP_Plan', secondary=XP_PlanXP_GesetzlicheGrundlageAssoc, back_populates='versionSonstRechtsgrundlage', info={'xplan_version': XPlanVersion.SIX})
    name = Column(String)
    datum = Column(Date)
    detail = Column(String)

    def __str__(self):
        return f'{self.name}, {self.datum}'

    def __eq__(self, other):
        if not isinstance(other, type(self)):
            return NotImplemented

        def _to_date(d):
            if isinstance(d, datetime):
                return d.date()
            return d
        return self.name == other.name and _to_date(self.datum) == _to_date(other.datum)

    @classmethod
    def avoid_export(cls):
        return ['xp_objekts', 'fp_bau_nvo', 'fp_bau_gb', 'fp_bau_sonst', 'bp_bau_nvo', 'bp_bau_gb', 'bp_bau_sonst']

class XP_Hoehenangabe(RelationshipMixin, ElementOrderMixin, Base):
    """ Spezifikation einer Angabe zur vertikalen Höhe oder zu einem Bereich vertikaler Höhen. Es ist möglich,
    spezifische Höhenangaben (z.B. die First- oder Traufhöhe eines Gebäudes) vorzugeben oder einzuschränken,
    oder den Gültigkeitsbereich eines Planinhalts auf eine bestimmte Höhe (hZwingend) bzw. einen Höhenbereich
    (hMin - hMax) zu beschränken, was vor allem bei der höhenabhängigen Festsetzung einer überbaubaren
    Grundstücksfläche (BP_UeberbaubareGrundstuecksflaeche), einer Baulinie (BP_Baulinie) oder einer Baugrenze
    (BP_Baugrenze) relevant ist. In diesem Fall bleiben die Attribute bezugspunkt und abweichenderBezugspunkt
    unbelegt """
    __tablename__ = 'xp_hoehenangabe'
    __avoidRelation__ = ['xp_objekt', 'dachgestaltung']
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    abweichenderHoehenbezug = Column(String)
    hoehenbezug = Column(XPEnum(XP_ArtHoehenbezug, include_default=True))
    abweichenderBezugspunkt = Column(String)
    bezugspunkt = Column(Enum(XP_ArtHoehenbezugspunkt))
    hMin = Column(Length)
    hMax = Column(Length)
    hZwingend = Column(Length)
    h = Column(Length)
    xp_objekt_id = Column(UUID(as_uuid=True), ForeignKey('xp_objekt.id', ondelete='CASCADE'))
    xp_objekt = relationship('XP_Objekt', back_populates='hoehenangabe')
    dachgestaltung_id = Column(UUID(as_uuid=True), ForeignKey('bp_dachgestaltung.id', ondelete='CASCADE'))
    dachgestaltung = relationship('BP_Dachgestaltung', back_populates='hoehenangabe')

    def to_xplan_node(self, node=None, version=XPlanVersion.FIVE_THREE):
        from SAGisXPlanung.GML.GMLWriter import GMLWriter
        this_node = etree.SubElement(node, f'{{{node.nsmap['xplan']}}}{self.__class__.__name__}')
        GMLWriter.write_attributes(this_node, self, version)
        return node

    @classmethod
    def avoid_export(cls):
        return ['xp_objekt', 'dachgestaltung']

    @classmethod
    def hidden_inputs(cls):
        return ['dachgestaltung']

class XP_VerbundenerPlan(RelationshipMixin, ElementOrderMixin, Base):
    """ Spezifikation eines anderen Plans, der mit dem Ausgangsplan oder Planbereich verbunden ist und diesen ändert
    bzw. von ihm geändert wird. """
    __tablename__ = 'xp_verbundener_plan'
    __avoidRelation__ = ['aendert_verbundenerPlan', 'wurdeGeaendertVon_verbundenerPlan', 'aendertPlan_verbundenerPlan', 'wurdeGeaendertVonPlan_verbundenerPlan']
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    planName = Column(String)
    rechtscharakter = Column(Enum(XP_RechtscharakterPlanaenderung), info={'xplan_version': XPlanVersion.FIVE_THREE})
    aenderungsArt = Column(Enum(XP_Aenderungsarten), info={'xplan_version': XPlanVersion.SIX})
    nummer = Column(String)
    aenderungsdatum = Column(Date, info={'xplan_version': XPlanVersion.SIX})
    aendert_verbundenerPlan_id = Column(UUID(as_uuid=True), ForeignKey('xp_plan.id', ondelete='CASCADE'))
    aendert_verbundenerPlan = relationship('XP_Plan', back_populates='aendert', foreign_keys=[aendert_verbundenerPlan_id])
    wurdeGeaendertVon_verbundenerPlan_id = Column(UUID(as_uuid=True), ForeignKey('xp_plan.id', ondelete='CASCADE'))
    wurdeGeaendertVon_verbundenerPlan = relationship('XP_Plan', back_populates='wurdeGeaendertVon', foreign_keys=[wurdeGeaendertVon_verbundenerPlan_id])
    aendertPlan_verbundenerPlan_id = Column(UUID(as_uuid=True), ForeignKey('xp_bereich.id', ondelete='CASCADE'))
    aendertPlan_verbundenerPlan = relationship('XP_Bereich', back_populates='aendertPlan', foreign_keys=[aendertPlan_verbundenerPlan_id])
    wurdeGeaendertVonPlan_verbundenerPlan_id = Column(UUID(as_uuid=True), ForeignKey('xp_bereich.id', ondelete='CASCADE'))
    wurdeGeaendertVonPlan_verbundenerPlan = relationship('XP_Bereich', back_populates='wurdeGeaendertVonPlan', foreign_keys=[wurdeGeaendertVonPlan_verbundenerPlan_id])

    @classmethod
    def avoid_export(cls):
        return ['aendert_verbundenerPlan', 'wurdeGeaendertVon_verbundenerPlan', 'aendertPlan_verbundenerPlan', 'wurdeGeaendertVonPlan_verbundenerPlan']

    @classmethod
    def hidden_inputs(cls):
        return ['aendert_verbundenerPlan', 'wurdeGeaendertVon_verbundenerPlan', 'aendertPlan_verbundenerPlan', 'wurdeGeaendertVonPlan_verbundenerPlan']

class XP_GenerAttribut(RelationshipMixin, ElementOrderMixin, Base):
    """ Abstrakte Basisklasse für Generische Attribute. """
    __tablename__ = 'xp_gener_attribut'
    __avoidRelation__ = ['plan', 'xp_objekt']
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    name = Column(String, nullable=False)
    type = Column(String())
    plan_id = Column(UUID(as_uuid=True), ForeignKey('xp_plan.id', ondelete='CASCADE'))
    plan = relationship('XP_Plan', back_populates='hatGenerAttribut')
    xp_objekt_id = Column(UUID(as_uuid=True), ForeignKey('xp_objekt.id', ondelete='CASCADE'))
    xp_objekt = relationship('XP_Objekt', back_populates='hatGenerAttribut')
    __mapper_args__ = {'polymorphic_on': type, 'polymorphic_identity': 'xp_gener_attribut'}

    @classmethod
    def avoid_export(cls):
        return ['plan', 'xp_objekt']

class XP_DatumAttribut(XP_GenerAttribut):
    """ Generische Attribute vom Datentyp "Datum" """
    __tablename__ = 'xp_datum_attribut'
    __mapper_args__ = {'polymorphic_identity': 'xp_datum_attribut'}
    id = Column(UUID(as_uuid=True), ForeignKey('xp_gener_attribut.id'), primary_key=True)
    wert = Column(Date, nullable=False)

class XP_DoubleAttribut(XP_GenerAttribut):
    """ Generisches Attribut vom Datentyp "Double". """
    __tablename__ = 'xp_double_attribut'
    __mapper_args__ = {'polymorphic_identity': 'xp_double_attribut'}
    id = Column(UUID(as_uuid=True), ForeignKey('xp_gener_attribut.id'), primary_key=True)
    wert = Column(Float, nullable=False)

class XP_IntegerAttribut(XP_GenerAttribut):
    """ Generische Attribute vom Datentyp "Integer". """
    __tablename__ = 'xp_integer_attribut'
    __mapper_args__ = {'polymorphic_identity': 'xp_integer_attribut'}
    id = Column(UUID(as_uuid=True), ForeignKey('xp_gener_attribut.id'), primary_key=True)
    wert = Column(Integer, nullable=False)

class XP_StringAttribut(XP_GenerAttribut):
    """ Generisches Attribut vom Datentyp "CharacterString" """
    __tablename__ = 'xp_string_attribut'
    __mapper_args__ = {'polymorphic_identity': 'xp_string_attribut'}
    id = Column(UUID(as_uuid=True), ForeignKey('xp_gener_attribut.id'), primary_key=True)
    wert = Column(String, nullable=False)

class XP_URLAttribut(XP_GenerAttribut):
    """ Generisches Attribut vom Datentyp "Double". """
    __tablename__ = 'xp_url_attribut'
    __mapper_args__ = {'polymorphic_identity': 'xp_url_attribut'}
    id = Column(UUID(as_uuid=True), ForeignKey('xp_gener_attribut.id'), primary_key=True)
    wert = Column(RefURL, nullable=False)