from sqlalchemy import (
    Column,
    Float,
    Integer,
    String,
    Unicode,
    CheckConstraint,
    UniqueConstraint,
    ForeignKey,
)
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.associationproxy import association_proxy

from zope.interface import implementer

from clld.db.meta import Base, PolymorphicBaseMixin
from clld import interfaces
from clld.util import DeclEnum

from . import (
    IdNameDescriptionMixin,
    DataMixin, HasDataMixin, FilesMixin, HasFilesMixin)

__all__ = (
    'Language', 'LanguageSource',
    'IdentifierType', 'Identifier', 'LanguageIdentifier',
)


class Language_data(Base, DataMixin):

    """Associated data mapper."""


class Language_files(Base, FilesMixin):

    """Associated files mapper."""


@implementer(interfaces.ILanguage)
class Language(Base,
               PolymorphicBaseMixin,
               IdNameDescriptionMixin,
               HasDataMixin,
               HasFilesMixin):

    """Languages are the main objects of discourse.

    We attach a geo-coordinate to them to be able to put them on maps.
    """

    latitude = Column(
        Float(),
        CheckConstraint('-90 <= latitude and latitude <= 90'),
        doc='geographical latitude in WGS84')
    longitude = Column(
        Float(),
        CheckConstraint('-180 <= longitude and longitude <= 180 '),
        doc='geographical longitude in WGS84')
    identifiers = association_proxy('languageidentifier', 'identifier')

    def get_identifier_objs(self, type_):
        return [i for i in self.identifiers if i.type == getattr(type_, 'value', type_)]

    def get_identifier(self, type_):
        objs = self.get_identifier_objs(type_)
        if objs:
            return objs[0].name

    @property
    def iso_code(self):
        return self.get_identifier(IdentifierType.iso)

    @property
    def glottocode(self):
        return self.get_identifier(IdentifierType.glottolog)


class LanguageSource(Base):

    """Association table."""

    __table_args__ = (UniqueConstraint('language_pk', 'source_pk'),)

    language_pk = Column(Integer, ForeignKey('language.pk'), nullable=False)
    source_pk = Column(Integer, ForeignKey('source.pk'), nullable=False)


class IdentifierType(DeclEnum):

    """Known language identifiers."""

    iso = 'iso639-3', 'ISO 639-3', 'https://iso639-3.sil.org/code/{0.name}'
    wals = 'wals', 'WALS Code', 'http://wals.info/languoid/lect/wals_code_{0.name}'
    glottolog = 'glottolog', 'Glottocode', \
                'http://glottolog.org/resource/languoid/id/{0.name}'
    ethnologue = 'ethnologue', 'Ethnologue', 'http://www.ethnologue.com/language/{0.name}'


class Identifier(Base, IdNameDescriptionMixin):

    """A language identifier.

    We want to be able to link languages to languages in other systems. Thus,
    we store identifiers of various types like 'wals', 'iso639-3', 'glottolog'.
    But we might as well just store alternative names for languages.
    """

    __table_args__ = (UniqueConstraint('name', 'type', 'description', 'lang'),)

    id = Column(String)
    type = Column(String)
    lang = Column(String(3), default='en')

    def url(self):
        try:
            return IdentifierType.from_string(self.type).args[0].format(self)
        except ValueError:
            return


class LanguageIdentifier(Base):

    """Association table.

    Languages are linked to identifiers with an optional description of this
    linkage, e.g. 'is dialect of'.
    """

    __table_args__ = (UniqueConstraint('language_pk', 'identifier_pk'),)

    language_pk = Column(Integer, ForeignKey('language.pk'), nullable=False)
    identifier_pk = Column(Integer, ForeignKey('identifier.pk'), nullable=False)
    description = Column(Unicode)

    identifier = relationship(Identifier, innerjoin=True)
    language = relationship(
        Language, innerjoin=True,
        backref=backref("languageidentifier", cascade="all, delete-orphan"))
