from functools import partial

from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy
from sqlalchemy.orm import Query, aliased, mapper, relationship, synonym
from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.orm.scoping import ScopedSession
from sqlalchemy.orm.session import Session, object_session
from sqlalchemy.schema import Column, ForeignKey, Table
from sqlalchemy.sql.expression import and_, bindparam, select, exists
from sqlalchemy.sql.operators import ColumnOperators
from sqlalchemy.types import Integer

from pokedex.db import markdown

class LocalAssociationProxy(AssociationProxy, ColumnOperators):
    """An association proxy for names in the default language

    Over the regular association_proxy, this provides sorting and filtering
    capabilities, implemented via SQL subqueries.
    """
    def __clause_element__(self):
        q = select([self.remote_attr])
        q = q.where(self.target_class.foreign_id == self.owning_class.id)
        q = q.where(self.target_class.local_language_id == bindparam('_default_language_id'))
        return q

    def operate(self, op, *other, **kwargs):
        q = select([self.remote_attr])
        q = q.where(self.target_class.foreign_id == self.owning_class.id)
        q = q.where(self.target_class.local_language_id == bindparam('_default_language_id'))
        q = q.where(op(self.remote_attr, *other))
        return exists(q)


def _getset_factory_factory(column_name, string_getter):
    """Hello!  I am a factory for creating getset_factory functions for SQLA.
    I exist to avoid the closure-in-a-loop problem.
    """
    def getset_factory(underlying_type, instance):
        def getter(translations):
            if translations is None:
                return None
            text = getattr(translations, column_name)
            if text is None:
                return text
            session = object_session(translations)
            language = translations.local_language
            return string_getter(text, session, language)
        def setter(translations, value):
            # The string must be set on the Translation directly.
            raise AttributeError("Cannot set %s" % column_name)
        return getter, setter
    return getset_factory

def create_translation_table(_table_name, foreign_class, relation_name,
    language_class, relation_lazy='select', **kwargs):
    """Creates a table that represents some kind of data attached to the given
    foreign class, but translated across several languages.  Returns the new
    table's mapped class.  It won't be declarative, but it will have a
    `__table__` attribute so you can retrieve the Table object.