from . import model
from bazbase import structure

class SqlElement(structure.Element):
    __slots__ = ('_e',)
    
    def __init__(self, e):
        structure.Element.__init__(self, e.ename)
        # It's unclear if we want to keep this long-term.
        self._e = e

    def get_orgmode(self):
        return self._e.orgmode

    # TODO(xavid): Memoize these and clean up model dependency.
    def get_descendants(self):
        return [SqlElement(d) for d in self._e.getDescendants()]
    def is_ancestor_of(self, other):
        return self._e.isAncestorOf(other._e)

    def get_ancestors(self):
        return [SqlElement(d) for d in self._e.getAncestors()]

    def create_child(self, ename):
        return SqlElement(model.Element(ename, self._e))

    def _get_value_map_impl(self, od):
        for pv in (model.Propval.query
                   .options(model.joinedload(model.Propval.prop))
                   .filter(model.Propval.element_id == self._e.id)
                   .all()):
            value, format = pv.get_value_and_format()
            od[pv.prop.name] = structure.Propval(self, pv.prop.name, value,
                                                 format)

def get_root_element():
    return SqlElement(model.Element.getRoot())

def create_root_element(ename):
    return SqlElement(model.Element(ename, parent=None))

class SqlProp(structure.Prop):
    __slots__ = ('_p',)

    def __init__(self, p):
        structure.Prop.__init__(self, p.name, p.flavor, p.default, p.visible,
                                p.comment)
        self._p = p

    def set_flavor(self, flavor):
        self._p.flavor = flavor
        self._flavor = flavor

    def set_default(self, default):
        self._p.default = default
        self._default = default

    def set_visible(self, visible):
        self._p.visible = visible
        self._visible = visible

    def set_comment(self, comment):
        self._p.comment = comment
        self._comment = comment

    # Should be ordered such that each element is followed by a single
    # region consisting of any included descendants.  Because of prop
    # inheritance, there shouldn't be holes.
    def containing_elements(self):
        return [SqlElement(e)
                for e in model.Element.query.join(model.Element.propvals)
                .filter(model.Propval.prop_id == self._p.id)
                .order_by(model.Element.treeleft).all()]

def list_all_props():
    return [SqlProp(p) for p in model.Prop.query.all()]

def list_all_elements():
    return [SqlElement(e) for e in model.Element.query.all()]

def get_element(ename):
    try:
        return SqlElement(model.Element.get(ename))
    except model.NoResultFound:
        return None

def _get_element_raw_simple(k, v):
    prop = model.Prop.get(k)
    pv = model.Propval.get_by(prop=prop, value=v)
    if pv is None:
        return None
    else:
        return SqlElement(pv.element)

def get_prop(pname):
    try:
        return SqlProp(model.Prop.get(pname))
    except model.NoResultFound:
        return None

def create_prop(pname, flavor=None):
    return SqlProp(model.Prop(pname, flavor))

def get_parent(ename, e):
    p = e._e.parent
    if p is None:
        return None
    else:
        return SqlElement(p)

def get_children(ename, e):
    return [SqlElement(d) for d in e._e.getChildren()]

# Overall operations
def clear_database():
    model.clear()

def flush_database_for_test():
    model.session.flush()

def verify_tree_for_test():
    model.verifyTree()
